mirror of
https://github.com/topjohnwu/Magisk
synced 2025-11-09 00:32:30 +01:00
Compare commits
24 Commits
manager-v5
...
manager-v5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d139e156e | ||
|
|
7c2849356a | ||
|
|
0025ffd1c0 | ||
|
|
2ef7146642 | ||
|
|
1b27e69e40 | ||
|
|
8e7b757efd | ||
|
|
1ab543cea1 | ||
|
|
a3f86903e4 | ||
|
|
c239c305ab | ||
|
|
2e02af994e | ||
|
|
836d9afe17 | ||
|
|
007a352742 | ||
|
|
e526e5659e | ||
|
|
4a5227c7bf | ||
|
|
c2c151ec4c | ||
|
|
452096e7e4 | ||
|
|
50c2a9859e | ||
|
|
677b667307 | ||
|
|
1adf331268 | ||
|
|
349b3e961b | ||
|
|
96650c06f0 | ||
|
|
26038a0a07 | ||
|
|
6a148b5dd9 | ||
|
|
0e109ef979 |
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 27
|
||||||
buildToolsVersion "27.0.2"
|
buildToolsVersion "27.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.topjohnwu.magisk"
|
applicationId "com.topjohnwu.magisk"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 27
|
targetSdkVersion 27
|
||||||
versionCode 82
|
versionCode 90
|
||||||
versionName "5.5.1"
|
versionName "5.5.4"
|
||||||
ndk {
|
ndk {
|
||||||
moduleName 'zipadjust'
|
moduleName 'zipadjust'
|
||||||
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||||
|
|||||||
3
proguard-rules.pro
vendored
3
proguard-rules.pro
vendored
@@ -22,3 +22,6 @@
|
|||||||
# BouncyCastle
|
# BouncyCastle
|
||||||
-keep class org.bouncycastle.jcajce.provider.** { *; }
|
-keep class org.bouncycastle.jcajce.provider.** { *; }
|
||||||
-dontwarn javax.naming.**
|
-dontwarn javax.naming.**
|
||||||
|
|
||||||
|
# Gson
|
||||||
|
-keepattributes Signature
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MagiskManager"
|
android:name=".MagiskManager"
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
### v5.5.1
|
### v5.5.4
|
||||||
- Fix an issue in setting up superuser database
|
- Fix on-boot dtbo detection
|
||||||
|
- Add fingerprint authentication for Superuser requests
|
||||||
### v5.5.0
|
|
||||||
- Fix dynamic resource loading, prevent crashes when checking SafetyNet
|
|
||||||
- Update SignAPK to use very little RAM for supporting old devices
|
|
||||||
- Support settings migration after hiding Magisk Manager
|
|
||||||
- Add reboot menu in modules section
|
|
||||||
- Add dark theme to superuser request dialogs
|
|
||||||
- Properly handle new `HIGHCOMP` and add recommended `KEEPVERITY` and `KEEPFORCEENCRYPT` flags for installation
|
|
||||||
- Support new paths for v14.6
|
|
||||||
- Massive improvements in repackaging Magisk Manager
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class MagiskFragment extends Fragment
|
|||||||
new CheckSafetyNet(getActivity()).exec();
|
new CheckSafetyNet(getActivity()).exec();
|
||||||
collapse();
|
collapse();
|
||||||
};
|
};
|
||||||
if (mm.snetVersion < 0) {
|
if (!CheckSafetyNet.dexPath.exists()) {
|
||||||
// Show dialog
|
// Show dialog
|
||||||
new AlertDialogBuilder(getActivity())
|
new AlertDialogBuilder(getActivity())
|
||||||
.setTitle(R.string.proprietary_title)
|
.setTitle(R.string.proprietary_title)
|
||||||
@@ -138,13 +138,9 @@ public class MagiskFragment extends Fragment
|
|||||||
setupExpandable();
|
setupExpandable();
|
||||||
|
|
||||||
keepVerityChkbox.setChecked(mm.keepVerity);
|
keepVerityChkbox.setChecked(mm.keepVerity);
|
||||||
keepVerityChkbox.setOnCheckedChangeListener((view, isChecked) -> {
|
keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepVerity = checked);
|
||||||
mm.keepVerity = isChecked;
|
|
||||||
});
|
|
||||||
keepEncChkbox.setChecked(mm.keepEnc);
|
keepEncChkbox.setChecked(mm.keepEnc);
|
||||||
keepEncChkbox.setOnCheckedChangeListener((view, isChecked) -> {
|
keepEncChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepEnc = checked);
|
||||||
mm.keepEnc = isChecked;
|
|
||||||
});
|
|
||||||
|
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(this);
|
mSwipeRefreshLayout.setOnRefreshListener(this);
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ public class MagiskManager extends Application {
|
|||||||
public int remoteManagerVersionCode = -1;
|
public int remoteManagerVersionCode = -1;
|
||||||
public String managerLink;
|
public String managerLink;
|
||||||
public String bootBlock = null;
|
public String bootBlock = null;
|
||||||
public int snetVersion;
|
|
||||||
public boolean keepVerity = false;
|
public boolean keepVerity = false;
|
||||||
public boolean keepEnc = false;
|
public boolean keepEnc = false;
|
||||||
|
|
||||||
@@ -63,8 +62,6 @@ public class MagiskManager extends Application {
|
|||||||
|
|
||||||
public boolean magiskHide;
|
public boolean magiskHide;
|
||||||
public boolean isDarkTheme;
|
public boolean isDarkTheme;
|
||||||
public boolean updateNotification;
|
|
||||||
public boolean suReauth;
|
|
||||||
public int suRequestTimeout;
|
public int suRequestTimeout;
|
||||||
public int suLogTimeout = 14;
|
public int suLogTimeout = 14;
|
||||||
public int suAccessState;
|
public int suAccessState;
|
||||||
@@ -75,7 +72,7 @@ public class MagiskManager extends Application {
|
|||||||
public String localeConfig;
|
public String localeConfig;
|
||||||
public int updateChannel;
|
public int updateChannel;
|
||||||
public String bootFormat;
|
public String bootFormat;
|
||||||
public String customChannelUrl;
|
public int repoOrder;
|
||||||
|
|
||||||
// Global resources
|
// Global resources
|
||||||
public SharedPreferences prefs;
|
public SharedPreferences prefs;
|
||||||
@@ -106,7 +103,7 @@ public class MagiskManager extends Application {
|
|||||||
} catch (PackageManager.NameNotFoundException ignored) { /* Expected */ }
|
} catch (PackageManager.NameNotFoundException ignored) { /* Expected */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
suDB = new SuDatabaseHelper(false);
|
suDB = SuDatabaseHelper.getSuDB(false);
|
||||||
repoDB = new RepoDatabaseHelper(this);
|
repoDB = new RepoDatabaseHelper(this);
|
||||||
defaultLocale = Locale.getDefault();
|
defaultLocale = Locale.getDefault();
|
||||||
setLocale();
|
setLocale();
|
||||||
@@ -135,21 +132,38 @@ public class MagiskManager extends Application {
|
|||||||
suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
|
suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
|
||||||
suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
|
suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
|
||||||
suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
|
suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
|
||||||
suReauth = prefs.getBoolean(Const.Key.SU_REAUTH, false);
|
|
||||||
suAccessState = suDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
|
suAccessState = suDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
|
||||||
multiuserMode = suDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
|
multiuserMode = suDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
|
||||||
suNamespaceMode = suDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
|
suNamespaceMode = suDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
|
||||||
|
|
||||||
// config
|
// config
|
||||||
isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
|
isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
|
||||||
updateNotification = prefs.getBoolean(Const.Key.UPDATE_NOTIFICATION, true);
|
|
||||||
updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
|
updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
|
||||||
bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
||||||
snetVersion = prefs.getInt(Const.Key.SNET_VER, -1);
|
repoOrder = prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_NAME);
|
||||||
customChannelUrl = prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void toast(String msg, int duration) {
|
public void writeConfig() {
|
||||||
|
prefs.edit()
|
||||||
|
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
||||||
|
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
||||||
|
.putBoolean(Const.Key.HOSTS, Utils.itemExist(Const.MAGISK_HOST_FILE()))
|
||||||
|
.putBoolean(Const.Key.COREONLY, Utils.itemExist(Const.MAGISK_DISABLE_FILE))
|
||||||
|
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
||||||
|
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
||||||
|
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
|
||||||
|
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
|
||||||
|
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
|
||||||
|
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
|
||||||
|
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
|
||||||
|
.putString(Const.Key.LOCALE, localeConfig)
|
||||||
|
.putString(Const.Key.BOOT_FORMAT, bootFormat)
|
||||||
|
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
||||||
|
.putInt(Const.Key.REPO_ORDER, repoOrder)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void toast(CharSequence msg, int duration) {
|
||||||
mHandler.post(() -> Toast.makeText(weakSelf.get(), msg, duration).show());
|
mHandler.post(() -> Toast.makeText(weakSelf.get(), msg, duration).show());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,6 +204,14 @@ public class MagiskManager extends Application {
|
|||||||
if (Utils.isValidShellResponse(ret))
|
if (Utils.isValidShellResponse(ret))
|
||||||
bootBlock = ret.get(0);
|
bootBlock = ret.get(0);
|
||||||
|
|
||||||
|
if (suDB != null && !SuDatabaseHelper.verified) {
|
||||||
|
suDB.close();
|
||||||
|
suDB = SuDatabaseHelper.getSuDB(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getDefaultInstallFlags() {
|
||||||
|
List<String> ret;
|
||||||
ret = Shell.su("echo \"$DTBOIMAGE\"");
|
ret = Shell.su("echo \"$DTBOIMAGE\"");
|
||||||
if (Utils.isValidShellResponse(ret))
|
if (Utils.isValidShellResponse(ret))
|
||||||
keepVerity = true;
|
keepVerity = true;
|
||||||
@@ -213,11 +235,6 @@ public class MagiskManager extends Application {
|
|||||||
if (Utils.isValidShellResponse(ret))
|
if (Utils.isValidShellResponse(ret))
|
||||||
keepEnc = Boolean.parseBoolean(ret.get(0));
|
keepEnc = Boolean.parseBoolean(ret.get(0));
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {}
|
||||||
|
|
||||||
if (suDB != null && !SuDatabaseHelper.verified) {
|
|
||||||
suDB.close();
|
|
||||||
suDB = new SuDatabaseHelper();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPermissionGrantCallback(Runnable callback) {
|
public void setPermissionGrantCallback(Runnable callback) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
import android.support.v4.widget.SwipeRefreshLayout;
|
||||||
@@ -7,6 +8,7 @@ import android.support.v7.widget.RecyclerView;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.SearchView;
|
import android.widget.SearchView;
|
||||||
@@ -15,6 +17,7 @@ import android.widget.TextView;
|
|||||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
||||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.Fragment;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
@@ -98,6 +101,22 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
MagiskManager mm = getApplication();
|
||||||
|
if (item.getItemId() == R.id.repo_sort) {
|
||||||
|
new AlertDialog.Builder(getActivity())
|
||||||
|
.setTitle(R.string.sorting_order)
|
||||||
|
.setSingleChoiceItems(R.array.sorting_orders, mm.repoOrder, (d, which) -> {
|
||||||
|
mm.repoOrder = which;
|
||||||
|
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, mm.repoOrder).apply();
|
||||||
|
adapter.notifyDBChanged();
|
||||||
|
d.dismiss();
|
||||||
|
}).show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
|||||||
import com.topjohnwu.magisk.asyncs.HideManager;
|
import com.topjohnwu.magisk.asyncs.HideManager;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
import com.topjohnwu.magisk.utils.Shell;
|
import com.topjohnwu.magisk.utils.Shell;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
@@ -112,13 +113,14 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
|
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
|
||||||
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
|
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
|
||||||
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
|
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
|
||||||
|
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
|
||||||
|
|
||||||
updateChannel.setOnPreferenceChangeListener((pref, o) -> {
|
updateChannel.setOnPreferenceChangeListener((pref, o) -> {
|
||||||
mm.updateChannel = Integer.parseInt((String) o);
|
mm.updateChannel = Integer.parseInt((String) o);
|
||||||
if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) {
|
if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) {
|
||||||
View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null);
|
View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null);
|
||||||
EditText url = v.findViewById(R.id.custom_url);
|
EditText url = v.findViewById(R.id.custom_url);
|
||||||
url.setText(mm.customChannelUrl);
|
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setTitle(R.string.settings_update_custom)
|
.setTitle(R.string.settings_update_custom)
|
||||||
.setView(v)
|
.setView(v)
|
||||||
@@ -144,6 +146,11 @@ public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|||||||
reauth.setSummary(R.string.android_o_not_support);
|
reauth.setSummary(R.string.android_o_not_support);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove fingerprint option if not possible
|
||||||
|
if (!FingerprintHelper.canUseFingerprint()) {
|
||||||
|
suCategory.removePreference(fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME) && mm.magiskVersionCode >= 1440) {
|
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME) && mm.magiskVersionCode >= 1440) {
|
||||||
hideManager.setOnPreferenceClickListener((pref) -> {
|
hideManager.setOnPreferenceClickListener((pref) -> {
|
||||||
Utils.runWithPermission(getActivity(),
|
Utils.runWithPermission(getActivity(),
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class SplashActivity extends Activity {
|
|||||||
MagiskManager mm = getMagiskManager();
|
MagiskManager mm = getMagiskManager();
|
||||||
|
|
||||||
mm.loadMagiskInfo();
|
mm.loadMagiskInfo();
|
||||||
|
mm.getDefaultInstallFlags();
|
||||||
Utils.loadPrefs();
|
Utils.loadPrefs();
|
||||||
|
|
||||||
// Dynamic detect all locales
|
// Dynamic detect all locales
|
||||||
@@ -61,7 +62,7 @@ public class SplashActivity extends Activity {
|
|||||||
if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
|
if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
|
||||||
|
|
||||||
// Add update checking service
|
// Add update checking service
|
||||||
if (Const.Value.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
if (Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
||||||
ComponentName service = new ComponentName(this, UpdateCheckService.class);
|
ComponentName service = new ComponentName(this, UpdateCheckService.class);
|
||||||
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
||||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||||
@@ -79,24 +80,7 @@ public class SplashActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write back default values
|
// Write back default values
|
||||||
mm.prefs.edit()
|
mm.writeConfig();
|
||||||
.putBoolean(Const.Key.DARK_THEME, mm.isDarkTheme)
|
|
||||||
.putBoolean(Const.Key.MAGISKHIDE, mm.magiskHide)
|
|
||||||
.putBoolean(Const.Key.UPDATE_NOTIFICATION, mm.updateNotification)
|
|
||||||
.putBoolean(Const.Key.HOSTS, Utils.itemExist(Const.MAGISK_HOST_FILE()))
|
|
||||||
.putBoolean(Const.Key.COREONLY, Utils.itemExist(Const.MAGISK_DISABLE_FILE))
|
|
||||||
.putBoolean(Const.Key.SU_REAUTH, mm.suReauth)
|
|
||||||
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(mm.suRequestTimeout))
|
|
||||||
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(mm.suResponseType))
|
|
||||||
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(mm.suNotificationType))
|
|
||||||
.putString(Const.Key.ROOT_ACCESS, String.valueOf(mm.suAccessState))
|
|
||||||
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(mm.multiuserMode))
|
|
||||||
.putString(Const.Key.SU_MNT_NS, String.valueOf(mm.suNamespaceMode))
|
|
||||||
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(mm.updateChannel))
|
|
||||||
.putString(Const.Key.LOCALE, mm.localeConfig)
|
|
||||||
.putString(Const.Key.BOOT_FORMAT, mm.bootFormat)
|
|
||||||
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.Value.UPDATE_SERVICE_VER)
|
|
||||||
.apply();
|
|
||||||
|
|
||||||
mm.hasInit = true;
|
mm.hasInit = true;
|
||||||
|
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
@BindView(R.id.app_icon) ImageView appIcon;
|
@BindView(R.id.app_icon) ImageView appIcon;
|
||||||
@BindView(R.id.app_name) TextView appName;
|
@BindView(R.id.app_name) TextView appName;
|
||||||
@BindView(R.id.app_package) TextView appPackage;
|
@BindView(R.id.package_name) TextView appPackage;
|
||||||
@BindView(R.id.checkbox) CheckBox checkBox;
|
@BindView(R.id.checkbox) CheckBox checkBox;
|
||||||
|
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
String author = repo.getAuthor();
|
String author = repo.getAuthor();
|
||||||
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
|
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
|
||||||
holder.description.setText(repo.getDescription());
|
holder.description.setText(repo.getDescription());
|
||||||
|
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
|
||||||
|
|
||||||
holder.infoLayout.setOnClickListener(v ->
|
holder.infoLayout.setOnClickListener(v ->
|
||||||
new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec());
|
new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec());
|
||||||
@@ -180,6 +181,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
@BindView(R.id.author) TextView author;
|
@BindView(R.id.author) TextView author;
|
||||||
@BindView(R.id.info_layout) LinearLayout infoLayout;
|
@BindView(R.id.info_layout) LinearLayout infoLayout;
|
||||||
@BindView(R.id.download) ImageView downloadImage;
|
@BindView(R.id.download) ImageView downloadImage;
|
||||||
|
@BindView(R.id.update_time) TextView updateTime;
|
||||||
|
|
||||||
RepoHolder(View itemView) {
|
RepoHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ import dalvik.system.DexClassLoader;
|
|||||||
|
|
||||||
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
||||||
|
|
||||||
private File dexPath;
|
public static final File dexPath =
|
||||||
|
new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
|
||||||
private DexClassLoader loader;
|
private DexClassLoader loader;
|
||||||
private Class<?> helperClazz, callbackClazz;
|
private Class<?> helperClazz, callbackClazz;
|
||||||
|
|
||||||
public CheckSafetyNet(Activity activity) {
|
public CheckSafetyNet(Activity activity) {
|
||||||
super(activity);
|
super(activity);
|
||||||
dexPath = new File(activity.getCacheDir().getParent() + "/snet", "snet.apk");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dlSnet() throws IOException {
|
private void dlSnet() throws IOException {
|
||||||
@@ -65,7 +65,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snet_ver != Const.Value.SNET_VER) {
|
if (snet_ver != Const.SNET_VER) {
|
||||||
dlSnet();
|
dlSnet();
|
||||||
loadClasses();
|
loadClasses();
|
||||||
}
|
}
|
||||||
@@ -79,8 +79,6 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Exception err) {
|
protected void onPostExecute(Exception err) {
|
||||||
MagiskManager mm = MagiskManager.get();
|
MagiskManager mm = MagiskManager.get();
|
||||||
mm.snetVersion = Const.Value.SNET_VER;
|
|
||||||
mm.prefs.edit().putInt(Const.Key.SNET_VER, Const.Value.SNET_VER).apply();
|
|
||||||
try {
|
try {
|
||||||
if (err != null) throw err;
|
if (err != null) throw err;
|
||||||
Object helper = helperClazz.getConstructors()[0].newInstance(
|
Object helper = helperClazz.getConstructors()[0].newInstance(
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
|||||||
jsonStr = WebService.getString(Const.Url.BETA_URL);
|
jsonStr = WebService.getString(Const.Url.BETA_URL);
|
||||||
break;
|
break;
|
||||||
case Const.Value.CUSTOM_CHANNEL:
|
case Const.Value.CUSTOM_CHANNEL:
|
||||||
jsonStr = WebService.getString(mm.customChannelUrl);
|
jsonStr = WebService.getString(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -54,7 +54,7 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void v) {
|
protected void onPostExecute(Void v) {
|
||||||
MagiskManager mm = MagiskManager.get();
|
MagiskManager mm = MagiskManager.get();
|
||||||
if (showNotification && mm.updateNotification) {
|
if (showNotification && mm.prefs.getBoolean(Const.Key.UPDATE_NOTIFICATION, true)) {
|
||||||
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
|
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
|
||||||
ShowUI.managerUpdateNotification();
|
ShowUI.managerUpdateNotification();
|
||||||
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
|
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package com.topjohnwu.magisk.container;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class Repo extends BaseModule {
|
public class Repo extends BaseModule {
|
||||||
@@ -40,7 +42,7 @@ public class Repo extends BaseModule {
|
|||||||
if (getVersionCode() < 0) {
|
if (getVersionCode() < 0) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
||||||
}
|
}
|
||||||
if (getMinMagiskVersion() < Const.Value.MIN_MODULE_VER) {
|
if (getMinMagiskVersion() < Const.MIN_MODULE_VER) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] is outdated");
|
throw new IllegalRepoException("Repo [" + repoName + "] is outdated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,6 +80,11 @@ public class Repo extends BaseModule {
|
|||||||
return String.format(Const.Url.FILE_URL, repoName, "README.md");
|
return String.format(Const.Url.FILE_URL, repoName, "README.md");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLastUpdateString() {
|
||||||
|
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
|
||||||
|
MagiskManager.locale).format(mLastUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
public Date getLastUpdate() {
|
public Date getLastUpdate() {
|
||||||
return mLastUpdate;
|
return mLastUpdate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
// Clear bad repos
|
// Clear bad repos
|
||||||
mDb.delete(TABLE_NAME, "minMagisk<?",
|
mDb.delete(TABLE_NAME, "minMagisk<?",
|
||||||
new String[] { String.valueOf(Const.Value.MIN_MODULE_VER) });
|
new String[] { String.valueOf(Const.MIN_MODULE_VER) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,9 +95,17 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getRepoCursor() {
|
public Cursor getRepoCursor() {
|
||||||
|
String orderBy = null;
|
||||||
|
switch (mm.repoOrder) {
|
||||||
|
case Const.Value.ORDER_NAME:
|
||||||
|
orderBy = "name COLLATE NOCASE";
|
||||||
|
break;
|
||||||
|
case Const.Value.ORDER_DATE:
|
||||||
|
orderBy = "last_update DESC";
|
||||||
|
}
|
||||||
return mDb.query(TABLE_NAME, null, "minMagisk<=?",
|
return mDb.query(TABLE_NAME, null, "minMagisk<=?",
|
||||||
new String[] { String.valueOf(mm.magiskVersionCode) },
|
new String[] { String.valueOf(mm.magiskVersionCode) },
|
||||||
null, null, "name COLLATE NOCASE");
|
null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getRepoIDList() {
|
public List<String> getRepoIDList() {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
File db = Utils.getDB(context, DB_NAME);
|
File db = Utils.getDB(context, DB_NAME);
|
||||||
if (!verify) {
|
if (!verify) {
|
||||||
if (db.length() == 0) {
|
if (db.exists() && db.length() == 0) {
|
||||||
ce.loadMagiskInfo();
|
ce.loadMagiskInfo();
|
||||||
// Continue verification
|
// Continue verification
|
||||||
} else {
|
} else {
|
||||||
@@ -108,7 +108,7 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
// New global su db
|
// New global su db
|
||||||
Shell.su(Utils.fmt("mkdir %s 2>/dev/null; chmod 700 %s", GLOBAL_DB.getParent(), GLOBAL_DB.getParent()));
|
Shell.su(Utils.fmt("mkdir %s 2>/dev/null; chmod 700 %s", GLOBAL_DB.getParent(), GLOBAL_DB.getParent()));
|
||||||
if (!Utils.itemExist(GLOBAL_DB)) {
|
if (!Utils.itemExist(GLOBAL_DB)) {
|
||||||
Utils.javaCreateFile(db);
|
context.openOrCreateDatabase(DB_NAME, 0, null).close();
|
||||||
Shell.su(Utils.fmt("cp -af %s %s; rm -f %s*", db, GLOBAL_DB, db));
|
Shell.su(Utils.fmt("cp -af %s %s; rm -f %s*", db, GLOBAL_DB, db));
|
||||||
}
|
}
|
||||||
verified = TextUtils.equals(Utils.checkInode(GLOBAL_DB), Utils.checkInode(db));
|
verified = TextUtils.equals(Utils.checkInode(GLOBAL_DB), Utils.checkInode(db));
|
||||||
@@ -125,12 +125,17 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SuDatabaseHelper() {
|
public static SuDatabaseHelper getSuDB(boolean verify) {
|
||||||
this(true);
|
try {
|
||||||
}
|
return new SuDatabaseHelper(initDB(verify));
|
||||||
|
} catch(Exception e) {
|
||||||
public SuDatabaseHelper(boolean verify) {
|
// Try to catch runtime exceptions and remove all db for retry
|
||||||
this(initDB(verify));
|
unmntDB();
|
||||||
|
Shell.su(Utils.fmt("rm -rf /data/user*/*/magisk.db /data/adb/magisk.db /data/user*/*/%s/databases",
|
||||||
|
MagiskManager.get().getPackageName()));
|
||||||
|
e.printStackTrace();
|
||||||
|
return new SuDatabaseHelper(initDB(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SuDatabaseHelper(Context context) {
|
private SuDatabaseHelper(Context context) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
import com.topjohnwu.magisk.utils.Shell;
|
import com.topjohnwu.magisk.utils.Shell;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ public class PackageReceiver extends BroadcastReceiver {
|
|||||||
switch (intent.getAction()) {
|
switch (intent.getAction()) {
|
||||||
case Intent.ACTION_PACKAGE_REPLACED:
|
case Intent.ACTION_PACKAGE_REPLACED:
|
||||||
// This will only work pre-O
|
// This will only work pre-O
|
||||||
if (mm.suReauth) {
|
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
|
||||||
mm.suDB.deletePolicy(pkg);
|
mm.suDB.deletePolicy(pkg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class OnBootIntentService extends IntentService {
|
|||||||
* */
|
* */
|
||||||
MagiskManager mm = Utils.getMagiskManager(this);
|
MagiskManager mm = Utils.getMagiskManager(this);
|
||||||
mm.loadMagiskInfo();
|
mm.loadMagiskInfo();
|
||||||
|
mm.getDefaultInstallFlags();
|
||||||
if (Shell.rootAccess()) {
|
if (Shell.rootAccess()) {
|
||||||
Utils.patchDTBO();
|
Utils.patchDTBO();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package com.topjohnwu.magisk.superuser;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.net.LocalSocket;
|
import android.net.LocalSocket;
|
||||||
import android.net.LocalSocketAddress;
|
import android.net.LocalSocketAddress;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CountDownTimer;
|
import android.os.CountDownTimer;
|
||||||
import android.os.FileObserver;
|
import android.os.FileObserver;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -23,6 +25,7 @@ import com.topjohnwu.magisk.asyncs.ParallelTask;
|
|||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.Activity;
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Const;
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@@ -40,6 +43,8 @@ public class RequestActivity extends Activity {
|
|||||||
@BindView(R.id.package_name) TextView packageNameView;
|
@BindView(R.id.package_name) TextView packageNameView;
|
||||||
@BindView(R.id.grant_btn) Button grant_btn;
|
@BindView(R.id.grant_btn) Button grant_btn;
|
||||||
@BindView(R.id.deny_btn) Button deny_btn;
|
@BindView(R.id.deny_btn) Button deny_btn;
|
||||||
|
@BindView(R.id.fingerprint) ImageView fingerprintImg;
|
||||||
|
@BindView(R.id.warning) TextView warning;
|
||||||
|
|
||||||
private String socketPath;
|
private String socketPath;
|
||||||
private LocalSocket socket;
|
private LocalSocket socket;
|
||||||
@@ -49,6 +54,7 @@ public class RequestActivity extends Activity {
|
|||||||
private boolean hasTimeout;
|
private boolean hasTimeout;
|
||||||
private Policy policy;
|
private Policy policy;
|
||||||
private CountDownTimer timer;
|
private CountDownTimer timer;
|
||||||
|
private FingerprintHelper fingerprintHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
@@ -80,6 +86,15 @@ public class RequestActivity extends Activity {
|
|||||||
new SocketManager(this).exec();
|
new SocketManager(this).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
if (timer != null)
|
||||||
|
timer.cancel();
|
||||||
|
if (fingerprintHelper != null)
|
||||||
|
fingerprintHelper.cancel();
|
||||||
|
super.finish();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean cancelTimeout() {
|
private boolean cancelTimeout() {
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
deny_btn.setText(getString(R.string.deny));
|
deny_btn.setText(getString(R.string.deny));
|
||||||
@@ -128,11 +143,49 @@ public class RequestActivity extends Activity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
grant_btn.setOnClickListener(v -> {
|
boolean useFingerprint = mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) && FingerprintHelper.canUseFingerprint();
|
||||||
handleAction(Policy.ALLOW);
|
|
||||||
timer.cancel();
|
if (useFingerprint) {
|
||||||
});
|
try {
|
||||||
grant_btn.requestFocus();
|
fingerprintHelper = new FingerprintHelper() {
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
||||||
|
warning.setText(errString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
||||||
|
warning.setText(helpString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
||||||
|
handleAction(Policy.ALLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
warning.setText(R.string.auth_fail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fingerprintHelper.startAuth();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
useFingerprint = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!useFingerprint) {
|
||||||
|
grant_btn.setOnClickListener(v -> {
|
||||||
|
handleAction(Policy.ALLOW);
|
||||||
|
timer.cancel();
|
||||||
|
});
|
||||||
|
grant_btn.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE);
|
||||||
|
fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
deny_btn.setOnClickListener(v -> {
|
deny_btn.setOnClickListener(v -> {
|
||||||
handleAction(Policy.DENY);
|
handleAction(Policy.DENY);
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
@@ -151,8 +204,9 @@ public class RequestActivity extends Activity {
|
|||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (policy != null) {
|
if (policy != null) {
|
||||||
handleAction(Policy.DENY);
|
handleAction(Policy.DENY);
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAction() {
|
void handleAction() {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ public class Const {
|
|||||||
public static final String UTIL_FUNCTIONS= "util_functions.sh";
|
public static final String UTIL_FUNCTIONS= "util_functions.sh";
|
||||||
public static final String ANDROID_MANIFEST = "AndroidManifest.xml";
|
public static final String ANDROID_MANIFEST = "AndroidManifest.xml";
|
||||||
|
|
||||||
|
public static final String SU_KEYSTORE_KEY = "su_key";
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
|
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
|
||||||
public static final String TMP_FOLDER_PATH = "/dev/tmp";
|
public static final String TMP_FOLDER_PATH = "/dev/tmp";
|
||||||
@@ -30,6 +32,11 @@ public class Const {
|
|||||||
public static final File EXTERNAL_PATH = new File(Environment.getExternalStorageDirectory(), "MagiskManager");
|
public static final File EXTERNAL_PATH = new File(Environment.getExternalStorageDirectory(), "MagiskManager");
|
||||||
public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
|
public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
|
||||||
|
|
||||||
|
// Versions
|
||||||
|
public static final int UPDATE_SERVICE_VER = 1;
|
||||||
|
public static final int SNET_VER = 7;
|
||||||
|
public static final int MIN_MODULE_VER = 1400;
|
||||||
|
|
||||||
public static String BUSYBOX_PATH() {
|
public static String BUSYBOX_PATH() {
|
||||||
if (Utils.itemExist("/sbin/.core/busybox/busybox")) {
|
if (Utils.itemExist("/sbin/.core/busybox/busybox")) {
|
||||||
return "/sbin/.core/busybox";
|
return "/sbin/.core/busybox";
|
||||||
@@ -104,6 +111,7 @@ public class Const {
|
|||||||
public static final String SU_AUTO_RESPONSE = "su_auto_response";
|
public static final String SU_AUTO_RESPONSE = "su_auto_response";
|
||||||
public static final String SU_NOTIFICATION = "su_notification";
|
public static final String SU_NOTIFICATION = "su_notification";
|
||||||
public static final String SU_REAUTH = "su_reauth";
|
public static final String SU_REAUTH = "su_reauth";
|
||||||
|
public static final String SU_FINGERPRINT = "su_fingerprint";
|
||||||
|
|
||||||
// intents
|
// intents
|
||||||
public static final String OPEN_SECTION = "section";
|
public static final String OPEN_SECTION = "section";
|
||||||
@@ -118,7 +126,6 @@ public class Const {
|
|||||||
public static final String UPDATE_CHANNEL = "update_channel";
|
public static final String UPDATE_CHANNEL = "update_channel";
|
||||||
public static final String CUSTOM_CHANNEL = "custom_channel";
|
public static final String CUSTOM_CHANNEL = "custom_channel";
|
||||||
public static final String BOOT_FORMAT = "boot_format";
|
public static final String BOOT_FORMAT = "boot_format";
|
||||||
public static final String SNET_VER = "snet_version";
|
|
||||||
public static final String UPDATE_SERVICE_VER = "update_service_version";
|
public static final String UPDATE_SERVICE_VER = "update_service_version";
|
||||||
public static final String APP_VER = "app_version";
|
public static final String APP_VER = "app_version";
|
||||||
public static final String MAGISKHIDE = "magiskhide";
|
public static final String MAGISKHIDE = "magiskhide";
|
||||||
@@ -129,6 +136,7 @@ public class Const {
|
|||||||
public static final String ETAG_KEY = "ETag";
|
public static final String ETAG_KEY = "ETag";
|
||||||
public static final String LINK_KEY = "Link";
|
public static final String LINK_KEY = "Link";
|
||||||
public static final String IF_NONE_MATCH = "If-None-Match";
|
public static final String IF_NONE_MATCH = "If-None-Match";
|
||||||
|
public static final String REPO_ORDER = "repo_order";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -158,8 +166,7 @@ public class Const {
|
|||||||
public static final String PATCH_BOOT = "patch";
|
public static final String PATCH_BOOT = "patch";
|
||||||
public static final String FLASH_MAGISK = "magisk";
|
public static final String FLASH_MAGISK = "magisk";
|
||||||
public static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
public static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
||||||
public static final int UPDATE_SERVICE_VER = 1;
|
public static final int ORDER_NAME = 0;
|
||||||
public static final int SNET_VER = 7;
|
public static final int ORDER_DATE = 1;
|
||||||
public static final int MIN_MODULE_VER = 1400;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
|
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||||
|
import android.security.keystore.KeyProperties;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
|
||||||
|
import java.security.KeyStore;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
public abstract class FingerprintHelper extends FingerprintManager.AuthenticationCallback {
|
||||||
|
|
||||||
|
private FingerprintManager manager;
|
||||||
|
private Cipher cipher;
|
||||||
|
private CancellationSignal cancel;
|
||||||
|
|
||||||
|
public static boolean canUseFingerprint() {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||||
|
return false;
|
||||||
|
MagiskManager mm = MagiskManager.get();
|
||||||
|
KeyguardManager km = mm.getSystemService(KeyguardManager.class);
|
||||||
|
FingerprintManager fm = mm.getSystemService(FingerprintManager.class);
|
||||||
|
return km.isKeyguardSecure() && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected FingerprintHelper() throws Exception {
|
||||||
|
MagiskManager mm = MagiskManager.get();
|
||||||
|
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||||
|
manager = mm.getSystemService(FingerprintManager.class);
|
||||||
|
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
||||||
|
+ KeyProperties.BLOCK_MODE_CBC + "/"
|
||||||
|
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
||||||
|
keyStore.load(null);
|
||||||
|
SecretKey key = (SecretKey) keyStore.getKey(Const.SU_KEYSTORE_KEY, null);
|
||||||
|
if (key == null) {
|
||||||
|
key = generateKey();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||||
|
} catch (KeyPermanentlyInvalidatedException e) {
|
||||||
|
// Only happens on Marshmallow
|
||||||
|
key = generateKey();
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startAuth() {
|
||||||
|
cancel = new CancellationSignal();
|
||||||
|
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
|
||||||
|
manager.authenticate(cryptoObject, cancel, 0, this, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
if (cancel != null)
|
||||||
|
cancel.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey generateKey() throws Exception {
|
||||||
|
KeyGenerator keygen = KeyGenerator
|
||||||
|
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
|
||||||
|
KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
|
||||||
|
Const.SU_KEYSTORE_KEY,
|
||||||
|
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
||||||
|
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||||
|
.setUserAuthenticationRequired(true)
|
||||||
|
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
builder.setInvalidatedByBiometricEnrollment(false);
|
||||||
|
}
|
||||||
|
keygen.init(builder.build());
|
||||||
|
return keygen.generateKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -89,13 +89,10 @@ public class Shell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Root shell initialization
|
// Root shell initialization
|
||||||
String bbpath = Const.BUSYBOX_PATH();
|
|
||||||
mm.shell.run_raw(false, false,
|
mm.shell.run_raw(false, false,
|
||||||
"export PATH=" + bbpath + ":$PATH",
|
"export PATH=" + Const.BUSYBOX_PATH() + ":$PATH",
|
||||||
"mount_partitions",
|
"mount_partitions",
|
||||||
"find_boot_image",
|
"run_migrations");
|
||||||
"find_dtbo_image",
|
|
||||||
"migrate_boot_backup");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.jar.JarInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
public class ZipUtils {
|
public class ZipUtils {
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ public class ZipUtils {
|
|||||||
|
|
||||||
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws Exception {
|
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws Exception {
|
||||||
try {
|
try {
|
||||||
JarInputStream zipfile = new JarInputStream(zip);
|
ZipInputStream zipfile = new ZipInputStream(zip);
|
||||||
JarEntry entry;
|
ZipEntry entry;
|
||||||
while ((entry = zipfile.getNextJarEntry()) != null) {
|
while ((entry = zipfile.getNextEntry()) != null) {
|
||||||
if (!entry.getName().startsWith(path) || entry.isDirectory()){
|
if (!entry.getName().startsWith(path) || entry.isDirectory()){
|
||||||
// Ignore directories, only create files
|
// Ignore directories, only create files
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
9
src/main/res/drawable/ic_fingerprint.xml
Normal file
9
src/main/res/drawable/ic_fingerprint.xml
Normal file
File diff suppressed because one or more lines are too long
9
src/main/res/drawable/ic_sort.xml
Normal file
9
src/main/res/drawable/ic_sort.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/imageColorTint"
|
||||||
|
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
|
||||||
|
</vector>
|
||||||
@@ -101,18 +101,27 @@
|
|||||||
<Button
|
<Button
|
||||||
style="?android:buttonBarButtonStyle"
|
style="?android:buttonBarButtonStyle"
|
||||||
android:text="@string/deny_with_str"
|
android:text="@string/deny_with_str"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/deny_btn"
|
android:id="@+id/deny_btn"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
style="?android:buttonBarButtonStyle"
|
|
||||||
android:text="@string/grant"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:id="@+id/grant_btn"
|
android:id="@+id/grant_btn"
|
||||||
android:layout_weight="1" />
|
style="?android:buttonBarButtonStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/grant" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/fingerprint"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="7dp"
|
||||||
|
android:tint="?attr/colorAccent"
|
||||||
|
android:src="@drawable/ic_fingerprint" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -15,71 +15,63 @@
|
|||||||
card_view:cardCornerRadius="@dimen/card_corner_radius"
|
card_view:cardCornerRadius="@dimen/card_corner_radius"
|
||||||
card_view:cardElevation="@dimen/card_elevation">
|
card_view:cardElevation="@dimen/card_elevation">
|
||||||
|
|
||||||
<RelativeLayout
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:id="@+id/info_layout"
|
||||||
android:padding="@dimen/card_layout_padding">
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/app_icon"
|
android:id="@+id/app_icon"
|
||||||
android:layout_width="@dimen/card_appicon_size"
|
android:layout_width="50dp"
|
||||||
android:layout_height="@dimen/card_appicon_size"
|
android:layout_height="50dp"
|
||||||
android:layout_centerVertical="true"
|
android:layout_gravity="center_vertical"
|
||||||
android:scaleType="centerCrop"/>
|
android:gravity="end" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
android:layout_alignBottom="@+id/app_icon"
|
|
||||||
android:gravity="center_horizontal"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingEnd="@dimen/card_appicon_size"
|
android:layout_width="0dp"
|
||||||
android:paddingStart="65dp"
|
android:layout_height="wrap_content"
|
||||||
android:weightSum="1">
|
android:layout_weight="1"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="5dp"
|
||||||
|
android:layout_marginEnd="5dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/app_name"
|
android:id="@+id/app_name"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:paddingEnd="3dp"
|
android:ellipsize="end"
|
||||||
android:paddingStart="3dp"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textStyle="bold"/>
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/app_package"
|
android:id="@+id/package_name"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="25dp"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="marquee"
|
android:maxLines="1"
|
||||||
android:gravity="center_vertical"
|
android:ellipsize="end"
|
||||||
android:marqueeRepeatLimit="marquee_forever"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:paddingEnd="3dp"
|
android:textColor="@android:color/tertiary_text_dark"
|
||||||
android:paddingStart="3dp"
|
android:textIsSelectable="false" />
|
||||||
android:singleLine="true"/>
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:focusable="false"
|
||||||
android:layout_centerVertical="true">
|
android:layout_gravity="center_vertical"
|
||||||
|
android:src="@drawable/ic_menu_overflow_material"
|
||||||
<CheckBox
|
android:checked="false" />
|
||||||
android:id="@+id/checkbox"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:focusable="false"
|
|
||||||
android:gravity="center"
|
|
||||||
android:src="@drawable/ic_menu_overflow_material"
|
|
||||||
android:checked="false" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</android.support.v7.widget.CardView>
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,6 @@
|
|||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
|
||||||
android:textIsSelectable="false"/>
|
android:textIsSelectable="false"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -67,6 +67,15 @@
|
|||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
android:textIsSelectable="false" />
|
android:textIsSelectable="false" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/update_time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:textColor="@android:color/tertiary_text_dark"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
android:textStyle="bold|italic" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
|||||||
@@ -2,9 +2,15 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/repo_sort"
|
||||||
|
android:icon="@drawable/ic_sort"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/repo_search"
|
android:id="@+id/repo_search"
|
||||||
android:title=""
|
android:title=""
|
||||||
app:actionViewClass="android.widget.SearchView"
|
app:actionViewClass="android.widget.SearchView"
|
||||||
app:showAsAction="always"/>
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
@@ -53,6 +53,10 @@
|
|||||||
<string name="update_available">Aktualisierung verfügbar</string>
|
<string name="update_available">Aktualisierung verfügbar</string>
|
||||||
<string name="installed">Installiert</string>
|
<string name="installed">Installiert</string>
|
||||||
<string name="not_installed">Nicht installiert</string>
|
<string name="not_installed">Nicht installiert</string>
|
||||||
|
<string name="updated_on">Zuletzt aktualisiert: %1$s</string>
|
||||||
|
<string name="sorting_order">Sortierung</string>
|
||||||
|
<string name="sort_by_name">Nach Namen sortiert</string>
|
||||||
|
<string name="sort_by_update">Nach Aktualisierung sortiert</string>
|
||||||
|
|
||||||
<!--Log Fragment-->
|
<!--Log Fragment-->
|
||||||
<string name="menuSaveLog">Protokoll speichern</string>
|
<string name="menuSaveLog">Protokoll speichern</string>
|
||||||
@@ -179,6 +183,7 @@
|
|||||||
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namensraum</string>
|
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namensraum</string>
|
||||||
<string name="requester_summary">Root-Sitzungen erben den Namensraum des Abfragenden</string>
|
<string name="requester_summary">Root-Sitzungen erben den Namensraum des Abfragenden</string>
|
||||||
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namensraum</string>
|
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namensraum</string>
|
||||||
|
<string name="android_o_not_support">Android 8.0+ wird nicht unterstützt</string>
|
||||||
|
|
||||||
<!--Superuser-->
|
<!--Superuser-->
|
||||||
<string name="su_request_title">Superuser-Anfrage</string>
|
<string name="su_request_title">Superuser-Anfrage</string>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user