package com.topjohnwu.magisk; import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.Handler; import android.preference.PreferenceManager; import android.widget.Toast; import com.topjohnwu.magisk.asyncs.DownloadBusybox; import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.database.RepoDatabaseHelper; import com.topjohnwu.magisk.database.SuDatabaseHelper; import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.utils.SafetyNetHelper; import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Topic; import com.topjohnwu.magisk.utils.Utils; import java.io.File; import java.util.List; import java.util.Locale; import java.util.Map; public class MagiskManager extends Application { public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk"; public static final String TMP_FOLDER_PATH = "/dev/tmp"; public static final String MAGISK_PATH = "/magisk"; public static final String INTENT_SECTION = "section"; public static final String INTENT_VERSION = "version"; public static final String INTENT_LINK = "link"; public static final String MAGISKHIDE_PROP = "persist.magisk.hide"; public static final String DISABLE_INDICATION_PROP = "ro.magisk.disable"; public static final String NOTIFICATION_CHANNEL = "magisk_update_notice"; public static final String BUSYBOX_VERSION = "1.27.1"; // Topics public final Topic magiskHideDone = new Topic(); public final Topic reloadActivity = new Topic(); public final Topic moduleLoadDone = new Topic(); public final Topic repoLoadDone = new Topic(); public final Topic updateCheckDone = new Topic(); public final Topic safetyNetDone = new Topic(); public final Topic localeDone = new Topic(); // Info public String magiskVersionString; public int magiskVersionCode = -1; public String remoteMagiskVersionString; public int remoteMagiskVersionCode = -1; public String magiskLink; public String releaseNoteLink; public String remoteManagerVersionString; public int remoteManagerVersionCode = -1; public String managerLink; public SafetyNetHelper.Result SNCheckResult; public String bootBlock = null; public boolean isSuClient = false; public String suVersion = null; public boolean disabled; // Data public Map moduleMap; public List blockList; public List locales; // Configurations public static boolean shellLogging; public static boolean devLogging; public static Locale locale; public static Locale defaultLocale; public boolean magiskHide; public boolean isDarkTheme; public boolean updateNotification; public boolean suReauth; public int suRequestTimeout; public int suLogTimeout = 14; public int suAccessState; public int multiuserMode; public int suResponseType; public int suNotificationType; public int suNamespaceMode; public String localeConfig; // Global resources public SharedPreferences prefs; public SuDatabaseHelper suDB; public RepoDatabaseHelper repoDB; public Shell shell; private static Handler mHandler = new Handler(); private static class LoadLocale extends ParallelTask { LoadLocale(Context context) { super(context); } @Override protected Void doInBackground(Void... voids) { getMagiskManager().locales = Utils.getAvailableLocale(getMagiskManager()); return null; } @Override protected void onPostExecute(Void aVoid) { getMagiskManager().localeDone.publish(); } } @Override public void onCreate() { super.onCreate(); new File(getApplicationInfo().dataDir).mkdirs(); /* Create the app data directory */ prefs = PreferenceManager.getDefaultSharedPreferences(this); suDB = new SuDatabaseHelper(this); repoDB = new RepoDatabaseHelper(this); loadConfig(); } public void setLocale() { localeConfig = prefs.getString("locale", ""); if (localeConfig.isEmpty()) { locale = defaultLocale; } else { locale = Locale.forLanguageTag(localeConfig); } Resources res = getBaseContext().getResources(); Configuration config = new Configuration(res.getConfiguration()); config.setLocale(locale); res.updateConfiguration(config, res.getDisplayMetrics()); } private void loadConfig() { // Locale defaultLocale = Locale.getDefault(); setLocale(); isDarkTheme = prefs.getBoolean("dark_theme", false); if (BuildConfig.DEBUG) { devLogging = prefs.getBoolean("developer_logging", false); shellLogging = prefs.getBoolean("shell_logging", false); } else { devLogging = false; shellLogging = false; } // su suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10); suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0); suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1); suReauth = prefs.getBoolean("su_reauth", false); suAccessState = suDB.getSettings(SuDatabaseHelper.ROOT_ACCESS, 3); multiuserMode = suDB.getSettings(SuDatabaseHelper.MULTIUSER_MODE, 0); suNamespaceMode = suDB.getSettings(SuDatabaseHelper.MNT_NS, 1); updateNotification = prefs.getBoolean("notification", true); } public void toast(String msg, int duration) { mHandler.post(() -> Toast.makeText(this, msg, duration).show()); } public void toast(int resId, int duration) { mHandler.post(() -> Toast.makeText(this, resId, duration).show()); } public void init() { new LoadLocale(this).exec(); new DownloadBusybox(this).exec(); getMagiskInfo(); updateBlockInfo(); // Write back default values prefs.edit() .putBoolean("dark_theme", isDarkTheme) .putBoolean("magiskhide", magiskHide) .putBoolean("notification", updateNotification) .putBoolean("hosts", new File("/magisk/.core/hosts").exists()) .putBoolean("disable", Utils.itemExist(shell, MAGISK_DISABLE_FILE)) .putBoolean("su_reauth", suReauth) .putString("su_request_timeout", String.valueOf(suRequestTimeout)) .putString("su_auto_response", String.valueOf(suResponseType)) .putString("su_notification", String.valueOf(suNotificationType)) .putString("su_access", String.valueOf(suAccessState)) .putString("multiuser_mode", String.valueOf(multiuserMode)) .putString("mnt_ns", String.valueOf(suNamespaceMode)) .putString("busybox_version", BUSYBOX_VERSION) .putString("locale", localeConfig) .apply(); // Add busybox to PATH shell.su_raw("PATH=" + getApplicationInfo().dataDir + "/busybox:$PATH"); // Create notification channel on Android O if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL, getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT); ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); } } public void getMagiskInfo() { Shell.getShell(this); List ret; ret = shell.sh("su -v"); if (Utils.isValidShellResponse(ret)) { suVersion = ret.get(0); isSuClient = suVersion.toUpperCase().contains("MAGISK"); } ret = shell.sh("magisk -v"); if (!Utils.isValidShellResponse(ret)) { ret = shell.sh("getprop magisk.version"); if (Utils.isValidShellResponse(ret)) { try { magiskVersionString = ret.get(0); magiskVersionCode = (int) Double.parseDouble(ret.get(0)) * 10; } catch (NumberFormatException ignored) {} } } else { magiskVersionString = ret.get(0).split(":")[0]; ret = shell.sh("magisk -V"); try { magiskVersionCode = Integer.parseInt(ret.get(0)); } catch (NumberFormatException ignored) {} } ret = shell.sh("getprop " + DISABLE_INDICATION_PROP); try { disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0; } catch (NumberFormatException e) { disabled = false; } ret = shell.sh("getprop " + MAGISKHIDE_PROP); try { magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0; } catch (NumberFormatException e) { magiskHide = true; } } private void updateBlockInfo() { List res = shell.su( "for BLOCK in boot_a kern-a android_boot kernel boot lnx; do", " BOOTIMAGE=`find /dev/block -iname $BLOCK | head -n 1` 2>/dev/null", " [ ! -z $BOOTIMAGE ] && break", "done", "[ ! -z \"$BOOTIMAGE\" -a -L \"$BOOTIMAGE\" ] && BOOTIMAGE=`readlink $BOOTIMAGE`", "echo \"$BOOTIMAGE\"" ); if (Utils.isValidShellResponse(res)) { bootBlock = res.get(0); } else { blockList = shell.su("ls -d /dev/block/mmc* /dev/block/sd* 2>/dev/null"); } } }