Separate backend logic from frontend UI

This commit is contained in:
topjohnwu
2018-12-13 04:35:50 -05:00
parent 006d28abd5
commit ff3d66a661
87 changed files with 1194 additions and 1138 deletions

1
app/.gitignore vendored
View File

@ -6,7 +6,6 @@
app/release
*.hprof
.externalNativeBuild/
src/full/res/raw/util_functions.sh
public.certificate.x509.pem
private.key.pk8
*.apk

View File

@ -73,6 +73,7 @@ dependencies {
implementation 'androidx.core:core:1.0.1'
implementation project(':net')
fullImplementation project(':utils')
fullImplementation project(':core')
fullImplementation 'androidx.appcompat:appcompat:1.0.2'
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"

View File

@ -23,14 +23,11 @@
-dontwarn javax.naming.**
# Snet extention
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
-keepclassmembers class com.topjohnwu.magisk.utils.BootSigner { *; }
# Fast Android Networking Library
-dontwarn okhttp3.**
-keepclassmembers class com.topjohnwu.core.utils.ISafetyNetHelper { *; }
-keepclassmembers class com.topjohnwu.core.utils.BootSigner { *; }
# Strip logging
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
-assumenosideeffects class com.topjohnwu.core.utils.Logger {
public *** debug(...);
}

View File

@ -40,9 +40,6 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name="a.g"
android:theme="@style/AppTheme.Translucent" />
<!-- Superuser -->

View File

@ -1,6 +1,6 @@
package a;
import com.topjohnwu.magisk.utils.BootSigner;
import com.topjohnwu.core.utils.BootSigner;
import androidx.annotation.Keep;

View File

@ -1,7 +0,0 @@
package a;
import com.topjohnwu.magisk.NoUIActivity;
public class g extends NoUIActivity {
/* stub */
}

View File

@ -1,7 +1,7 @@
package a;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
public class q extends MagiskManager {
public class q extends App {
/* stub */
}

View File

@ -5,10 +5,11 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.core.Const;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.components.MarkDownWindow;
import com.topjohnwu.magisk.utils.AppUtils;
import java.util.Locale;
@ -62,9 +63,9 @@ public class AboutActivity extends BaseActivity {
appTranslators.setSummary(translators);
}
appSourceCode.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.SOURCE_CODE_URL)));
supportThread.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.XDA_THREAD)));
twitter.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.TWITTER_URL)));
appSourceCode.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.SOURCE_CODE_URL)));
supportThread.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.XDA_THREAD)));
twitter.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.TWITTER_URL)));
setFloating();
}

View File

@ -0,0 +1,34 @@
package com.topjohnwu.magisk;
import com.topjohnwu.core.App;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.services.UpdateCheckService;
import java.util.HashMap;
import java.util.Map;
public class ClassMap {
private static Map<Class, Class> classMap = new HashMap<>();
static {
classMap.put(App.class, a.q.class);
classMap.put(MainActivity.class, a.b.class);
classMap.put(SplashActivity.class, a.c.class);
classMap.put(AboutActivity.class, a.d.class);
classMap.put(DonationActivity.class, a.e.class);
classMap.put(FlashActivity.class, a.f.class);
classMap.put(GeneralReceiver.class, a.h.class);
classMap.put(ShortcutReceiver.class, a.i.class);
classMap.put(OnBootService.class, a.j.class);
classMap.put(UpdateCheckService.class, a.k.class);
classMap.put(AboutCardRow.class, a.l.class);
classMap.put(SuRequestActivity.class, a.m.class);
}
public static Class get(Class c) {
return classMap.get(c);
}
}

View File

@ -1,150 +0,0 @@
package com.topjohnwu.magisk;
import android.os.Environment;
import android.os.Process;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class Const {
public static final String DEBUG_TAG = "MagiskManager";
// APK content
public static final String ANDROID_MANIFEST = "AndroidManifest.xml";
public static final String SU_KEYSTORE_KEY = "su_key";
// Paths
public static final String MAGISK_PATH = "/sbin/.magisk/img";
public static final File EXTERNAL_PATH;
public static File MAGISK_DISABLE_FILE;
static {
MAGISK_DISABLE_FILE = new File("xxx");
EXTERNAL_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
EXTERNAL_PATH.mkdirs();
}
public static final String BUSYBOX_PATH = "/sbin/.magisk/busybox";
public static final String TMP_FOLDER_PATH = "/dev/tmp";
public static final String MAGISK_LOG = "/cache/magisk.log";
public static final String MANAGER_CONFIGS = ".tmp.magisk.config";
// Versions
public static final int UPDATE_SERVICE_VER = 1;
public static final int MIN_MODULE_VER = 1500;
public static final int SNET_EXT_VER = 12;
/* A list of apps that should not be shown as hide-able */
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
Data.MM().getPackageName(),
"com.google.android.gms"
);
public static final int USER_ID = Process.myUid() / 100000;
public static final class MAGISK_VER {
/* Currently no backwards compatibility needed */
}
public static class ID {
public static final int UPDATE_SERVICE_ID = 1;
public static final int FETCH_ZIP = 2;
public static final int SELECT_BOOT = 3;
public static final int ONBOOT_SERVICE_ID = 6;
// notifications
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
public static final int APK_UPDATE_NOTIFICATION_ID = 5;
public static final int DTBO_NOTIFICATION_ID = 7;
public static final int HIDE_MANAGER_NOTIFICATION_ID = 8;
public static final String UPDATE_NOTIFICATION_CHANNEL = "update";
public static final String PROGRESS_NOTIFICATION_CHANNEL = "progress";
}
public static class Url {
public static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/stable.json";
public static final String BETA_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/beta.json";
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed&page=%d";
public static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
public static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
public static final String PAYPAL_URL = "https://www.paypal.me/topjohnwu";
public static final String PATREON_URL = "https://www.patreon.com/topjohnwu";
public static final String TWITTER_URL = "https://twitter.com/topjohnwu";
public static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
public static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk";
public static final String SNET_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/b66b1a914978e5f4c4bbfd74a59f4ad371bac107/snet.apk";
}
public static class Key {
// su
public static final String ROOT_ACCESS = "root_access";
public static final String SU_MULTIUSER_MODE = "multiuser_mode";
public static final String SU_MNT_NS = "mnt_ns";
public static final String SU_MANAGER = "requester";
public static final String SU_REQUEST_TIMEOUT = "su_request_timeout";
public static final String SU_AUTO_RESPONSE = "su_auto_response";
public static final String SU_NOTIFICATION = "su_notification";
public static final String SU_REAUTH = "su_reauth";
public static final String SU_FINGERPRINT = "su_fingerprint";
// intents
public static final String OPEN_SECTION = "section";
public static final String INTENT_SET_NAME = "filename";
public static final String INTENT_SET_LINK = "link";
public static final String FLASH_ACTION = "action";
public static final String FLASH_SET_BOOT = "boot";
public static final String BROADCAST_MANAGER_UPDATE = "manager_update";
public static final String BROADCAST_REBOOT = "reboot";
// others
public static final String CHECK_UPDATES = "check_update";
public static final String UPDATE_CHANNEL = "update_channel";
public static final String CUSTOM_CHANNEL = "custom_channel";
public static final String BOOT_FORMAT = "boot_format";
public static final String UPDATE_SERVICE_VER = "update_service_version";
public static final String APP_VER = "app_version";
public static final String MAGISKHIDE = "magiskhide";
public static final String HOSTS = "hosts";
public static final String COREONLY = "disable";
public static final String LOCALE = "locale";
public static final String DARK_THEME = "dark_theme";
public static final String ETAG_KEY = "ETag";
public static final String LINK_KEY = "Link";
public static final String IF_NONE_MATCH = "If-None-Match";
public static final String REPO_ORDER = "repo_order";
}
public static class Value {
public static final int STABLE_CHANNEL = 0;
public static final int BETA_CHANNEL = 1;
public static final int CUSTOM_CHANNEL = 2;
public static final int ROOT_ACCESS_DISABLED = 0;
public static final int ROOT_ACCESS_APPS_ONLY = 1;
public static final int ROOT_ACCESS_ADB_ONLY = 2;
public static final int ROOT_ACCESS_APPS_AND_ADB = 3;
public static final int MULTIUSER_MODE_OWNER_ONLY = 0;
public static final int MULTIUSER_MODE_OWNER_MANAGED = 1;
public static final int MULTIUSER_MODE_USER = 2;
public static final int NAMESPACE_MODE_GLOBAL = 0;
public static final int NAMESPACE_MODE_REQUESTER = 1;
public static final int NAMESPACE_MODE_ISOLATE = 2;
public static final int NO_NOTIFICATION = 0;
public static final int NOTIFICATION_TOAST = 1;
public static final int SU_PROMPT = 0;
public static final int SU_AUTO_DENY = 1;
public static final int SU_AUTO_ALLOW = 2;
public static final String FLASH_ZIP = "flash";
public static final String PATCH_BOOT = "patch";
public static final String FLASH_MAGISK = "magisk";
public static final String FLASH_INACTIVE_SLOT = "slot";
public static final String UNINSTALL = "uninstall";
public static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
public static final int ORDER_NAME = 0;
public static final int ORDER_DATE = 1;
}
}

View File

@ -1,190 +0,0 @@
package com.topjohnwu.magisk;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import android.util.Xml;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class Data {
// Global app instance
public static WeakReference<MagiskManager> weakApp;
public static Handler mainHandler = new Handler(Looper.getMainLooper());
public static Map<Class, Class> classMap = new HashMap<>();
// Current status
public static String magiskVersionString;
public static int magiskVersionCode = -1;
public static boolean magiskHide;
// Update Info
public static String remoteMagiskVersionString;
public static int remoteMagiskVersionCode = -1;
public static String magiskLink;
public static String magiskNoteLink;
public static String magiskMD5;
public static String remoteManagerVersionString;
public static int remoteManagerVersionCode = -1;
public static String managerLink;
public static String managerNoteLink;
public static String uninstallerLink;
// Install flags
public static boolean keepVerity = false;
public static boolean keepEnc = false;
// Configs
public static boolean isDarkTheme;
public static int suRequestTimeout;
public static int suLogTimeout = 14;
public static int multiuserState = -1;
public static int suResponseType;
public static int suNotificationType;
public static int updateChannel;
public static int repoOrder;
static {
classMap.put(MagiskManager.class, a.q.class);
classMap.put(MainActivity.class, a.b.class);
classMap.put(SplashActivity.class, a.c.class);
classMap.put(AboutActivity.class, a.d.class);
classMap.put(DonationActivity.class, a.e.class);
classMap.put(FlashActivity.class, a.f.class);
classMap.put(NoUIActivity.class, a.g.class);
classMap.put(GeneralReceiver.class, a.h.class);
classMap.put(ShortcutReceiver.class, a.i.class);
classMap.put(OnBootService.class, a.j.class);
classMap.put(UpdateCheckService.class, a.k.class);
classMap.put(AboutCardRow.class, a.l.class);
classMap.put(SuRequestActivity.class, a.m.class);
}
public static void loadMagiskInfo() {
try {
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
magiskHide = Shell.su("magiskhide --status").exec().isSuccess();
} catch (NumberFormatException ignored) {}
}
public static MagiskManager MM() {
return weakApp.get();
}
public static void exportPrefs() {
// Flush prefs to disk
MagiskManager mm = MM();
mm.prefs.edit().commit();
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
mm.getPackageName() + "_preferences.xml");
Shell.su(Utils.fmt("cat %s > /data/user/0/%s", xml, Const.MANAGER_CONFIGS)).exec();
}
public static void importPrefs() {
MagiskManager mm = MM();
SuFile config = new SuFile("/data/user/0/" + Const.MANAGER_CONFIGS);
if (config.exists()) {
SharedPreferences.Editor editor = mm.prefs.edit();
try {
SuFileInputStream is = new SuFileInputStream(config);
XmlPullParser parser = Xml.newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
parser.setInput(is, "UTF-8");
parser.nextTag();
parser.require(XmlPullParser.START_TAG, null, "map");
while (parser.next() != XmlPullParser.END_TAG) {
if (parser.getEventType() != XmlPullParser.START_TAG)
continue;
String key = parser.getAttributeValue(null, "name");
String value = parser.getAttributeValue(null, "value");
switch (parser.getName()) {
case "string":
parser.require(XmlPullParser.START_TAG, null, "string");
editor.putString(key, parser.nextText());
parser.require(XmlPullParser.END_TAG, null, "string");
break;
case "boolean":
parser.require(XmlPullParser.START_TAG, null, "boolean");
editor.putBoolean(key, Boolean.parseBoolean(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "boolean");
break;
case "int":
parser.require(XmlPullParser.START_TAG, null, "int");
editor.putInt(key, Integer.parseInt(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "int");
break;
case "long":
parser.require(XmlPullParser.START_TAG, null, "long");
editor.putLong(key, Long.parseLong(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "long");
break;
case "float":
parser.require(XmlPullParser.START_TAG, null, "int");
editor.putFloat(key, Float.parseFloat(value));
parser.nextTag();
parser.require(XmlPullParser.END_TAG, null, "int");
break;
default:
parser.next();
}
}
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
}
editor.remove(Const.Key.ETAG_KEY);
editor.apply();
loadConfig();
config.delete();
}
}
public static void loadConfig() {
MagiskManager mm = MM();
// su
suRequestTimeout = Utils.getPrefsInt(mm.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
suResponseType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
suNotificationType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
// config
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
updateChannel = Utils.getPrefsInt(mm.prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
repoOrder = mm.prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_DATE);
}
public static void writeConfig() {
MM().prefs.edit()
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
.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.UPDATE_CHANNEL, String.valueOf(updateChannel))
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
.putInt(Const.Key.REPO_ORDER, repoOrder)
.apply();
}
}

View File

@ -3,9 +3,10 @@ package com.topjohnwu.magisk;
import android.net.Uri;
import android.os.Bundle;
import com.topjohnwu.core.Const;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.AppUtils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
@ -38,7 +39,7 @@ public class DonationActivity extends BaseActivity {
ab.setDisplayHomeAsUpEnabled(true);
}
paypal.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.PAYPAL_URL)));
patreon.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.PATREON_URL)));
paypal.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.PAYPAL_URL)));
patreon.setOnClickListener(v -> AppUtils.openLink(this, Uri.parse(Const.Url.PATREON_URL)));
}
}

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -12,18 +11,22 @@ import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.tasks.FlashZip;
import com.topjohnwu.core.tasks.MagiskInstaller;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.CallbackList;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@ -40,7 +43,7 @@ public class FlashActivity extends BaseActivity {
@BindView(R.id.reboot) public Button reboot;
@BindView(R.id.scrollView) ScrollView sv;
private List<String> logs;
private List<String> console, logs;
@OnClick(R.id.reboot)
void reboot() {
@ -49,7 +52,7 @@ public class FlashActivity extends BaseActivity {
@OnClick(R.id.save_logs)
void saveLogs() {
runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
runWithExternalRW(() -> {
Calendar now = Calendar.getInstance();
String filename = String.format(Locale.US,
"magisk_install_log_%04d%02d%02d_%02d%02d%02d.log",
@ -71,6 +74,17 @@ public class FlashActivity extends BaseActivity {
});
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button
}
@Override
public int getDarkTheme() {
return R.style.AppTheme_StatusBar_Dark;
@ -92,77 +106,155 @@ public class FlashActivity extends BaseActivity {
if (!Shell.rootAccess())
reboot.setVisibility(View.GONE);
logs = new ArrayList<>();
CallbackList<String> console = new CallbackList<String>(new ArrayList<>()) {
logs = Collections.synchronizedList(new ArrayList<>());
console = new ConsoleList();
private void updateUI() {
flashLogs.setText(TextUtils.join("\n", this));
sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
}
@Override
public void onAddElement(String s) {
logs.add(s);
updateUI();
}
@Override
public String set(int i, String s) {
String ret = super.set(i, s);
Data.mainHandler.post(this::updateUI);
return ret;
}
};
// We must receive a Uri of the target zip
Intent intent = getIntent();
Uri uri = intent.getData();
switch (intent.getStringExtra(Const.Key.FLASH_ACTION)) {
case Const.Value.FLASH_ZIP:
new FlashZip(this, uri, console, logs).exec();
new FlashModule(uri).exec();
break;
case Const.Value.UNINSTALL:
new UninstallMagisk(this, uri, console, logs).exec();
new Uninstall(uri).exec();
break;
case Const.Value.FLASH_MAGISK:
new InstallMagisk(this, console, logs, InstallMagisk.DIRECT_MODE).exec();
new DirectInstall().exec();
break;
case Const.Value.FLASH_INACTIVE_SLOT:
new InstallMagisk(this, console, logs, InstallMagisk.SECOND_SLOT_MODE).exec();
new SecondSlot().exec();
break;
case Const.Value.PATCH_BOOT:
new InstallMagisk(this, console, logs,
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
new PatchBoot(uri).exec();
break;
}
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
}
private class ConsoleList extends CallbackList<String> {
@Override
public void onBackPressed() {
// Prevent user accidentally press back button
}
ConsoleList() {
super(new ArrayList<>());
}
private static class UninstallMagisk extends FlashZip {
private UninstallMagisk(BaseActivity context, Uri uri, List<String> console, List<String> logs) {
super(context, uri, console, logs);
private void updateUI() {
flashLogs.setText(TextUtils.join("\n", this));
sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
}
@Override
protected void onPostExecute(Integer result) {
if (result == 1) {
Data.mainHandler.postDelayed(() ->
Shell.su("pm uninstall " + getActivity().getPackageName()).exec(), 3000);
} else {
super.onPostExecute(result);
}
public void onAddElement(String s) {
logs.add(s);
updateUI();
}
@Override
public String set(int i, String s) {
String ret = super.set(i, s);
App.mainHandler.post(this::updateUI);
return ret;
}
}
private class FlashModule extends FlashZip {
FlashModule(Uri uri) {
super(uri, console, logs);
}
@Override
protected void onResult(boolean success) {
if (success) {
Utils.loadModules();
} else {
console.add("! Installation failed");
reboot.setVisibility(View.GONE);
}
buttonPanel.setVisibility(View.VISIBLE);
}
}
private class Uninstall extends FlashModule {
Uninstall(Uri uri) {
super(uri);
}
@Override
protected void onResult(boolean success) {
if (success)
App.mainHandler.postDelayed(Shell.su("pm uninstall " + getPackageName())::exec, 3000);
else
super.onResult(false);
}
}
private abstract class BaseInstaller extends MagiskInstaller {
BaseInstaller() {
super(console, logs);
}
@Override
protected void onResult(boolean success) {
if (success) {
console.add("- All done!");
} else {
Shell.sh("rm -rf " + installDir).submit();
console.add("! Installation failed");
reboot.setVisibility(View.GONE);
}
buttonPanel.setVisibility(View.VISIBLE);
}
}
private class DirectInstall extends BaseInstaller {
@Override
protected boolean operations() {
console.add("- Detecting target image");
srcBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
if (srcBoot.isEmpty()) {
console.add("! Unable to detect target image");
return false;
}
return extractZip() && patchBoot() && flashBoot();
}
}
private class SecondSlot extends BaseInstaller {
@Override
protected boolean operations() {
String slot = ShellUtils.fastCmd("echo $SLOT");
String target = (TextUtils.equals(slot, "_a") ? "_b" : "_a");
console.add("- Target slot: " + target);
console.add("- Detecting target image");
srcBoot = ShellUtils.fastCmd(
"SLOT=" + target,
"find_boot_image",
"SLOT=" + slot,
"echo \"$BOOTIMAGE\""
);
if (srcBoot.isEmpty()) {
console.add("! Unable to detect target image");
return false;
}
return extractZip() && patchBoot() && flashBoot() && postOTA();
}
}
private class PatchBoot extends BaseInstaller {
private Uri uri;
PatchBoot(Uri u) {
uri = u;
}
@Override
protected boolean operations() {
return copyBoot(uri) && extractZip() && patchBoot() && storeBoot();
}
}
}

View File

@ -1,52 +0,0 @@
package com.topjohnwu.magisk;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.preference.PreferenceManager;
import com.topjohnwu.magisk.database.MagiskDB;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.superuser.ContainerApp;
import com.topjohnwu.superuser.Shell;
import java.lang.ref.WeakReference;
public class MagiskManager extends ContainerApp {
// Info
public boolean hasInit = false;
// Global resources
public SharedPreferences prefs;
public RepoDatabaseHelper repoDB;
public MagiskDB mDB;
public MagiskManager() {
Data.weakApp = new WeakReference<>(this);
}
@Override
public void onCreate() {
super.onCreate();
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER);
Shell.Config.verboseLogging(BuildConfig.DEBUG);
Shell.Config.setInitializer(RootUtils.class);
Shell.Config.setTimeout(2);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
repoDB = new RepoDatabaseHelper(this);
mDB = new MagiskDB(this);
LocaleManager.setLocale(this);
Data.loadConfig();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
LocaleManager.setLocale(this);
}
}

View File

@ -8,6 +8,10 @@ import android.view.MenuItem;
import android.view.View;
import com.google.android.material.navigation.NavigationView;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.fragments.LogFragment;
import com.topjohnwu.magisk.fragments.MagiskFragment;
@ -17,8 +21,6 @@ import com.topjohnwu.magisk.fragments.ReposFragment;
import com.topjohnwu.magisk.fragments.SettingsFragment;
import com.topjohnwu.magisk.fragments.SuperuserFragment;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import androidx.annotation.NonNull;
@ -49,8 +51,8 @@ public class MainActivity extends BaseActivity
@Override
protected void onCreate(final Bundle savedInstanceState) {
if (!mm.hasInit) {
startActivity(new Intent(this, Data.classMap.get(SplashActivity.class)));
if (!app.init) {
startActivity(new Intent(this, ClassMap.get(SplashActivity.class)));
finish();
}
@ -120,7 +122,7 @@ public class MainActivity extends BaseActivity
public void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
app.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
&& Shell.rootAccess() && Data.magiskVersionCode >= 0);
@ -189,11 +191,11 @@ public class MainActivity extends BaseActivity
displayFragment(new SettingsFragment(), true);
break;
case R.id.app_about:
startActivity(new Intent(this, Data.classMap.get(AboutActivity.class)));
startActivity(new Intent(this, ClassMap.get(AboutActivity.class)));
mDrawerItem = bak;
break;
case R.id.donation:
startActivity(new Intent(this, Data.classMap.get(DonationActivity.class)));
startActivity(new Intent(this, ClassMap.get(DonationActivity.class)));
mDrawerItem = bak;
break;
}

View File

@ -1,13 +0,0 @@
package com.topjohnwu.magisk;
import com.topjohnwu.magisk.components.BaseActivity;
import androidx.annotation.NonNull;
public class NoUIActivity extends BaseActivity {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
finish();
}
}

View File

@ -5,14 +5,17 @@ import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.core.tasks.UpdateRepos;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
import com.topjohnwu.magisk.utils.AppUtils;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
public class SplashActivity extends BaseActivity {
@ -21,9 +24,9 @@ public class SplashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String pkg = mm.mDB.getStrings(Const.Key.SU_MANAGER, null);
String pkg = app.mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(BuildConfig.APPLICATION_ID)) {
mm.mDB.setStrings(Const.Key.SU_MANAGER, null);
app.mDB.setStrings(Const.Key.SU_MANAGER, null);
Shell.su("pm uninstall " + pkg).exec();
}
if (TextUtils.equals(pkg, getPackageName())) {
@ -37,7 +40,7 @@ public class SplashActivity extends BaseActivity {
// Magisk working as expected
if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
// Update check service
Utils.setupUpdateCheck();
AppUtils.setupUpdateCheck();
// Load modules
Utils.loadModules();
}
@ -45,13 +48,13 @@ public class SplashActivity extends BaseActivity {
Data.importPrefs();
// Dynamic detect all locales
LocaleManager.loadAvailableLocales();
LocaleManager.loadAvailableLocales(R.string.download_file_error);
// Create notification channel on Android O
Notifications.setup(this);
// Setup shortcuts
sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
sendBroadcast(new Intent(this, ClassMap.get(ShortcutReceiver.class)));
if (Download.checkNetworkStatus(this)) {
// Fire update check
@ -63,9 +66,9 @@ public class SplashActivity extends BaseActivity {
// Write back default values
Data.writeConfig();
mm.hasInit = true;
app.init = true;
Intent intent = new Intent(this, Data.classMap.get(MainActivity.class));
Intent intent = new Intent(this, ClassMap.get(MainActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
startActivity(intent);

View File

@ -15,8 +15,10 @@ import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.SuConnector;
@ -70,7 +72,7 @@ public class SuRequestActivity extends BaseActivity {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
PackageManager pm = getPackageManager();
mm.mDB.clearOutdated();
app.mDB.clearOutdated();
// Get policy
Intent intent = getIntent();
@ -84,7 +86,7 @@ public class SuRequestActivity extends BaseActivity {
};
Bundle bundle = connector.readSocketInput();
int uid = Integer.parseInt(bundle.getString("uid"));
policy = mm.mDB.getPolicy(uid);
policy = app.mDB.getPolicy(uid);
if (policy == null) {
policy = new Policy(uid, pm);
}
@ -212,7 +214,7 @@ public class SuRequestActivity extends BaseActivity {
policy.policy = action;
if (time >= 0) {
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
mm.mDB.updatePolicy(policy);
app.mDB.updatePolicy(policy);
}
handleAction();
}

View File

@ -13,10 +13,10 @@ import android.widget.Filter;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;

View File

@ -10,9 +10,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.superuser.Shell;
import java.util.List;

View File

@ -10,12 +10,12 @@ import android.widget.Switch;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.core.database.MagiskDB;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.MagiskDB;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.util.HashSet;

View File

@ -11,14 +11,14 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.container.Repo;
import com.topjohnwu.core.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.DownloadModule;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.components.MarkDownWindow;
import com.topjohnwu.magisk.utils.DownloadModule;
import java.util.ArrayList;
import java.util.List;

View File

@ -8,10 +8,10 @@ import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.core.container.SuLogEntry;
import com.topjohnwu.core.database.MagiskDB;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.database.MagiskDB;
import java.util.Collections;
import java.util.HashSet;

View File

@ -1,103 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
import org.json.JSONException;
import org.json.JSONObject;
public class CheckUpdates {
private static int getInt(JSONObject json, String name, int defValue) {
if (json == null)
return defValue;
try {
return json.getInt(name);
} catch (JSONException e) {
return defValue;
}
}
private static String getString(JSONObject json, String name, String defValue) {
if (json == null)
return defValue;
try {
return json.getString(name);
} catch (JSONException e) {
return defValue;
}
}
private static JSONObject getJson(JSONObject json, String name) {
try {
return json.getJSONObject(name);
} catch (JSONException e) {
return null;
}
}
public static void check(Runnable cb) {
String url;
switch (Data.updateChannel) {
case Const.Value.STABLE_CHANNEL:
url = Const.Url.STABLE_URL;
break;
case Const.Value.BETA_CHANNEL:
url = Const.Url.BETA_URL;
break;
case Const.Value.CUSTOM_CHANNEL:
url = Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
break;
default:
return;
}
Networking.get(url).getAsJSONObject(new UpdateListener(cb));
}
public static void check() {
check(null);
}
private static class UpdateListener implements ResponseListener<JSONObject> {
private Runnable cb;
UpdateListener(Runnable callback) {
cb = callback;
}
@Override
public void onResponse(JSONObject json) {
JSONObject magisk = getJson(json, "magisk");
Data.remoteMagiskVersionString = getString(magisk, "version", null);
Data.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
Data.magiskLink = getString(magisk, "link", null);
Data.magiskNoteLink = getString(magisk, "note", null);
Data.magiskMD5 = getString(magisk, "md5", null);
JSONObject manager = getJson(json, "app");
Data.remoteManagerVersionString = getString(manager, "version", null);
Data.remoteManagerVersionCode = getInt(manager, "versionCode", -1);
Data.managerLink = getString(manager, "link", null);
Data.managerNoteLink = getString(manager, "note", null);
JSONObject uninstaller = getJson(json, "uninstaller");
Data.uninstallerLink = getString(uninstaller, "link", null);
if (cb != null) {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
Notifications.managerUpdate();
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
Notifications.magiskUpdate();
}
cb.run();
}
Topic.publish(Topic.UPDATE_CHECK_DONE);
}
}
}

View File

@ -1,104 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.net.Uri;
import android.view.View;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class FlashZip extends ParallelTask<Void, Void, Integer> {
private Uri mUri;
private File mCachedFile;
private List<String> console, logs;
public FlashZip(Activity context, Uri uri, List<String> console, List<String> logs) {
super(context);
mUri = uri;
this.console = console;
this.logs = logs;
mCachedFile = new File(context.getCacheDir(), "install.zip");
}
private boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android", true);
return ShellUtils.fastCmdResult("grep -q '#MAGISK' " + new File(mCachedFile.getParentFile(), "updater-script"));
}
@Override
protected Integer doInBackground(Void... voids) {
MagiskManager mm = Data.MM();
try {
console.add("- Copying zip to temp directory");
mCachedFile.delete();
try (
InputStream in = mm.getContentResolver().openInputStream(mUri);
OutputStream out = new BufferedOutputStream(new FileOutputStream(mCachedFile))
) {
if (in == null) throw new FileNotFoundException();
InputStream buf= new BufferedInputStream(in);
ShellUtils.pump(buf, out);
} catch (FileNotFoundException e) {
console.add("! Invalid Uri");
throw e;
} catch (IOException e) {
console.add("! Cannot copy to cache");
throw e;
}
if (!unzipAndCheck()) return 0;
console.add("- Installing " + Utils.getNameFromUri(mm, mUri));
if (!Shell.su("cd " + mCachedFile.getParent(),
"BOOTMODE=true sh update-binary dummy 1 " + mCachedFile)
.to(console, logs)
.exec().isSuccess())
return -1;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
console.add("- All done!");
return 1;
}
// -1 = error, manual install; 0 = invalid zip; 1 = success
@Override
protected void onPostExecute(Integer result) {
FlashActivity activity = (FlashActivity) getActivity();
Shell.su("rm -rf " + mCachedFile.getParent(), "rm -rf " + Const.TMP_FOLDER_PATH).submit();
switch (result) {
case -1:
console.add("! Installation failed");
SnackbarMaker.showUri(getActivity(), mUri);
break;
case 0:
console.add("! This zip is not a Magisk Module!");
break;
case 1:
// Reload modules
Utils.loadModules();
break;
}
activity.reboot.setVisibility(result > 0 ? View.VISIBLE : View.GONE);
activity.buttonPanel.setVisibility(View.VISIBLE);
}
}

View File

@ -1,354 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.TarEntry;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import com.topjohnwu.net.DownloadProgressListener;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.internal.NOPList;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileInputStream;
import com.topjohnwu.superuser.io.SuFileOutputStream;
import com.topjohnwu.utils.SignBoot;
import org.kamranzafar.jtar.TarInputStream;
import org.kamranzafar.jtar.TarOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private static final int PATCH_MODE = 0;
public static final int DIRECT_MODE = 1;
private static final int FIX_ENV_MODE = 2;
public static final int SECOND_SLOT_MODE = 3;
private Uri bootUri;
private List<String> console, logs;
private String mBoot;
private int mode;
private File installDir;
private ProgressDialog dialog;
private MagiskManager mm;
public InstallMagisk(Activity context) {
super(context);
mm = Data.MM();
mode = FIX_ENV_MODE;
}
public InstallMagisk(Activity context, List<String> console, List<String> logs, int mode) {
this(context);
this.console = console;
this.logs = logs;
this.mode = mode;
}
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri boot) {
this(context, console, logs, PATCH_MODE);
bootUri = boot;
}
@Override
protected void onPreExecute() {
if (mode == FIX_ENV_MODE) {
Activity a = getActivity();
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
console = NOPList.getInstance();
}
}
class ProgressLog implements DownloadProgressListener {
private int prev = -1;
private int location;
@Override
public void onProgress(long bytesDownloaded, long totalBytes) {
if (prev < 0) {
location = console.size();
console.add("... 0%");
}
int curr = (int) (100 * bytesDownloaded / totalBytes);
if (prev != curr) {
prev = curr;
console.set(location, "... " + prev + "%");
}
}
}
private void extractFiles(String arch) throws IOException {
File zip = new File(mm.getFilesDir(), "magisk.zip");
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
console.add("- Downloading zip");
Networking.get(Data.magiskLink)
.setDownloadProgressListener(new ProgressLog())
.execForFile(zip);
} else {
console.add("- Existing zip found");
}
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(zip), (int) zip.length());
buf.mark((int) zip.length() + 1);
console.add("- Extracting files");
try (InputStream in = buf) {
ZipUtils.unzip(in, installDir, arch + "/", true);
in.reset();
ZipUtils.unzip(in, installDir, "common/", true);
in.reset();
ZipUtils.unzip(in, installDir, "chromeos/", false);
in.reset();
ZipUtils.unzip(in, installDir, "META-INF/com/google/android/update-binary", true);
} catch (IOException e) {
console.add("! Cannot unzip zip");
throw e;
}
Shell.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
installDir, installDir, installDir)).exec();
}
private boolean dumpBoot() {
console.add("- Copying image to cache");
// Copy boot image to local
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
OutputStream out = new FileOutputStream(mBoot)
) {
if (in == null)
throw new FileNotFoundException();
InputStream src;
if (Utils.getNameFromUri(mm, bootUri).endsWith(".tar")) {
// Extract boot.img from tar
TarInputStream tar = new TarInputStream(new BufferedInputStream(in));
org.kamranzafar.jtar.TarEntry entry;
while ((entry = tar.getNextEntry()) != null) {
if (entry.getName().equals("boot.img"))
break;
}
src = tar;
} else {
// Direct copy raw image
src = new BufferedInputStream(in);
}
ShellUtils.pump(src, out);
} catch (FileNotFoundException e) {
console.add("! Invalid Uri");
return false;
} catch (IOException e) {
console.add("! Copy failed");
return false;
}
return true;
}
private File patchBoot() throws IOException {
boolean isSigned;
try (InputStream in = new SuFileInputStream(mBoot)) {
isSigned = SignBoot.verifySignature(in, null);
if (isSigned) {
console.add("- Boot image is signed with AVB 1.0");
}
} catch (IOException e) {
console.add("! Unable to check signature");
throw e;
}
// Patch boot image
if (!Shell.sh("cd " + installDir, Utils.fmt(
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep boot_patch.sh %s",
Data.keepEnc, Data.keepVerity, mBoot))
.to(console, logs).exec().isSuccess())
return null;
Shell.Job job = Shell.sh("mv bin/busybox busybox",
"rm -rf magisk.apk bin boot.img update-binary",
"cd /");
File patched = new File(installDir, "new-boot.img");
if (isSigned) {
console.add("- Signing boot image with test keys");
File signed = new File(installDir, "signed.img");
try (InputStream in = new SuFileInputStream(patched);
OutputStream out = new BufferedOutputStream(new FileOutputStream(signed))
) {
SignBoot.doSignature("/boot", in, out, null, null);
}
job.add("mv -f " + signed + " " + patched);
}
job.exec();
return patched;
}
private boolean outputBoot(File patched) throws IOException {
switch (mode) {
case PATCH_MODE:
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + fmt);
dest.getParentFile().mkdirs();
OutputStream out;
switch (fmt) {
case ".img.tar":
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
break;
default:
case ".img":
out = new BufferedOutputStream(new FileOutputStream(dest));
break;
}
try (InputStream in = new SuFileInputStream(patched)) {
ShellUtils.pump(in, out);
out.close();
}
Shell.sh("rm -f " + patched).exec();
console.add("");
console.add("****************************");
console.add(" Patched image is placed in ");
console.add(" " + dest + " ");
console.add("****************************");
break;
case SECOND_SLOT_MODE:
case DIRECT_MODE:
if (!Shell.su(Utils.fmt("direct_install %s %s", installDir, mBoot))
.to(console, logs).exec().isSuccess())
return false;
if (!Data.keepVerity)
Shell.su("find_dtbo_image", "patch_dtbo_image").to(console, logs).exec();
break;
}
return true;
}
private void postOTA() {
SuFile bootctl = new SuFile(Const.MAGISK_PATH + "/.core/bootctl");
try (InputStream in = mm.getResources().openRawResource(R.raw.bootctl);
OutputStream out = new SuFileOutputStream(bootctl)) {
ShellUtils.pump(in, out);
Shell.su("post_ota " + bootctl.getParent()).exec();
console.add("***************************************");
console.add(" Next reboot will boot to second slot!");
console.add("***************************************");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected Boolean doInBackground(Void... voids) {
if (mode == FIX_ENV_MODE) {
installDir = new File("/data/adb/magisk");
Shell.su("rm -rf /data/adb/magisk/*").exec();
} else {
installDir = new File(
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
mm.createDeviceProtectedStorageContext() : mm)
.getFilesDir().getParent()
, "install");
Shell.sh("rm -rf " + installDir).exec();
installDir.mkdirs();
}
switch (mode) {
case PATCH_MODE:
mBoot = new File(installDir, "boot.img").getAbsolutePath();
if (!dumpBoot())
return false;
break;
case DIRECT_MODE:
console.add("- Detecting target image");
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
break;
case SECOND_SLOT_MODE:
String slot = ShellUtils.fastCmd("echo $SLOT");
String target = (TextUtils.equals(slot, "_a") ? "_b" : "_a");
console.add("- Target slot: " + target);
console.add("- Detecting target image");
mBoot = ShellUtils.fastCmd(
"SLOT=" + target,
"find_boot_image",
"SLOT=" + slot,
"echo \"$BOOTIMAGE\""
);
break;
case FIX_ENV_MODE:
mBoot = "";
break;
}
if (mBoot == null) {
console.add("! Unable to detect target image");
return false;
}
if (mode == DIRECT_MODE || mode == SECOND_SLOT_MODE)
console.add("- Target image: " + mBoot);
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
String arch = abis.contains("x86") ? "x86" : "arm";
console.add("- Device platform: " + Build.SUPPORTED_ABIS[0]);
try {
extractFiles(arch);
if (mode == FIX_ENV_MODE) {
Shell.su("fix_env").exec();
} else {
File patched = patchBoot();
if (patched == null)
return false;
if (!outputBoot(patched))
return false;
if (mode == SECOND_SLOT_MODE)
postOTA();
console.add("- All done!");
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (mode == FIX_ENV_MODE) {
dialog.dismiss();
Utils.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
} else {
// Running in FlashActivity
FlashActivity activity = (FlashActivity) getActivity();
if (!result) {
Shell.sh("rm -rf " + installDir).submit();
console.add("! Installation failed");
activity.reboot.setVisibility(View.GONE);
}
activity.buttonPanel.setVisibility(View.VISIBLE);
}
}
}

View File

@ -1,26 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.os.AsyncTask;
import java.lang.ref.WeakReference;
public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private WeakReference<Activity> weakActivity;
public ParallelTask() {}
public ParallelTask(Activity context) {
weakActivity = new WeakReference<>(context);
}
protected Activity getActivity() {
return weakActivity.get();
}
@SuppressWarnings("unchecked")
public void exec(Params... params) {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
}

View File

@ -1,51 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import java.io.File;
import dalvik.system.DexClassLoader;
public class SafetyNet {
public static final File EXT_APK =
new File(Data.MM().getFilesDir().getParent() + "/snet", "snet.apk");
private static void dyRun(Activity activity) throws Exception {
DexClassLoader loader = new DexClassLoader(EXT_APK.getPath(), EXT_APK.getParent(),
null, ISafetyNetHelper.class.getClassLoader());
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.Snet");
ISafetyNetHelper helper = (ISafetyNetHelper) clazz.getMethod("newHelper",
Class.class, String.class, Activity.class, Object.class)
.invoke(null, ISafetyNetHelper.class, EXT_APK.getPath(), activity,
(ISafetyNetHelper.Callback) code ->
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
if (helper.getVersion() < Const.SNET_EXT_VER)
throw new Exception();
helper.attest();
}
public static void check(Activity activity) {
try {
dyRun(activity);
} catch (Exception ignored) {
Shell.sh("rm -rf " + EXT_APK.getParent()).exec();
EXT_APK.getParentFile().mkdir();
Networking.get(Const.Url.SNET_URL).getAsFile(f -> {
try {
dyRun(activity);
} catch (Exception e) {
e.printStackTrace();
Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
}
}, EXT_APK);
}
}
}

View File

@ -1,167 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.database.Cursor;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.Request;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class UpdateRepos {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
private static final DateFormat dateFormat;
private MagiskManager mm;
private Set<String> cached;
private ExecutorService threadPool;
static {
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
public UpdateRepos() {
mm = Data.MM();
}
private void waitTasks() {
threadPool.shutdown();
while (true) {
try {
if (threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS))
break;
} catch (InterruptedException ignored) {}
}
}
private void loadJSON(JSONArray array) throws JSONException, ParseException {
for (int i = 0; i < array.length(); i++) {
JSONObject rawRepo = array.getJSONObject(i);
String id = rawRepo.getString("name");
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
threadPool.execute(() -> {
Repo repo = mm.repoDB.getRepo(id);
try {
if (repo == null)
repo = new Repo(id);
else
cached.remove(id);
repo.update(date);
mm.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
Logger.debug(e.getMessage());
mm.repoDB.removeRepo(id);
}
});
}
}
/* We sort repos by last push, which means that we only need to check whether the
* first page is updated to determine whether the online repo database is changed
*/
private boolean loadPage(int page) {
Request req = Networking.get(Utils.fmt(Const.Url.REPO_URL, page + 1));
if (page == 0) {
String etag = mm.prefs.getString(Const.Key.ETAG_KEY, null);
if (etag != null)
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
}
Request.Result<JSONArray> res = req.execForJSONArray();
// JSON not updated
if (res.getCode() == HttpURLConnection.HTTP_NOT_MODIFIED)
return false;
// Network error
if (res.getResult() == null) {
cached.clear();
return true;
}
// Current page is the last page
if (res.getResult().length() == 0)
return true;
try {
loadJSON(res.getResult());
} catch (JSONException | ParseException e) {
// Should not happen, but if exception occurs, page load fails
return false;
}
// Update ETAG
if (page == 0) {
String etag = res.getConnection().getHeaderField(Const.Key.ETAG_KEY);
if (etag != null) {
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
}
}
String links = res.getConnection().getHeaderField(Const.Key.LINK_KEY);
return links == null || !links.contains("next") || loadPage(page + 1);
}
private boolean loadPages() {
return loadPage(0);
}
private void fullReload() {
Cursor c = mm.repoDB.getRawCursor();
while (c.moveToNext()) {
Repo repo = new Repo(c);
threadPool.execute(() -> {
try {
repo.update();
mm.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
Logger.debug(e.getMessage());
mm.repoDB.removeRepo(repo);
}
});
}
waitTasks();
}
public void exec(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
if (loadPages()) {
waitTasks();
// The leftover cached means they are removed from online repo
mm.repoDB.removeRepo(cached);
} else if (force) {
fullReload();
}
Topic.publish(Topic.REPO_LOAD_DONE);
});
}
public void exec() {
exec(false);
}
}

View File

@ -1,5 +1,6 @@
package com.topjohnwu.magisk.components;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@ -8,13 +9,12 @@ import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.NoUIActivity;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -26,12 +26,12 @@ import androidx.core.content.ContextCompat;
public abstract class BaseActivity extends AppCompatActivity implements Topic.AutoSubscriber {
public static final String INTENT_PERM = "perm_dialog";
private static Runnable grantCallback;
protected static Runnable permissionGrantCallback;
static int[] EMPTY_INT_ARRAY = new int[0];
private ActivityResultListener activityResultListener;
public MagiskManager mm;
public App app;
@Override
public int[] getSubscribedTopics() {
@ -49,7 +49,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
Configuration config = base.getResources().getConfiguration();
config.setLocale(LocaleManager.locale);
applyOverrideConfiguration(config);
mm = Data.MM();
app = App.self;
}
@Override
@ -84,6 +84,14 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
}
}
public void runWithExternalRW(Runnable callback) {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, callback);
}
public void runWithPermission(String[] permissions, Runnable callback) {
runWithPermission(this, permissions, callback);
}
public static void runWithPermission(Context context, String[] permissions, Runnable callback) {
boolean granted = true;
for (String perm : permissions) {
@ -95,23 +103,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
callback.run();
} else {
// Passed in context should be an activity if not granted, need to show dialog!
permissionGrantCallback = callback;
if (!(context instanceof BaseActivity)) {
// Start NoUIActivity to show dialog
Intent intent = new Intent(context, Data.classMap.get(NoUIActivity.class));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(INTENT_PERM, permissions);
context.startActivity(intent);
} else {
if (context instanceof BaseActivity) {
grantCallback = callback;
ActivityCompat.requestPermissions((BaseActivity) context, permissions, 0);
}
}
}
public void runWithPermission(String[] permissions, Runnable callback) {
runWithPermission(this, permissions, callback);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (activityResultListener != null)
@ -132,13 +130,13 @@ public abstract class BaseActivity extends AppCompatActivity implements Topic.Au
grant = false;
}
if (grant) {
if (permissionGrantCallback != null) {
permissionGrantCallback.run();
if (grantCallback != null) {
grantCallback.run();
}
} else {
Toast.makeText(this, R.string.no_rw_storage, Toast.LENGTH_LONG).show();
}
permissionGrantCallback = null;
grantCallback = null;
}
public interface ActivityResultListener {

View File

@ -2,20 +2,19 @@ package com.topjohnwu.magisk.components;
import android.content.Intent;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.core.App;
import com.topjohnwu.core.utils.Topic;
import androidx.fragment.app.Fragment;
import butterknife.Unbinder;
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
public MagiskManager mm;
public App app;
protected Unbinder unbinder = null;
public BaseFragment() {
mm = Data.MM();
app = App.self;
}
@Override

View File

@ -1,9 +1,14 @@
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.app.ProgressDialog;
import android.widget.Toast;
import com.topjohnwu.core.tasks.MagiskInstaller;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.InstallMagisk;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import androidx.annotation.NonNull;
@ -14,7 +19,25 @@ public class EnvFixDialog extends CustomAlertDialog {
setTitle(R.string.env_fix_title);
setMessage(R.string.env_fix_msg);
setCancelable(true);
setPositiveButton(R.string.yes, (d, i) -> new InstallMagisk(activity).exec());
setPositiveButton(R.string.yes, (d, i) -> {
ProgressDialog pd = ProgressDialog.show(activity,
activity.getString(R.string.setup_title),
activity.getString(R.string.setup_msg));
new MagiskInstaller() {
@Override
protected boolean operations() {
installDir = new SuFile("/data/adb/magisk");
Shell.su("rm -rf /data/adb/magisk/*").exec();
return extractZip() && Shell.su("fix_env").exec().isSuccess();
}
@Override
protected void onResult(boolean success) {
pd.dismiss();
Utils.toast(success ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
}
}.exec();
});
setNegativeButton(R.string.no_thanks, null);
}
}

View File

@ -1,16 +1,16 @@
package com.topjohnwu.magisk.components;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.widget.Toast;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import java.io.File;
@ -33,7 +33,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
downloadOnly(activity);
break;
case 2:
intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
intent = new Intent(activity, ClassMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
activity.startActivity(intent);
break;
@ -48,13 +48,13 @@ class InstallMethodDialog extends AlertDialog.Builder {
private void patchBoot(BaseActivity a) {
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
a.runWithPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, () ->
a.runWithExternalRW(() ->
a.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == Activity.RESULT_OK && data != null) {
Intent i = new Intent(a, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
Intent i = new Intent(a, ClassMap.get(FlashActivity.class))
.setData(data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
a.startActivity(i);
}
@ -63,7 +63,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
}
private void downloadOnly(BaseActivity a) {
a.runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
a.runWithExternalRW(() -> {
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
File zip = new File(Const.EXTERNAL_PATH, filename);
@ -86,7 +86,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
.setMessage(R.string.install_inactive_slot_msg)
.setCancelable(true)
.setPositiveButton(R.string.yes, (d, i) -> {
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
Intent intent = new Intent(activity, ClassMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(intent);
})

View File

@ -3,11 +3,10 @@ package com.topjohnwu.magisk.components;
import android.net.Uri;
import android.text.TextUtils;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.AppUtils;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@ -15,35 +14,34 @@ import java.util.ArrayList;
import java.util.List;
public class MagiskInstallDialog extends CustomAlertDialog {
public MagiskInstallDialog(BaseActivity activity) {
super(activity);
MagiskManager mm = Data.MM();
public MagiskInstallDialog(BaseActivity a) {
super(a);
String filename = Utils.fmt("Magisk-v%s(%d).zip",
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)));
setMessage(mm.getString(R.string.repo_install_msg, filename));
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.magisk)));
setMessage(a.getString(R.string.repo_install_msg, filename));
setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> {
List<String> options = new ArrayList<>();
options.add(mm.getString(R.string.download_zip_only));
options.add(mm.getString(R.string.patch_boot_file));
options.add(a.getString(R.string.download_zip_only));
options.add(a.getString(R.string.patch_boot_file));
if (Shell.rootAccess()) {
options.add(mm.getString(R.string.direct_install));
options.add(a.getString(R.string.direct_install));
String s = ShellUtils.fastCmd("grep_prop ro.build.ab_update");
if (!s.isEmpty() && Boolean.parseBoolean(s)) {
options.add(mm.getString(R.string.install_inactive_slot));
options.add(a.getString(R.string.install_inactive_slot));
}
}
new InstallMethodDialog(activity, options).show();
new InstallMethodDialog(a, options).show();
});
setNegativeButton(R.string.no_thanks, null);
if (!TextUtils.isEmpty(Data.magiskNoteLink)) {
setNeutralButton(R.string.release_notes, (d, i) -> {
if (Data.magiskNoteLink.contains("forum.xda-developers")) {
// Open forum links in browser
Utils.openLink(activity, Uri.parse(Data.magiskNoteLink));
AppUtils.openLink(a, Uri.parse(Data.magiskNoteLink));
} else {
new MarkDownWindow(activity, null, Data.magiskNoteLink).exec();
new MarkDownWindow(a, null, Data.magiskNoteLink).exec();
}
});
}

View File

@ -2,30 +2,27 @@ package com.topjohnwu.magisk.components;
import android.text.TextUtils;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.DownloadApp;
import androidx.annotation.NonNull;
public class ManagerInstallDialog extends CustomAlertDialog {
public ManagerInstallDialog(@NonNull BaseActivity activity) {
super(activity);
MagiskManager mm = Data.MM();
public ManagerInstallDialog(@NonNull BaseActivity a) {
super(a);
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)));
setMessage(mm.getString(R.string.repo_install_msg, name));
setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name)));
setMessage(a.getString(R.string.repo_install_msg, name));
setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> DlInstallManager.upgrade(name));
setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name));
setNegativeButton(R.string.no_thanks, null);
if (!TextUtils.isEmpty(Data.managerNoteLink)) {
setNeutralButton(R.string.app_changelog, (d, i) ->
new MarkDownWindow(activity, null, Data.managerNoteLink).exec());
new MarkDownWindow(a, null, Data.managerNoteLink).exec());
}
}
}

View File

@ -1,12 +1,13 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.webkit.WebView;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.ParallelTask;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.ShellUtils;
import org.commonmark.node.Node;
@ -40,7 +41,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
@Override
protected String doInBackground(Void... voids) {
MagiskManager mm = Data.MM();
App app = App.self;
String md;
if (mUrl != null) {
md = Utils.dlString(mUrl);
@ -55,14 +56,11 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
}
}
String css;
try (
InputStream in = mm.getResources().openRawResource(
Data.isDarkTheme ? R.raw.dark : R.raw.light);
ByteArrayOutputStream out = new ByteArrayOutputStream()
) {
try (InputStream in = app.getResources()
.openRawResource(Data.isDarkTheme ? R.raw.dark : R.raw.light);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
ShellUtils.pump(in, out);
css = out.toString();
in.close();
} catch (IOException e) {
e.printStackTrace();
return "";

View File

@ -7,13 +7,14 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.receivers.GeneralReceiver;
import com.topjohnwu.magisk.utils.Utils;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@ -36,74 +37,74 @@ public class Notifications {
}
public static void magiskUpdate() {
MagiskManager mm = Data.MM();
App app = App.self;
Intent intent = new Intent(mm, Data.classMap.get(SplashActivity.class));
Intent intent = new Intent(app, ClassMap.get(SplashActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
stackBuilder.addParentStack(Data.classMap.get(SplashActivity.class));
TaskStackBuilder stackBuilder = TaskStackBuilder.create(app);
stackBuilder.addParentStack(ClassMap.get(SplashActivity.class));
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.magisk_update_title))
.setContentText(mm.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
.setContentTitle(app.getString(R.string.magisk_update_title))
.setContentText(app.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void managerUpdate() {
MagiskManager mm = Data.MM();
App app = App.self;
String name = Utils.fmt("MagiskManager v%s(%d)",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class));
Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class));
intent.setAction(Const.Key.BROADCAST_MANAGER_UPDATE);
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
intent.putExtra(Const.Key.INTENT_SET_NAME, name);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
PendingIntent pendingIntent = PendingIntent.getBroadcast(app,
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.manager_update_title))
.setContentText(mm.getString(R.string.manager_download_install))
.setContentTitle(app.getString(R.string.manager_update_title))
.setContentText(app.getString(R.string.manager_download_install))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build());
}
public static void dtboPatched() {
MagiskManager mm = Data.MM();
App app = App.self;
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class))
Intent intent = new Intent(app, ClassMap.get(GeneralReceiver.class))
.setAction(Const.Key.BROADCAST_REBOOT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
PendingIntent pendingIntent = PendingIntent.getBroadcast(app,
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
builder.setSmallIcon(R.drawable.ic_magisk_outline)
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
.setContentTitle(app.getString(R.string.dtbo_patched_title))
.setContentText(app.getString(R.string.dtbo_patched_reboot))
.setVibrate(new long[]{0, 100, 100, 100})
.addAction(R.drawable.ic_refresh, mm.getString(R.string.reboot), pendingIntent);
.addAction(R.drawable.ic_refresh, app.getString(R.string.reboot), pendingIntent);
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
}
public static NotificationCompat.Builder progress(String title) {
MagiskManager mm = Data.MM();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.PROGRESS_NOTIFICATION_CHANNEL);
App app = App.self;
NotificationCompat.Builder builder = new NotificationCompat.Builder(app, Const.ID.PROGRESS_NOTIFICATION_CHANNEL);
builder.setPriority(NotificationCompat.PRIORITY_LOW)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setContentTitle(title)

View File

@ -2,10 +2,9 @@ package com.topjohnwu.magisk.components;
import android.widget.Toast;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.DownloadProgressListener;
import androidx.core.app.NotificationCompat;
@ -17,12 +16,11 @@ public class ProgressNotification implements DownloadProgressListener {
private long prevTime;
public ProgressNotification(String title) {
MagiskManager mm = Data.MM();
mgr = NotificationManagerCompat.from(mm);
mgr = NotificationManagerCompat.from(App.self);
builder = Notifications.progress(title);
prevTime = System.currentTimeMillis();
update();
Utils.toast(mm.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
Utils.toast(App.self.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
}
@Override
@ -47,7 +45,7 @@ public class ProgressNotification implements DownloadProgressListener {
public void dlDone() {
builder.setProgress(0, 0, false)
.setContentText(Data.MM().getString(R.string.download_complete))
.setContentText(App.self.getString(R.string.download_complete))
.setSmallIcon(R.drawable.ic_check_circle)
.setOngoing(false);
update();
@ -55,7 +53,7 @@ public class ProgressNotification implements DownloadProgressListener {
public void dlFail() {
builder.setProgress(0, 0, false)
.setContentText(Data.MM().getString(R.string.download_file_error))
.setContentText(App.self.getString(R.string.download_file_error))
.setSmallIcon(R.drawable.ic_cancel)
.setOngoing(false);
update();

View File

@ -6,8 +6,8 @@ import android.view.View;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.StringRes;

View File

@ -7,11 +7,12 @@ import android.net.Uri;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
@ -47,7 +48,7 @@ public class UninstallDialog extends CustomAlertDialog {
.setErrorHandler(((conn, e) -> progress.dlFail()))
.getAsFile(f -> {
progress.dismiss();
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
Intent intent = new Intent(activity, ClassMap.get(FlashActivity.class))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setData(Uri.fromFile(f))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);

View File

@ -1,127 +0,0 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
import java.util.List;
import androidx.annotation.NonNull;
public abstract class BaseModule implements Comparable<BaseModule> {
private String mId, mName, mVersion, mAuthor, mDescription;
private int mVersionCode = -1, minMagiskVersion = -1;
protected BaseModule() {
mId = mName = mVersion = mAuthor = mDescription = "";
}
protected BaseModule(Cursor c) {
mId = nonNull(c.getString(c.getColumnIndex("id")));
mName = nonNull(c.getString(c.getColumnIndex("name")));
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
mAuthor = nonNull(c.getString(c.getColumnIndex("author")));
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk"));
}
private String nonNull(String s) {
return s == null ? "" : s;
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("id", mId);
values.put("name", mName);
values.put("version", mVersion);
values.put("versionCode", mVersionCode);
values.put("author", mAuthor);
values.put("description", mDescription);
values.put("minMagisk", minMagiskVersion);
return values;
}
protected void parseProps(List<String> props) { parseProps(props.toArray(new String[0])); }
protected void parseProps(String[] props) throws NumberFormatException {
for (String line : props) {
String[] prop = line.split("=", 2);
if (prop.length != 2)
continue;
String key = prop[0].trim();
String value = prop[1].trim();
if (key.isEmpty() || key.charAt(0) == '#')
continue;
switch (key) {
case "id":
mId = value;
break;
case "name":
mName = value;
break;
case "version":
mVersion = value;
break;
case "versionCode":
mVersionCode = Integer.parseInt(value);
break;
case "author":
mAuthor = value;
break;
case "description":
mDescription = value;
break;
case "minMagisk":
case "template":
minMagiskVersion = Integer.parseInt(value);
break;
default:
break;
}
}
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
public String getVersion() {
return mVersion;
}
public String getAuthor() {
return mAuthor;
}
public String getId() {
return mId;
}
public void setId(String id) {
mId = id;
}
public String getDescription() {
return mDescription;
}
public int getVersionCode() {
return mVersionCode;
}
public int getMinMagiskVersion() {
return minMagiskVersion;
}
@Override
public int compareTo(@NonNull BaseModule module) {
return this.getName().toLowerCase().compareTo(module.getName().toLowerCase());
}
}

View File

@ -1,63 +0,0 @@
package com.topjohnwu.magisk.container;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
public class Module extends BaseModule {
private SuFile mRemoveFile, mDisableFile, mUpdateFile;
private boolean mEnable, mRemove, mUpdated;
public Module(String path) {
try {
parseProps(Shell.su("dos2unix < " + path + "/module.prop").exec().getOut());
} catch (NumberFormatException ignored) {}
mRemoveFile = new SuFile(path, "remove");
mDisableFile = new SuFile(path, "disable");
mUpdateFile = new SuFile(path, "update");
if (getId().isEmpty()) {
int sep = path.lastIndexOf('/');
setId(path.substring(sep + 1));
}
if (getName().isEmpty()) {
setName(getId());
}
mEnable = !mDisableFile.exists();
mRemove = mRemoveFile.exists();
mUpdated = mUpdateFile.exists();
}
public void createDisableFile() {
mEnable = !mDisableFile.createNewFile();
}
public void removeDisableFile() {
mEnable = mDisableFile.delete();
}
public boolean isEnabled() {
return mEnable;
}
public void createRemoveFile() {
mRemove = mRemoveFile.createNewFile();
}
public void deleteRemoveFile() {
mRemove = !mRemoveFile.delete();
}
public boolean willBeRemoved() {
return mRemove;
}
public boolean isUpdated() {
return mUpdated;
}
}

View File

@ -1,61 +0,0 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.NonNull;
public class Policy implements Comparable<Policy>{
public static final int INTERACTIVE = 0;
public static final int DENY = 1;
public static final int ALLOW = 2;
public int uid, policy = INTERACTIVE;
public long until;
public boolean logging = true, notification = true;
public String packageName, appName;
public ApplicationInfo info;
public Policy(int uid, PackageManager pm) throws PackageManager.NameNotFoundException {
String[] pkgs = pm.getPackagesForUid(uid);
if (pkgs == null || pkgs.length == 0)
throw new PackageManager.NameNotFoundException();
this.uid = uid;
packageName = pkgs[0];
info = pm.getApplicationInfo(packageName, 0);
appName = Utils.getAppLabel(info, pm);
}
public Policy(ContentValues values, PackageManager pm) throws PackageManager.NameNotFoundException {
uid = values.getAsInteger("uid");
packageName = values.getAsString("package_name");
policy = values.getAsInteger("policy");
until = values.getAsInteger("until");
logging = values.getAsInteger("logging") != 0;
notification = values.getAsInteger("notification") != 0;
info = pm.getApplicationInfo(packageName, 0);
if (info.uid != uid)
throw new PackageManager.NameNotFoundException();
appName = info.loadLabel(pm).toString();
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("uid", uid);
values.put("package_name", packageName);
values.put("policy", policy);
values.put("until", until);
values.put("logging", logging ? 1 : 0);
values.put("notification", notification ? 1 : 0);
return values;
}
@Override
public int compareTo(@NonNull Policy policy) {
return appName.toLowerCase().compareTo(policy.appName.toLowerCase());
}
}

View File

@ -1,84 +0,0 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import java.text.DateFormat;
import java.util.Date;
public class Repo extends BaseModule {
private Date mLastUpdate;
public Repo(String id) {
setId(id);
}
public Repo(Cursor c) {
super(c);
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
}
public void update() throws IllegalRepoException {
String props[] = Utils.dlString(getPropUrl()).split("\\n");
try {
parseProps(props);
} catch (NumberFormatException e) {
throw new IllegalRepoException("Repo [" + getId() + "] parse error: " + e.getMessage());
}
if (getVersionCode() < 0) {
throw new IllegalRepoException("Repo [" + getId() + "] does not contain versionCode");
}
if (getMinMagiskVersion() < Const.MIN_MODULE_VER) {
Logger.debug("Repo [" + getId() + "] is outdated");
}
}
public void update(Date lastUpdate) throws IllegalRepoException {
mLastUpdate = lastUpdate;
update();
}
@Override
public ContentValues getContentValues() {
ContentValues values = super.getContentValues();
values.put("last_update", mLastUpdate.getTime());
return values;
}
public String getZipUrl() {
return String.format(Const.Url.ZIP_URL, getId());
}
public String getPropUrl() {
return String.format(Const.Url.FILE_URL, getId(), "module.prop");
}
public String getDetailUrl() {
return String.format(Const.Url.FILE_URL, getId(), "README.md");
}
public String getLastUpdateString() {
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(mLastUpdate);
}
public Date getLastUpdate() {
return mLastUpdate;
}
public String getDownloadFilename() {
return Download.getLegalFilename(getName() + "-" + getVersion() + ".zip");
}
public class IllegalRepoException extends Exception {
IllegalRepoException(String message) {
super(message);
}
}
}

View File

@ -1,56 +0,0 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import com.topjohnwu.magisk.utils.LocaleManager;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SuLogEntry {
public int fromUid, toUid, fromPid;
public String packageName, appName, command;
public boolean action;
public Date date;
public SuLogEntry(Policy policy) {
fromUid = policy.uid;
packageName = policy.packageName;
appName = policy.appName;
action = policy.policy == Policy.ALLOW;
}
public SuLogEntry(ContentValues values) {
fromUid = values.getAsInteger("from_uid");
packageName = values.getAsString("package_name");
appName = values.getAsString("app_name");
fromPid = values.getAsInteger("from_pid");
command = values.getAsString("command");
toUid = values.getAsInteger("to_uid");
action = values.getAsInteger("action") != 0;
date = new Date(values.getAsLong("time"));
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("from_uid", fromUid);
values.put("package_name", packageName);
values.put("app_name", appName);
values.put("from_pid", fromPid);
values.put("command", command);
values.put("to_uid", toUid);
values.put("action", action ? 1 : 0);
values.put("time", date.getTime());
return values;
}
public String getDateString() {
return DateFormat.getDateInstance(DateFormat.MEDIUM, LocaleManager.locale).format(date);
}
public String getTimeString() {
return new SimpleDateFormat("h:mm a", LocaleManager.locale).format(date);
}
}

View File

@ -1,64 +0,0 @@
package com.topjohnwu.magisk.container;
import org.kamranzafar.jtar.TarHeader;
import java.io.File;
import java.util.Arrays;
public class TarEntry extends org.kamranzafar.jtar.TarEntry {
public TarEntry(File file, String entryName) {
super(file, entryName);
}
/*
* Workaround missing java.nio.file.attribute.PosixFilePermission
* Simply just assign a default permission to the file
* */
@Override
public void extractTarHeader(String entryName) {
int permissions = file.isDirectory() ? 000755 : 000644;
header = TarHeader.createHeader(entryName, file.length(), file.lastModified() / 1000, file.isDirectory(), permissions);
header.userName = new StringBuffer("");
header.groupName = header.userName;
}
/*
* Rewrite the header to GNU format
* */
@Override
public void writeEntryHeader(byte[] outbuf) {
super.writeEntryHeader(outbuf);
System.arraycopy("ustar \0".getBytes(), 0, outbuf, 257, TarHeader.USTAR_MAGICLEN);
getOctalBytes(header.mode, outbuf, 100, TarHeader.MODELEN);
getOctalBytes(header.userId, outbuf, 108, TarHeader.UIDLEN);
getOctalBytes(header.groupId, outbuf, 116, TarHeader.GIDLEN);
getOctalBytes(header.size, outbuf, 124, TarHeader.SIZELEN);
getOctalBytes(header.modTime, outbuf, 136, TarHeader.MODTIMELEN);
Arrays.fill(outbuf, 148, 148 + TarHeader.CHKSUMLEN, (byte) ' ');
Arrays.fill(outbuf, 329, 329 + TarHeader.USTAR_DEVLEN, (byte) '\0');
Arrays.fill(outbuf, 337, 337 + TarHeader.USTAR_DEVLEN, (byte) '\0');
// Recalculate checksum
getOctalBytes(computeCheckSum(outbuf), outbuf, 148, TarHeader.CHKSUMLEN);
}
/*
* Proper octal to ASCII conversion
* */
private void getOctalBytes(long value, byte[] buf, int offset, int length) {
int idx = length - 1;
buf[offset + idx] = 0;
--idx;
for (long val = value; idx >= 0; --idx) {
buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7));
val = val >> 3;
}
}
}

View File

@ -1,43 +0,0 @@
package com.topjohnwu.magisk.container;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
public class ValueSortedMap<K, V extends Comparable<? super V>> extends HashMap<K, V> {
private List<V> sorted = new ArrayList<>();
@NonNull
@Override
public Collection<V> values() {
if (sorted.isEmpty()) {
sorted.addAll(super.values());
Collections.sort(sorted);
}
return sorted;
}
@Override
public V put(K key, V value) {
sorted.clear();
return super.put(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
sorted.clear();
super.putAll(m);
}
@Override
public V remove(Object key) {
sorted.clear();
return super.remove(key);
}
}

View File

@ -1,186 +0,0 @@
package com.topjohnwu.magisk.database;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class MagiskDB {
private static final String POLICY_TABLE = "policies";
private static final String LOG_TABLE = "logs";
private static final String SETTINGS_TABLE = "settings";
private static final String STRINGS_TABLE = "strings";
private PackageManager pm;
public MagiskDB(Context context) {
pm = context.getPackageManager();
}
public void deletePolicy(Policy policy) {
deletePolicy(policy.uid);
}
private List<String> rawSQL(String fmt, Object... args) {
return Shell.su("magisk --sqlite '" + Utils.fmt(fmt, args) + "'").exec().getOut();
}
private List<ContentValues> SQL(String fmt, Object... args) {
List<ContentValues> list = new ArrayList<>();
for (String raw : rawSQL(fmt, args)) {
ContentValues values = new ContentValues();
String[] cols = raw.split("\\|");
for (String col : cols) {
String[] pair = col.split("=", 2);
if (pair.length != 2)
continue;
values.put(pair[0], pair[1]);
}
list.add(values);
}
return list;
}
private String toSQL(ContentValues values) {
StringBuilder keys = new StringBuilder(), vals = new StringBuilder();
keys.append('(');
vals.append("VALUES(");
boolean first = true;
for (Map.Entry<String, Object> entry : values.valueSet()) {
if (!first) {
keys.append(',');
vals.append(',');
} else {
first = false;
}
keys.append(entry.getKey());
vals.append('"');
vals.append(entry.getValue());
vals.append('"');
}
keys.append(')');
vals.append(')');
keys.append(vals);
return keys.toString();
}
public void clearOutdated() {
rawSQL(
"DELETE FROM %s WHERE until > 0 AND until < %d;" +
"DELETE FROM %s WHERE time < %d",
POLICY_TABLE, System.currentTimeMillis() / 1000,
LOG_TABLE, System.currentTimeMillis() - Data.suLogTimeout * 86400000
);
}
public void deletePolicy(String pkg) {
rawSQL("DELETE FROM %s WHERE package_name=\"%s\"", POLICY_TABLE, pkg);
}
public void deletePolicy(int uid) {
rawSQL("DELETE FROM %s WHERE uid=%d", POLICY_TABLE, uid);
}
public Policy getPolicy(int uid) {
List<ContentValues> res =
SQL("SELECT * FROM %s WHERE uid=%d", POLICY_TABLE, uid);
if (!res.isEmpty()) {
try {
return new Policy(res.get(0), pm);
} catch (PackageManager.NameNotFoundException e) {
deletePolicy(uid);
}
}
return null;
}
public void updatePolicy(Policy policy) {
rawSQL("REPLACE INTO %s %s", POLICY_TABLE, toSQL(policy.getContentValues()));
}
public List<Policy> getPolicyList() {
List<Policy> list = new ArrayList<>();
for (ContentValues values : SQL("SELECT * FROM %s WHERE uid/100000=%d", POLICY_TABLE, Const.USER_ID)) {
try {
list.add(new Policy(values, pm));
} catch (PackageManager.NameNotFoundException e) {
deletePolicy(values.getAsInteger("uid"));
}
}
Collections.sort(list);
return list;
}
public List<List<SuLogEntry>> getLogs() {
List<List<SuLogEntry>> ret = new ArrayList<>();
List<SuLogEntry> list = null;
String dateString = null, newString;
for (ContentValues values : SQL("SELECT * FROM %s ORDER BY time DESC", LOG_TABLE)) {
Date date = new Date(values.getAsLong("time"));
newString = DateFormat.getDateInstance(DateFormat.MEDIUM, LocaleManager.locale).format(date);
if (!TextUtils.equals(dateString, newString)) {
dateString = newString;
list = new ArrayList<>();
ret.add(list);
}
list.add(new SuLogEntry(values));
}
return ret;
}
public void addLog(SuLogEntry log) {
rawSQL("INSERT INTO %s %s", LOG_TABLE, toSQL(log.getContentValues()));
}
public void clearLogs() {
rawSQL("DELETE FROM %s", LOG_TABLE);
}
public void setSettings(String key, int value) {
ContentValues data = new ContentValues();
data.put("key", key);
data.put("value", value);
rawSQL("REPLACE INTO %s %s", SETTINGS_TABLE, toSQL(data));
}
public int getSettings(String key, int defaultValue) {
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", SETTINGS_TABLE, key);
if (res.isEmpty())
return defaultValue;
return res.get(0).getAsInteger("value");
}
public void setStrings(String key, String value) {
if (value == null) {
rawSQL("DELETE FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
return;
}
ContentValues data = new ContentValues();
data.put("key", key);
data.put("value", value);
rawSQL("REPLACE INTO %s %s", STRINGS_TABLE, toSQL(data));
}
public String getStrings(String key, String defaultValue) {
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
if (res.isEmpty())
return defaultValue;
return res.get(0).getAsString("value");
}
}

View File

@ -1,136 +0,0 @@
package com.topjohnwu.magisk.database;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.container.Repo;
import java.util.HashSet;
import java.util.Set;
public class RepoDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VER = 4;
private static final String TABLE_NAME = "repos";
private SQLiteDatabase mDb;
private MagiskManager mm;
private ReposAdapter adapter;
public RepoDatabaseHelper(Context context) {
super(context, "repo.db", null, DATABASE_VER);
mm = Data.MM();
mDb = getWritableDatabase();
// Remove outdated repos
mDb.delete(TABLE_NAME, "minMagisk<?", new String[] { String.valueOf(Const.MIN_MODULE_VER) });
}
@Override
public void onCreate(SQLiteDatabase db) {
onUpgrade(db, 0, DATABASE_VER);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion != newVersion) {
// Nuke old DB and create new table
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
}
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, 0, DATABASE_VER);
}
public void clearRepo() {
mDb.delete(TABLE_NAME, null, null);
notifyAdapter();
}
public void removeRepo(String id) {
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
notifyAdapter();
}
public void removeRepo(Repo repo) {
removeRepo(repo.getId());
}
public void removeRepo(Iterable<String> list) {
for (String id : list) {
if (id == null) continue;
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
}
notifyAdapter();
}
public void addRepo(Repo repo) {
mDb.replace(TABLE_NAME, null, repo.getContentValues());
notifyAdapter();
}
public Repo getRepo(String id) {
try (Cursor c = mDb.query(TABLE_NAME, null, "id=?", new String[] { id }, null, null, null)) {
if (c.moveToNext()) {
return new Repo(c);
}
}
return null;
}
public Cursor getRawCursor() {
return mDb.query(TABLE_NAME, null, null, null, null, null, null);
}
public Cursor getRepoCursor() {
String orderBy = null;
switch (Data.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<=? AND minMagisk>=?",
new String[] { String.valueOf(Data.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER) },
null, null, orderBy);
}
public Set<String> getRepoIDSet() {
HashSet<String> set = new HashSet<>(300);
try (Cursor c = mDb.query(TABLE_NAME, null, null, null, null, null, null)) {
while (c.moveToNext()) {
set.add(c.getString(c.getColumnIndex("id")));
}
}
return set;
}
public void registerAdapter(ReposAdapter a) {
adapter = a;
}
public void unregisterAdapter() {
adapter = null;
}
private void notifyAdapter() {
if (adapter != null) {
Data.mainHandler.post(adapter::notifyDBChanged);
}
}
}

View File

@ -15,13 +15,15 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.core.tasks.SafetyNet;
import com.topjohnwu.core.utils.ISafetyNetHelper;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MainActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.SafetyNet;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.CustomAlertDialog;
@ -31,8 +33,6 @@ import com.topjohnwu.magisk.components.MagiskInstallDialog;
import com.topjohnwu.magisk.components.ManagerInstallDialog;
import com.topjohnwu.magisk.components.UninstallDialog;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@ -119,7 +119,7 @@ public class MagiskFragment extends BaseFragment
return;
}
((NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
((NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
new MagiskInstallDialog((BaseActivity) getActivity()).show();
}
@ -169,7 +169,7 @@ public class MagiskFragment extends BaseFragment
shownDialog = false;
// Trigger state check
if (Download.checkNetworkStatus(mm)) {
if (Download.checkNetworkStatus(app)) {
CheckUpdates.check();
} else {
mSwipeRefreshLayout.setRefreshing(false);
@ -199,7 +199,7 @@ public class MagiskFragment extends BaseFragment
}
private boolean hasGms() {
PackageManager pm = mm.getPackageManager();
PackageManager pm = app.getPackageManager();
PackageInfo info;
try {
info = pm.getPackageInfo("com.google.android.gms", 0);
@ -212,13 +212,13 @@ public class MagiskFragment extends BaseFragment
private void updateUI() {
((MainActivity) requireActivity()).checkHideSection();
boolean hasNetwork = Download.checkNetworkStatus(mm);
boolean hasNetwork = Download.checkNetworkStatus(app);
boolean hasRoot = Shell.rootAccess();
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
uninstallButton.setVisibility(hasRoot ? View.VISIBLE : View.GONE);
coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
coreOnlyNotice.setVisibility(app.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
int image, color;

View File

@ -8,10 +8,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.SearchView;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.utils.Topic;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View File

@ -15,11 +15,11 @@ import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.io.File;

View File

@ -12,15 +12,15 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@ -94,7 +94,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
Intent intent = new Intent(getActivity(), Data.classMap.get(FlashActivity.class));
Intent intent = new Intent(getActivity(), ClassMap.get(FlashActivity.class));
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
startActivity(intent);
}

View File

@ -11,14 +11,14 @@ import android.view.ViewGroup;
import android.widget.SearchView;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Module;
import com.topjohnwu.core.tasks.UpdateRepos;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.asyncs.UpdateRepos;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.utils.Topic;
import java.util.Map;
@ -70,8 +70,8 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public void onPublish(int topic, Object[] result) {
if (topic == Topic.MODULE_LOAD_DONE) {
adapter = new ReposAdapter(mm.repoDB, (Map<String, Module>) result[0]);
mm.repoDB.registerAdapter(adapter);
adapter = new ReposAdapter(app.repoDB, (Map<String, Module>) result[0]);
app.repoDB.registerAdapterCallback(adapter::notifyDBChanged);
recyclerView.setAdapter(adapter);
recyclerView.setVisibility(View.VISIBLE);
emptyRv.setVisibility(View.GONE);
@ -108,7 +108,7 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
.setTitle(R.string.sorting_order)
.setSingleChoiceItems(R.array.sorting_orders, Data.repoOrder, (d, which) -> {
Data.repoOrder = which;
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, Data.repoOrder).apply();
app.prefs.edit().putInt(Const.Key.REPO_ORDER, Data.repoOrder).apply();
adapter.notifyDBChanged();
d.dismiss();
}).show();
@ -119,6 +119,6 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public void onDestroyView() {
super.onDestroyView();
mm.repoDB.unregisterAdapter();
app.repoDB.unregisterAdapterCallback();
}
}

View File

@ -10,19 +10,20 @@ import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.core.utils.LocaleManager;
import com.topjohnwu.core.utils.Topic;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.AppUtils;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.DownloadApp;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.PatchAPK;
import com.topjohnwu.superuser.Shell;
import java.io.IOException;
@ -43,7 +44,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener,
Topic.Subscriber, Topic.AutoSubscriber {
private MagiskManager mm;
private App app = App.self;
private ListPreference updateChannel, autoRes, suNotification,
requestTimeout, rootConfig, multiuserConfig, nsConfig;
@ -52,10 +53,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
private boolean showSuperuser;
private void prefsSync() {
rootState = mm.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
namespaceState = mm.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
rootState = app.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
namespaceState = app.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
showSuperuser = Utils.showSuperUser();
mm.prefs.edit()
app.prefs.edit()
.putString(Const.Key.ROOT_ACCESS, String.valueOf(rootState))
.putString(Const.Key.SU_MNT_NS, String.valueOf(namespaceState))
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(Data.multiuserState))
@ -65,7 +66,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
mm = Data.MM();
prefsSync();
setPreferencesFromResource(R.xml.app_settings, rootKey);
@ -82,12 +82,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
});
Preference restoreManager = findPreference("restore");
restoreManager.setOnPreferenceClickListener(pref -> {
DlInstallManager.restore();
DownloadApp.restore();
return true;
});
findPreference("clear").setOnPreferenceClickListener(pref -> {
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
mm.repoDB.clearRepo();
app.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
app.repoDB.clearRepo();
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
return true;
});
@ -114,17 +114,17 @@ public class SettingsFragment extends PreferenceFragmentCompat
if (channel == Const.Value.CUSTOM_CHANNEL) {
View v = LayoutInflater.from(requireActivity()).inflate(R.layout.custom_channel_dialog, null);
EditText url = v.findViewById(R.id.custom_url);
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
url.setText(app.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
new AlertDialog.Builder(requireActivity())
.setTitle(R.string.settings_update_custom)
.setView(v)
.setPositiveButton(R.string.ok, (d, i) ->
mm.prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
app.prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
url.getText().toString()).apply())
.setNegativeButton(R.string.close, (d, i) ->
mm.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
app.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
.setOnCancelListener(d ->
mm.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
app.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
.show();
}
return true;
@ -152,10 +152,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
if (Shell.rootAccess() && Const.USER_ID == 0) {
if (mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
if (app.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
generalCatagory.removePreference(restoreManager);
} else {
if (!Download.checkNetworkStatus(mm))
if (!Download.checkNetworkStatus(app))
generalCatagory.removePreference(restoreManager);
generalCatagory.removePreference(hideManager);
}
@ -191,7 +191,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mm.prefs.registerOnSharedPreferenceChangeListener(this);
app.prefs.registerOnSharedPreferenceChangeListener(this);
Topic.subscribe(this);
requireActivity().setTitle(R.string.settings);
return super.onCreateView(inflater, container, savedInstanceState);
@ -199,7 +199,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
@Override
public void onDestroyView() {
mm.prefs.unregisterOnSharedPreferenceChangeListener(this);
app.prefs.unregisterOnSharedPreferenceChangeListener(this);
Topic.unsubscribe(this);
super.onDestroyView();
}
@ -210,7 +210,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
case Const.Key.ROOT_ACCESS:
case Const.Key.SU_MULTIUSER_MODE:
case Const.Key.SU_MNT_NS:
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
app.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
break;
}
switch (key) {
@ -244,7 +244,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
}
break;
case Const.Key.LOCALE:
LocaleManager.setLocale(mm);
LocaleManager.setLocale(app);
requireActivity().recreate();
break;
case Const.Key.UPDATE_CHANNEL:
@ -252,7 +252,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
CheckUpdates.check();
break;
case Const.Key.CHECK_UPDATES:
Utils.setupUpdateCheck();
AppUtils.setupUpdateCheck();
break;
}
Data.loadConfig();
@ -268,7 +268,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
((SwitchPreference) preference).setChecked(!checked);
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
((SwitchPreference) preference).setChecked(checked);
mm.mDB.setSettings(key, checked ? 1 : 0);
app.mDB.setSettings(key, checked ? 1 : 0);
});
break;
}
@ -286,7 +286,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
.getStringArray(R.array.su_notification)[Data.suNotificationType]);
requestTimeout.setSummary(
getString(R.string.request_timeout_summary,
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
app.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
multiuserConfig.setSummary(getResources()
.getStringArray(R.array.multiuser_summary)[Data.multiuserState]);
nsConfig.setSummary(getResources()

View File

@ -42,7 +42,7 @@ public class SuLogFragment extends BaseFragment {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = new SuLogFragment_ViewBinding(this, v);
adapter = new SuLogAdapter(mm.mDB);
adapter = new SuLogAdapter(app.mDB);
recyclerView.setAdapter(adapter);
updateList();
@ -69,7 +69,7 @@ public class SuLogFragment extends BaseFragment {
updateList();
return true;
case R.id.menu_clear:
mm.mDB.clearLogs();
app.mDB.clearLogs();
updateList();
return true;
default:

View File

@ -7,10 +7,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import com.topjohnwu.magisk.container.Policy;
import java.util.List;
@ -48,13 +48,13 @@ public class SuperuserFragment extends BaseFragment {
}
private void displayPolicyList() {
List<Policy> policyList = mm.mDB.getPolicyList();
List<Policy> policyList = app.mDB.getPolicyList();
if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(new PolicyAdapter(policyList, mm.mDB, pm));
recyclerView.setAdapter(new PolicyAdapter(policyList, app.mDB, pm));
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}

View File

@ -4,12 +4,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.services.OnBootService;
import com.topjohnwu.magisk.utils.DlInstallManager;
import com.topjohnwu.magisk.utils.DownloadApp;
import com.topjohnwu.magisk.utils.SuConnector;
import com.topjohnwu.superuser.Shell;
@ -21,7 +22,7 @@ public class GeneralReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
MagiskManager mm = Data.MM();
App app = App.self;
if (intent == null)
return;
String action = intent.getAction();
@ -34,10 +35,10 @@ public class GeneralReceiver extends BroadcastReceiver {
bootAction = "boot";
switch (bootAction) {
case "request":
Intent i = new Intent(mm, Data.classMap.get(SuRequestActivity.class))
Intent i = new Intent(app, ClassMap.get(SuRequestActivity.class))
.putExtra("socket", intent.getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mm.startActivity(i);
app.startActivity(i);
break;
case "log":
SuConnector.handleLogs(intent);
@ -48,24 +49,24 @@ public class GeneralReceiver extends BroadcastReceiver {
case "boot":
default:
/* The actual on-boot trigger */
OnBootService.enqueueWork(mm);
OnBootService.enqueueWork(app);
break;
}
break;
case Intent.ACTION_PACKAGE_REPLACED:
// This will only work pre-O
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
mm.mDB.deletePolicy(getPkg(intent));
if (app.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
app.mDB.deletePolicy(getPkg(intent));
}
break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
String pkg = getPkg(intent);
mm.mDB.deletePolicy(pkg);
app.mDB.deletePolicy(pkg);
Shell.su("magiskhide --rm " + pkg).submit();
break;
case Const.Key.BROADCAST_MANAGER_UPDATE:
Data.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK);
DlInstallManager.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME));
DownloadApp.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME));
break;
case Const.Key.BROADCAST_REBOOT:
Shell.su("/system/bin/reboot").submit();

View File

@ -8,12 +8,13 @@ import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Build;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@ -24,55 +25,54 @@ public class ShortcutReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
MagiskManager mm = Data.MM();
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
manager.setDynamicShortcuts(getShortCuts(mm));
manager.setDynamicShortcuts(getShortCuts(App.self));
}
}
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
private ArrayList<ShortcutInfo> getShortCuts(App app) {
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
boolean root = Shell.rootAccess();
if (Utils.showSuperUser()) {
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
.setShortLabel(mm.getString(R.string.superuser))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
shortCuts.add(new ShortcutInfo.Builder(app, "superuser")
.setShortLabel(app.getString(R.string.superuser))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "superuser")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_superuser))
.setIcon(Icon.createWithResource(app, R.drawable.sc_superuser))
.setRank(0)
.build());
}
if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
.setShortLabel(mm.getString(R.string.magiskhide))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
if (root && app.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(app, "magiskhide")
.setShortLabel(app.getString(R.string.magiskhide))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_magiskhide))
.setIcon(Icon.createWithResource(app, R.drawable.sc_magiskhide))
.setRank(1)
.build());
}
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
.setShortLabel(mm.getString(R.string.modules))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
if (!app.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(app, "modules")
.setShortLabel(app.getString(R.string.modules))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "modules")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_extension))
.setIcon(Icon.createWithResource(app, R.drawable.sc_extension))
.setRank(3)
.build());
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
.setShortLabel(mm.getString(R.string.downloads))
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
shortCuts.add(new ShortcutInfo.Builder(app, "downloads")
.setShortLabel(app.getString(R.string.downloads))
.setIntent(new Intent(app, ClassMap.get(SplashActivity.class))
.putExtra(Const.Key.OPEN_SECTION, "downloads")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
.setIcon(Icon.createWithResource(mm, R.drawable.sc_cloud_download))
.setIcon(Icon.createWithResource(app, R.drawable.sc_cloud_download))
.setRank(2)
.build());
}

View File

@ -3,8 +3,8 @@ package com.topjohnwu.magisk.services;
import android.content.Context;
import android.content.Intent;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.Const;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
@ -15,7 +15,7 @@ import androidx.core.app.JobIntentService;
public class OnBootService extends JobIntentService {
public static void enqueueWork(Context context) {
enqueueWork(context, Data.classMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
enqueueWork(context, ClassMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
}
@Override

View File

@ -3,7 +3,10 @@ package com.topjohnwu.magisk.services;
import android.app.job.JobParameters;
import android.app.job.JobService;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.tasks.CheckUpdates;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.superuser.Shell;
public class UpdateCheckService extends JobService {
@ -11,7 +14,14 @@ public class UpdateCheckService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Shell.getShell();
CheckUpdates.check(() -> jobFinished(params, false));
CheckUpdates.check(() -> {
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
Notifications.managerUpdate();
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
Notifications.magiskUpdate();
}
jobFinished(params, false);
});
return true;
}

View File

@ -0,0 +1,50 @@
package com.topjohnwu.magisk.utils;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.services.UpdateCheckService;
public class AppUtils {
public static void setupUpdateCheck() {
App app = App.self;
JobScheduler scheduler = (JobScheduler) app.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (app.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > app.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(app, ClassMap.get(UpdateCheckService.class));
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(8 * 60 * 60 * 1000)
.build();
scheduler.schedule(info);
}
} else {
scheduler.cancel(Const.UPDATE_SERVICE_VER);
}
}
public static void openLink(Context context, Uri link) {
Intent intent = new Intent(Intent.ACTION_VIEW, link);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
} else {
Utils.toast(R.string.open_link_failed_toast, Toast.LENGTH_SHORT);
}
}
}

View File

@ -1,44 +0,0 @@
package com.topjohnwu.magisk.utils;
import com.topjohnwu.utils.SignBoot;
import java.io.FileInputStream;
import java.io.InputStream;
public class BootSigner {
public static void main(String[] args) throws Exception {
if (args.length > 0 && "-verify".equals(args[0])) {
String certPath = "";
if (args.length >= 2) {
/* args[1] is the path to a public key certificate */
certPath = args[1];
}
boolean signed = SignBoot.verifySignature(System.in,
certPath.isEmpty() ? null : new FileInputStream(certPath));
System.exit(signed ? 0 : 1);
} else if (args.length > 0 && "-sign".equals(args[0])) {
InputStream cert = null;
InputStream key = null;
if (args.length >= 3) {
cert = new FileInputStream(args[1]);
key = new FileInputStream(args[2]);
}
boolean success = SignBoot.doSignature("/boot", System.in, System.out, cert, key);
System.exit(success ? 0 : 1);
} else {
System.err.println(
"BootSigner <actions> [args]\n" +
"Input from stdin, outputs to stdout\n" +
"\n" +
"Actions:\n" +
" -verify [x509.pem]\n" +
" verify image, cert is optional\n" +
" -sign [x509.pem] [pk8]\n" +
" sign image, cert and key pair is optional\n"
);
}
}
}

View File

@ -2,11 +2,12 @@ package com.topjohnwu.magisk.utils;
import android.os.AsyncTask;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.RootUtils;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.PatchAPK;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.net.Networking;
import com.topjohnwu.net.ResponseListener;
@ -18,7 +19,7 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class DlInstallManager {
public class DownloadApp {
public static void upgrade(String name) {
dlInstall(name, new PatchPackageName());
@ -31,8 +32,7 @@ public class DlInstallManager {
}
private static void dlInstall(String name, ManagerDownloadListener listener) {
MagiskManager mm = Data.MM();
File apk = new File(mm.getFilesDir(), "manager.apk");
File apk = new File(App.self.getFilesDir(), "manager.apk");
ProgressNotification progress = new ProgressNotification(name);
listener.setProgressNotification(progress);
Networking.get(Data.managerLink)
@ -63,23 +63,23 @@ public class DlInstallManager {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
File patched = apk;
MagiskManager mm = Data.MM();
if (!mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
App app = App.self;
if (!App.self.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
progress.getNotification()
.setProgress(0, 0, true)
.setContentTitle(mm.getString(R.string.hide_manager_title))
.setContentTitle(app.getString(R.string.hide_manager_title))
.setContentText("");
progress.update();
patched = new File(apk.getParent(), "patched.apk");
try {
JarMap jarMap = new JarMap(apk);
PatchAPK.patch(jarMap, mm.getPackageName());
PatchAPK.patch(jarMap, app.getPackageName());
SignAPK.sign(jarMap, new BufferedOutputStream(new FileOutputStream(patched)));
} catch (Exception e) {
return;
}
}
APKInstall.install(mm, patched);
APKInstall.install(app, patched);
progress.dismiss();
}
}
@ -88,17 +88,17 @@ public class DlInstallManager {
@Override
public void onDownloadComplete(File apk, ProgressNotification progress) {
MagiskManager mm = Data.MM();
App app = App.self;
progress.getNotification()
.setProgress(0, 0, true)
.setContentTitle(mm.getString(R.string.restore_img_msg))
.setContentTitle(app.getString(R.string.restore_img_msg))
.setContentText("");
progress.update();
Data.exportPrefs();
// Make it world readable
apk.setReadable(true, false);
if (ShellUtils.fastCmdResult("pm install " + apk))
RootUtils.rmAndLaunch(mm.getPackageName(), BuildConfig.APPLICATION_ID);
RootUtils.rmAndLaunch(app.getPackageName(), BuildConfig.APPLICATION_ID);
progress.dismiss();
}
}

View File

@ -1,17 +1,16 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.magisk.utils;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.container.Repo;
import com.topjohnwu.magisk.ClassMap;
import com.topjohnwu.magisk.FlashActivity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.components.ProgressNotification;
import com.topjohnwu.magisk.container.Repo;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.ShellUtils;
@ -28,26 +27,25 @@ import java.util.zip.ZipOutputStream;
public class DownloadModule {
public static void exec(BaseActivity activity, Repo repo, boolean install) {
activity.runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> dlProcessInstall(repo, install)));
activity.runWithExternalRW(() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(
() -> dlProcessInstall(repo, install)));
}
private static void dlProcessInstall(Repo repo, boolean install) {
File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
ProgressNotification progress = new ProgressNotification(output.getName());
try {
MagiskManager mm = Data.MM();
InputStream in = Networking.get(repo.getZipUrl())
.setDownloadProgressListener(progress)
.execForInputStream().getResult();
removeTopFolder(in, new BufferedOutputStream(new FileOutputStream(output)));
if (install) {
progress.dismiss();
Intent intent = new Intent(mm, Data.classMap.get(FlashActivity.class));
Intent intent = new Intent(App.self, ClassMap.get(FlashActivity.class));
intent.setData(Uri.fromFile(output))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
mm.startActivity(intent);
App.self.startActivity(intent);
} else {
progress.getNotification().setContentTitle(output.getName());
progress.dlDone();

View File

@ -16,9 +16,9 @@ import android.security.keystore.KeyProperties;
import android.view.Gravity;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
@ -36,10 +36,9 @@ public abstract class FingerprintHelper {
private CancellationSignal cancel;
public static boolean useFingerPrint() {
MagiskManager mm = Data.MM();
boolean fp = mm.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
boolean fp = App.self.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
if (fp && !canUseFingerprint()) {
mm.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
App.self.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
fp = false;
}
return fp;
@ -48,9 +47,8 @@ public abstract class FingerprintHelper {
public static boolean canUseFingerprint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return false;
MagiskManager mm = Data.MM();
KeyguardManager km = mm.getSystemService(KeyguardManager.class);
FingerprintManager fm = mm.getSystemService(FingerprintManager.class);
KeyguardManager km = App.self.getSystemService(KeyguardManager.class);
FingerprintManager fm = App.self.getSystemService(FingerprintManager.class);
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
}
@ -106,7 +104,7 @@ public abstract class FingerprintHelper {
protected FingerprintHelper() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = Data.MM().getSystemService(FingerprintManager.class);
manager = App.self.getSystemService(FingerprintManager.class);
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);

View File

@ -1,18 +0,0 @@
package com.topjohnwu.magisk.utils;
public interface ISafetyNetHelper {
int RESPONSE_ERR = 0x01;
int CONNECTION_FAIL = 0x02;
int BASIC_PASS = 0x10;
int CTS_PASS = 0x20;
void attest();
int getVersion();
interface Callback {
void onResponse(int responseCode);
}
}

View File

@ -1,77 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import androidx.annotation.StringRes;
public class LocaleManager {
public static Locale locale = Locale.getDefault();
public final static Locale defaultLocale = Locale.getDefault();
public static List<Locale> locales;
public static void setLocale(MagiskManager mm) {
String localeConfig = mm.prefs.getString(Const.Key.LOCALE, "");
if (localeConfig.isEmpty()) {
locale = defaultLocale;
} else {
locale = Locale.forLanguageTag(localeConfig);
}
Locale.setDefault(locale);
Resources res = mm.getResources();
Configuration config = res.getConfiguration();
config.setLocale(locale);
res.updateConfiguration(config, res.getDisplayMetrics());
}
public static String getString(Locale locale, @StringRes int id) {
Configuration config = new Configuration();
config.setLocale(locale);
return Data.MM().createConfigurationContext(config).getString(id);
}
public static void loadAvailableLocales() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
locales = new ArrayList<>();
HashSet<String> set = new HashSet<>();
Resources res = Data.MM().getResources();
Locale locale;
@StringRes int compareId = R.string.download_file_error;
// Add default locale
locales.add(Locale.ENGLISH);
set.add(getString(Locale.ENGLISH, compareId));
// Add some special locales
locales.add(Locale.TAIWAN);
set.add(getString(Locale.TAIWAN, compareId));
locale = new Locale("pt", "BR");
locales.add(locale);
set.add(getString(locale, compareId));
// Other locales
for (String s : res.getAssets().getLocales()) {
locale = Locale.forLanguageTag(s);
if (set.add(getString(locale, compareId))) {
locales.add(locale);
}
}
Collections.sort(locales, (a, b) -> a.getDisplayName(a).compareTo(b.getDisplayName(b)));
Topic.publish(Topic.LOCALE_FETCH_DONE);
});
}
}

View File

@ -1,26 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.util.Log;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
public class Logger {
public static void debug(String line) {
if (BuildConfig.DEBUG)
Log.d(Const.DEBUG_TAG, "DEBUG: " + line);
}
public static void debug(String fmt, Object... args) {
debug(Utils.fmt(fmt, args));
}
public static void error(String line) {
Log.e(Const.DEBUG_TAG, "ERROR: " + line);
}
public static void error(String fmt, Object... args) {
error(Utils.fmt(fmt, args));
}
}

View File

@ -1,16 +1,16 @@
package com.topjohnwu.magisk.asyncs;
package com.topjohnwu.magisk.utils;
import android.os.AsyncTask;
import android.widget.Toast;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.utils.RootUtils;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.BuildConfig;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.Notifications;
import com.topjohnwu.magisk.utils.RootUtils;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK;
@ -95,14 +95,14 @@ public class PatchAPK {
}
private static boolean patchAndHide() {
MagiskManager mm = Data.MM();
App app = App.self;
// Generate a new app with random package name
File repack = new File(mm.getFilesDir(), "patched.apk");
File repack = new File(app.getFilesDir(), "patched.apk");
String pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length());
try {
JarMap apk = new JarMap(mm.getPackageCodePath());
JarMap apk = new JarMap(app.getPackageCodePath());
if (!patch(apk, pkg))
return false;
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(repack)));
@ -115,7 +115,7 @@ public class PatchAPK {
if (!ShellUtils.fastCmdResult("pm install " + repack))
return false;;
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
app.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
Data.exportPrefs();
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, pkg);
@ -143,10 +143,10 @@ public class PatchAPK {
public static void hideManager() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
MagiskManager mm = Data.MM();
App app = App.self;
NotificationCompat.Builder progress =
Notifications.progress(mm.getString(R.string.hide_manager_title));
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
Notifications.progress(app.getString(R.string.hide_manager_title));
NotificationManagerCompat mgr = NotificationManagerCompat.from(app);
mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build());
boolean b = patchAndHide();
mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);

View File

@ -1,47 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R;
import com.topjohnwu.superuser.BusyBox;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;
import java.io.File;
import java.io.InputStream;
import androidx.annotation.NonNull;
public class RootUtils extends Shell.Initializer {
static {
BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
}
public static void rmAndLaunch(String rm, String launch) {
Shell.su(Utils.fmt("(rm_launch %d %s %s)&", Const.USER_ID, rm, launch)).exec();
}
@Override
public boolean onInit(Context context, @NonNull Shell shell) {
Shell.Job job = shell.newJob();
if (shell.isRoot()) {
job.add(context.getResources().openRawResource(R.raw.util_functions))
.add(context.getResources().openRawResource(R.raw.utils));
Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
Data.loadMagiskInfo();
} else {
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
job.add(nonroot);
}
job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
Data.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
return true;
}
}

View File

@ -9,12 +9,13 @@ import android.os.Process;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.core.App;
import com.topjohnwu.core.Const;
import com.topjohnwu.core.Data;
import com.topjohnwu.core.container.Policy;
import com.topjohnwu.core.container.SuLogEntry;
import com.topjohnwu.core.utils.Utils;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@ -76,8 +77,8 @@ public abstract class SuConnector {
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
MagiskManager mm = Data.MM();
PackageManager pm = mm.getPackageManager();
App app = App.self;
PackageManager pm = app.getPackageManager();
Policy policy;
boolean notify;
@ -91,7 +92,7 @@ public abstract class SuConnector {
}
} else {
// Doesn't report whether notify or not, check database ourselves
policy = mm.mDB.getPolicy(fromUid);
policy = app.mDB.getPolicy(fromUid);
if (policy == null)
return;
notify = policy.notification;
@ -116,24 +117,22 @@ public abstract class SuConnector {
log.fromPid = pid;
log.command = command;
log.date = new Date();
mm.mDB.addLog(log);
app.mDB.addLog(log);
}
private static void handleNotify(Policy policy) {
MagiskManager mm = Data.MM();
String message = mm.getString(policy.policy == Policy.ALLOW ?
String message = App.self.getString(policy.policy == Policy.ALLOW ?
R.string.su_allow_toast : R.string.su_deny_toast, policy.appName);
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
Utils.toast(message, Toast.LENGTH_SHORT);
}
public static void handleNotify(Intent intent) {
MagiskManager mm = Data.MM();
int fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
try {
Policy policy = new Policy(fromUid, mm.getPackageManager());
Policy policy = new Policy(fromUid, App.self.getPackageManager());
policy.policy = intent.getIntExtra("policy", -1);
if (policy.policy >= 0)
handleNotify(policy);

View File

@ -1,107 +0,0 @@
package com.topjohnwu.magisk.utils;
import com.topjohnwu.magisk.Data;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
import androidx.annotation.IntDef;
public class Topic {
public static final int MAGISK_HIDE_DONE = 0;
public static final int MODULE_LOAD_DONE = 1;
public static final int REPO_LOAD_DONE = 2;
public static final int UPDATE_CHECK_DONE = 3;
public static final int SNET_CHECK_DONE = 4;
public static final int LOCALE_FETCH_DONE = 5;
@IntDef({MAGISK_HIDE_DONE, MODULE_LOAD_DONE, REPO_LOAD_DONE,
UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCALE_FETCH_DONE})
@Retention(RetentionPolicy.SOURCE)
public @interface TopicID {}
// We will not dynamically add topics, so use arrays instead of hash tables
private static Store[] topicList = new Store[6];
public static void subscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
topicList[topic] = new Store();
topicList[topic].subscribers.add(sub);
if (topicList[topic].published) {
sub.onPublish(topic, topicList[topic].results);
}
}
}
public static void subscribe(AutoSubscriber sub) {
if (sub instanceof Subscriber)
subscribe((Subscriber) sub, sub.getSubscribedTopics());
}
public static void unsubscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
continue;
topicList[topic].subscribers.remove(sub);
}
}
public static void unsubscribe(AutoSubscriber sub) {
if (sub instanceof Subscriber)
unsubscribe((Subscriber) sub, sub.getSubscribedTopics());
}
public static void publish(@TopicID int topic, Object... results) {
publish(true, topic, results);
}
public static void publish(boolean persist, @TopicID int topic, Object... results) {
if (topicList[topic] == null)
topicList[topic] = new Store();
if (persist) {
topicList[topic].results = results;
topicList[topic].published = true;
}
for (Subscriber sub : topicList[topic].subscribers) {
Data.mainHandler.post(() -> sub.onPublish(topic, results));
}
}
public static void reset(@TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
continue;
topicList[topic].published = false;
topicList[topic].results = null;
}
}
public static boolean isPublished(@TopicID int... topics) {
for (int topic : topics) {
if (topicList[topic] == null)
return false;
if (!topicList[topic].published)
return false;
}
return true;
}
private static class Store {
boolean published = false;
Set<Subscriber> subscribers = new HashSet<>();
Object[] results;
}
public interface Subscriber {
void onPublish(int topic, Object[] result);
}
public interface AutoSubscriber {
@TopicID
int[] getSubscribedTopics();
}
}

View File

@ -1,158 +0,0 @@
package com.topjohnwu.magisk.utils;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.OpenableColumns;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.net.Networking;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.io.SuFile;
import java.util.Locale;
import java.util.Map;
public class Utils {
public static int getPrefsInt(SharedPreferences prefs, String key, int def) {
return Integer.parseInt(prefs.getString(key, String.valueOf(def)));
}
public static int getPrefsInt(SharedPreferences prefs, String key) {
return getPrefsInt(prefs, key, 0);
}
public static String getNameFromUri(Context context, Uri uri) {
String name = null;
try (Cursor c = context.getContentResolver().query(uri, null, null, null, null)) {
if (c != null) {
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
if (nameIndex != -1) {
c.moveToFirst();
name = c.getString(nameIndex);
}
}
}
if (name == null) {
int idx = uri.getPath().lastIndexOf('/');
name = uri.getPath().substring(idx + 1);
}
return name;
}
public static int dpInPx(int dp) {
float scale = Data.MM().getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5);
}
public static String fmt(String fmt, Object... args) {
return String.format(Locale.US, fmt, args);
}
public static String dos2unix(String s) {
String newString = s.replace("\r\n", "\n");
if(!newString.endsWith("\n")) {
return newString + "\n";
} else {
return newString;
}
}
public static void setupUpdateCheck() {
MagiskManager mm = Data.MM();
JobScheduler scheduler = (JobScheduler) mm.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(mm, Data.classMap.get(UpdateCheckService.class));
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(8 * 60 * 60 * 1000)
.build();
scheduler.schedule(info);
}
} else {
scheduler.cancel(Const.UPDATE_SERVICE_VER);
}
}
public static void openLink(Context context, Uri link) {
Intent intent = new Intent(Intent.ACTION_VIEW, link);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
} else {
toast(R.string.open_link_failed_toast, Toast.LENGTH_SHORT);
}
}
public static void toast(CharSequence msg, int duration) {
Data.mainHandler.post(() -> Toast.makeText(Data.MM(), msg, duration).show());
}
public static void toast(int resId, int duration) {
Data.mainHandler.post(() -> Toast.makeText(Data.MM(), resId, duration).show());
}
public static void loadModules() {
Topic.reset(Topic.MODULE_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
Map<String, Module> moduleMap = new ValueSortedMap<>();
SuFile path = new SuFile(Const.MAGISK_PATH);
SuFile[] modules = path.listFiles(
(file, name) -> !name.equals("lost+found") && !name.equals(".core"));
for (SuFile file : modules) {
if (file.isFile()) continue;
Module module = new Module(Const.MAGISK_PATH + "/" + file.getName());
moduleMap.put(module.getId(), module);
}
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap);
});
}
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
try {
if (info.labelRes > 0) {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
}
} catch (Exception ignored) {}
return info.loadLabel(pm).toString();
}
public static boolean showSuperUser() {
if (Data.multiuserState < 0)
Data.multiuserState = Data.MM().mDB.getSettings(Const.Key.SU_MULTIUSER_MODE,
Const.Value.MULTIUSER_MODE_OWNER_ONLY);
return Shell.rootAccess() && (Const.USER_ID == 0 ||
Data.multiuserState != Const.Value.MULTIUSER_MODE_OWNER_MANAGED);
}
public static String dlString(String url) {
String s = Networking.get(url).execForString().getResult();
return s == null ? "" : s;
}
}

View File

@ -1,65 +0,0 @@
package com.topjohnwu.magisk.utils;
import com.topjohnwu.superuser.ShellUtils;
import com.topjohnwu.superuser.io.SuFile;
import com.topjohnwu.superuser.io.SuFileOutputStream;
import com.topjohnwu.utils.JarMap;
import com.topjohnwu.utils.SignAPK;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipUtils {
public static void unzip(File zip, File folder, String path, boolean junkPath) throws IOException {
InputStream in = new BufferedInputStream(new FileInputStream(zip));
unzip(in, folder, path, junkPath);
in.close();
}
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws IOException {
try {
ZipInputStream zipfile = new ZipInputStream(zip);
ZipEntry entry;
while ((entry = zipfile.getNextEntry()) != null) {
if (!entry.getName().startsWith(path) || entry.isDirectory()){
// Ignore directories, only create files
continue;
}
String name;
if (junkPath) {
name = entry.getName().substring(entry.getName().lastIndexOf('/') + 1);
} else {
name = entry.getName();
}
File dest = new File(folder, name);
if (!dest.getParentFile().exists() && !dest.getParentFile().mkdirs()) {
dest = new SuFile(folder, name);
dest.getParentFile().mkdirs();
}
try (OutputStream out = new SuFileOutputStream(dest)) {
ShellUtils.pump(zipfile, out);
}
}
}
catch(IOException e) {
e.printStackTrace();
throw e;
}
}
public static void signZip(File input, File output) throws Exception {
try (JarMap map = new JarMap(input, false);
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
SignAPK.sign(map, out);
}
}
}

Binary file not shown.

View File

@ -1,13 +0,0 @@
mount_partitions() {
[ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix` || SLOT=
[ "`getprop ro.build.system_root_image`" = "true" ] && SYSTEM_ROOT=true || SYSTEM_ROOT=false
}
get_flags() {
$SYSTEM_ROOT && KEEPVERITY=true || KEEPVERITY=false
[ "`getprop ro.crypto.state`" = "encrypted" ] && KEEPFORCEENCRYPT=true || KEEPFORCEENCRYPT=false
}
run_migrations() {
# NOP
}

View File

@ -1,105 +0,0 @@
env_check() {
for file in busybox magisk magiskboot magiskinit util_functions.sh boot_patch.sh; do
[ -f /data/adb/magisk/$file ] || return 1
done
return 0
}
fix_env() {
cd /data/adb/magisk
sh update-binary extract
rm -f update-binary magisk.apk
cd /
rm -rf /sbin/.magisk/busybox/*
/sbin/.magisk/mirror/bin/busybox --install -s /sbin/.magisk/busybox
}
direct_install() {
rm -rf /data/adb/magisk/* 2>/dev/null
mkdir -p /data/adb/magisk 2>/dev/null
chmod 700 /data/adb
cp -rf $1/* /data/adb/magisk
rm -rf /data/adb/magisk/new-boot.img
echo "- Flashing new boot image"
flash_image $1/new-boot.img $2
if [ $? -ne 0 ]; then
echo "! Insufficient partition size"
return 1
fi
rm -rf $1
return 0
}
mm_patch_dtbo() {
if $KEEPVERITY; then
return 1
else
find_dtbo_image
patch_dtbo_image
fi
}
restore_imgs() {
local SHA1=`grep_prop SHA1 /sbin/.magisk/config`
[ -z $SHA1 ] && local SHA1=`cat /.backup/.sha1`
[ -z $SHA1 ] && return 1
local STOCKBOOT=/data/stock_boot_${SHA1}.img.gz
local STOCKDTBO=/data/stock_dtbo.img.gz
[ -f $STOCKBOOT ] || return 1
find_boot_image
find_dtbo_image
if [ -f $STOCKDTBO -a -b "$DTBOIMAGE" ]; then
flash_image $STOCKDTBO $DTBOIMAGE
fi
if [ -f $STOCKBOOT -a -b "$BOOTIMAGE" ]; then
flash_image $STOCKBOOT $BOOTIMAGE
return 0
fi
return 1
}
post_ota() {
cd $1
chmod 755 bootctl
./bootctl hal-info || return
[ `./bootctl get-current-slot` -eq 0 ] && SLOT_NUM=1 || SLOT_NUM=0
./bootctl set-active-boot-slot $SLOT_NUM
echo '${0%/*}/../bootctl mark-boot-successful;rm -f ${0%/*}/../bootctl $0' > post-fs-data.d/post_ota.sh
chmod 755 post-fs-data.d/post_ota.sh
cd /
}
add_hosts_module() {
# Do not touch existing hosts module
[ -d /sbin/.magisk/img/hosts ] && return
cd /sbin/.magisk/img
mkdir -p hosts/system/etc
cat << EOF > hosts/module.prop
id=hosts
name=Systemless Hosts
version=1.0
versionCode=1
author=Magisk Manager
description=Magisk Manager built-in systemless hosts module
minMagisk=17000
EOF
if [ -f .core/hosts ]; then
# Migrate old hosts file to new module
mv -f .core/hosts hosts/system/etc/hosts
else
cp -f /system/etc/hosts hosts/system/etc/hosts
fi
magisk --clone-attr /system/etc/hosts hosts/system/etc/hosts
touch hosts/update
touch hosts/auto_mount
cd /
}
rm_launch() {
db_clean $1
pm uninstall $2
am start -n ${3}/a.c
exit
}

View File

@ -6,12 +6,6 @@ import android.net.NetworkInfo;
public class Download {
public static String getLegalFilename(CharSequence filename) {
return filename.toString().replace(" ", "_").replace("'", "").replace("\"", "")
.replace("$", "").replace("`", "").replace("*", "").replace("/", "_")
.replace("#", "").replace("@", "").replace("\\", "_");
}
public static boolean checkNetworkStatus(Context context) {
ConnectivityManager manager = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);