Magisk/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java

312 lines
12 KiB
Java
Raw Normal View History

2017-02-06 19:01:32 +01:00
package com.topjohnwu.magisk;
import android.app.Application;
2017-07-13 20:27:02 +02:00
import android.app.NotificationChannel;
import android.app.NotificationManager;
2017-08-29 20:28:24 +02:00
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
2017-07-13 20:27:02 +02:00
import android.content.Context;
2017-02-06 19:01:32 +01:00
import android.content.SharedPreferences;
2017-07-22 16:14:02 +02:00
import android.content.res.Configuration;
import android.content.res.Resources;
2017-07-13 20:27:02 +02:00
import android.os.Build;
2017-02-06 23:02:06 +01:00
import android.os.Handler;
2017-02-06 19:01:32 +01:00
import android.preference.PreferenceManager;
2017-02-06 23:02:06 +01:00
import android.widget.Toast;
2017-02-06 19:01:32 +01:00
2017-08-28 19:34:42 +02:00
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.DownloadBusybox;
2017-08-29 20:28:24 +02:00
import com.topjohnwu.magisk.asyncs.LoadModules;
2017-07-22 16:14:02 +02:00
import com.topjohnwu.magisk.asyncs.ParallelTask;
2017-08-29 20:28:24 +02:00
import com.topjohnwu.magisk.asyncs.UpdateRepos;
2017-07-20 18:46:13 +02:00
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
2017-05-31 18:19:52 +02:00
import com.topjohnwu.magisk.database.SuDatabaseHelper;
2017-02-06 19:01:32 +01:00
import com.topjohnwu.magisk.module.Module;
2017-08-29 20:28:24 +02:00
import com.topjohnwu.magisk.services.UpdateCheckService;
2017-08-28 19:34:42 +02:00
import com.topjohnwu.magisk.superuser.SuReceiver;
import com.topjohnwu.magisk.superuser.SuRequestActivity;
2017-05-19 21:04:14 +02:00
import com.topjohnwu.magisk.utils.SafetyNetHelper;
2017-02-06 19:01:32 +01:00
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Topic;
2017-02-06 19:01:32 +01:00
import com.topjohnwu.magisk.utils.Utils;
import java.io.File;
import java.util.List;
2017-07-22 16:14:02 +02:00
import java.util.Locale;
import java.util.Map;
2017-09-02 21:26:01 +02:00
import java.util.concurrent.ExecutionException;
2017-02-06 19:01:32 +01:00
public class MagiskManager extends Application {
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
2017-02-12 12:49:46 +01:00
public static final String TMP_FOLDER_PATH = "/dev/tmp";
2017-02-12 13:53:41 +01:00
public static final String MAGISK_PATH = "/magisk";
public static final String INTENT_SECTION = "section";
2017-07-19 19:44:32 +02:00
public static final String INTENT_VERSION = "version";
public static final String INTENT_LINK = "link";
2017-05-26 20:41:24 +02:00
public static final String MAGISKHIDE_PROP = "persist.magisk.hide";
public static final String DISABLE_INDICATION_PROP = "ro.magisk.disable";
2017-07-13 20:27:02 +02:00
public static final String NOTIFICATION_CHANNEL = "magisk_update_notice";
2017-09-02 21:26:01 +02:00
public static final String BUSYBOXPATH = "/dev/magisk/bin";
2017-08-29 20:28:24 +02:00
public static final int UPDATE_SERVICE_ID = 1;
2017-02-06 19:01:32 +01:00
// 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();
2017-02-06 19:01:32 +01:00
// Info
public String magiskVersionString;
2017-05-11 20:25:07 +02:00
public int magiskVersionCode = -1;
public String remoteMagiskVersionString;
public int remoteMagiskVersionCode = -1;
2017-02-06 19:01:32 +01:00
public String magiskLink;
public String releaseNoteLink;
2017-06-06 20:19:23 +02:00
public String remoteManagerVersionString;
public int remoteManagerVersionCode = -1;
public String managerLink;
2017-05-19 21:04:14 +02:00
public SafetyNetHelper.Result SNCheckResult;
2017-02-06 19:01:32 +01:00
public String bootBlock = null;
public boolean isSuClient = false;
public String suVersion = null;
public boolean disabled;
2017-02-06 19:01:32 +01:00
// Data
public Map<String, Module> moduleMap;
2017-02-06 19:01:32 +01:00
public List<String> blockList;
2017-07-22 16:14:02 +02:00
public List<Locale> locales;
2017-02-06 19:01:32 +01:00
// Configurations
public static boolean shellLogging;
public static boolean devLogging;
2017-07-22 16:14:02 +02:00
public static Locale locale;
public static Locale defaultLocale;
2017-02-06 19:01:32 +01:00
2017-02-07 00:32:40 +01:00
public boolean magiskHide;
2017-02-06 19:01:32 +01:00
public boolean isDarkTheme;
2017-02-21 21:58:03 +01:00
public boolean updateNotification;
2017-05-31 10:31:33 +02:00
public boolean suReauth;
2017-02-06 19:01:32 +01:00
public int suRequestTimeout;
public int suLogTimeout = 14;
public int suAccessState;
2017-05-26 20:41:24 +02:00
public int multiuserMode;
2017-02-06 19:01:32 +01:00
public int suResponseType;
public int suNotificationType;
2017-06-08 16:27:24 +02:00
public int suNamespaceMode;
2017-08-26 19:38:05 +02:00
public String localeConfig;
2017-08-28 19:34:42 +02:00
public int updateChannel;
2017-09-03 11:24:05 +02:00
public String bootFormat;
2017-02-06 19:01:32 +01:00
2017-05-31 18:19:52 +02:00
// Global resources
2017-02-06 19:01:32 +01:00
public SharedPreferences prefs;
2017-05-31 18:19:52 +02:00
public SuDatabaseHelper suDB;
2017-07-20 18:46:13 +02:00
public RepoDatabaseHelper repoDB;
2017-07-17 21:34:06 +02:00
public Shell shell;
2017-02-06 19:01:32 +01:00
2017-02-06 23:02:06 +01:00
private static Handler mHandler = new Handler();
2017-09-02 21:26:01 +02:00
private boolean started = false;
2017-02-06 23:02:06 +01:00
2017-07-22 16:14:02 +02:00
private static class LoadLocale extends ParallelTask<Void, Void, Void> {
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();
2017-07-22 16:14:02 +02:00
}
}
2017-08-11 19:31:34 +02:00
@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();
}
2017-07-22 16:14:02 +02:00
public void setLocale() {
2017-08-26 19:38:05 +02:00
localeConfig = prefs.getString("locale", "");
if (localeConfig.isEmpty()) {
2017-07-22 16:14:02 +02:00
locale = defaultLocale;
} else {
2017-08-26 19:38:05 +02:00
locale = Locale.forLanguageTag(localeConfig);
2017-07-22 16:14:02 +02:00
}
Resources res = getBaseContext().getResources();
Configuration config = new Configuration(res.getConfiguration());
config.setLocale(locale);
res.updateConfiguration(config, res.getDisplayMetrics());
}
2017-09-03 09:35:14 +02:00
public void loadConfig() {
2017-08-11 19:31:34 +02:00
// Locale
2017-07-22 16:14:02 +02:00
defaultLocale = Locale.getDefault();
setLocale();
2017-08-11 19:31:34 +02:00
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);
2017-08-28 19:34:42 +02:00
suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", SuRequestActivity.PROMPT);
suNotificationType = Utils.getPrefsInt(prefs, "su_notification", SuReceiver.TOAST);
2017-08-11 19:31:34 +02:00
suReauth = prefs.getBoolean("su_reauth", false);
2017-08-28 19:34:42 +02:00
suAccessState = suDB.getSettings(SuDatabaseHelper.ROOT_ACCESS, SuDatabaseHelper.ROOT_ACCESS_APPS_AND_ADB);
multiuserMode = suDB.getSettings(SuDatabaseHelper.MULTIUSER_MODE, SuDatabaseHelper.MULTIUSER_MODE_OWNER_ONLY);
suNamespaceMode = suDB.getSettings(SuDatabaseHelper.MNT_NS, SuDatabaseHelper.NAMESPACE_MODE_REQUESTER);
2017-08-11 19:31:34 +02:00
updateNotification = prefs.getBoolean("notification", true);
2017-08-28 19:34:42 +02:00
updateChannel = Utils.getPrefsInt(prefs, "update_channel", CheckUpdates.STABLE_CHANNEL);
2017-09-03 11:24:05 +02:00
bootFormat = prefs.getString("boot_format", ".img");
2017-02-06 19:01:32 +01:00
}
2017-02-06 23:02:06 +01:00
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());
}
2017-08-29 20:28:24 +02:00
public void startup() {
2017-09-02 21:26:01 +02:00
if (started)
2017-08-29 20:28:24 +02:00
return;
2017-09-02 21:26:01 +02:00
started = true;
boolean hasNetwork = Utils.checkNetworkStatus(this);
2017-08-29 20:28:24 +02:00
2017-08-11 19:31:34 +02:00
getMagiskInfo();
2017-09-02 21:26:01 +02:00
new LoadLocale(this).exec();
// Force synchronous, make sure we have busybox to use
if (hasNetwork && Shell.rootAccess()
&& !Utils.itemExist(shell, BUSYBOXPATH + "/busybox")) {
try {
new DownloadBusybox(this).exec().get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
shell.su_raw("export PATH=" + BUSYBOXPATH + ":$PATH");
2017-07-17 21:34:06 +02:00
updateBlockInfo();
2017-08-11 19:31:34 +02:00
// Write back default values
2017-02-06 19:01:32 +01:00
prefs.edit()
.putBoolean("dark_theme", isDarkTheme)
.putBoolean("magiskhide", magiskHide)
2017-02-21 21:58:03 +01:00
.putBoolean("notification", updateNotification)
2017-02-06 19:01:32 +01:00
.putBoolean("hosts", new File("/magisk/.core/hosts").exists())
2017-07-17 21:34:06 +02:00
.putBoolean("disable", Utils.itemExist(shell, MAGISK_DISABLE_FILE))
2017-05-31 10:31:33 +02:00
.putBoolean("su_reauth", suReauth)
2017-02-06 19:01:32 +01:00
.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))
2017-05-26 20:41:24 +02:00
.putString("multiuser_mode", String.valueOf(multiuserMode))
2017-06-08 16:27:24 +02:00
.putString("mnt_ns", String.valueOf(suNamespaceMode))
2017-08-28 19:34:42 +02:00
.putString("update_channel", String.valueOf(updateChannel))
2017-08-26 19:38:05 +02:00
.putString("locale", localeConfig)
2017-09-03 11:24:05 +02:00
.putString("boot_format", bootFormat)
2017-02-06 19:01:32 +01:00
.apply();
2017-07-13 20:27:02 +02:00
// 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);
}
2017-08-29 20:28:24 +02:00
LoadModules loadModuleTask = new LoadModules(this);
// Start update check job
2017-09-02 21:26:01 +02:00
if (hasNetwork) {
2017-08-29 20:28:24 +02:00
ComponentName service = new ComponentName(this, UpdateCheckService.class);
JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(8 * 60 * 60 * 1000)
.build();
((JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(jobInfo);
loadModuleTask.setCallBack(() -> new UpdateRepos(this).exec());
}
// Fire asynctasks
loadModuleTask.exec();
2017-02-06 19:01:32 +01:00
}
2017-08-11 19:31:34 +02:00
public void getMagiskInfo() {
List<String> ret;
2017-09-02 21:26:01 +02:00
Shell.getShell(this);
2017-08-11 19:31:34 +02:00
ret = shell.sh("su -v");
2017-02-06 19:01:32 +01:00
if (Utils.isValidShellResponse(ret)) {
suVersion = ret.get(0);
isSuClient = suVersion.toUpperCase().contains("MAGISK");
}
2017-07-17 21:34:06 +02:00
ret = shell.sh("magisk -v");
2017-02-06 19:01:32 +01:00
if (!Utils.isValidShellResponse(ret)) {
2017-07-17 21:34:06 +02:00
ret = shell.sh("getprop magisk.version");
2017-05-11 20:25:07 +02:00
if (Utils.isValidShellResponse(ret)) {
try {
magiskVersionString = ret.get(0);
magiskVersionCode = (int) Double.parseDouble(ret.get(0)) * 10;
} catch (NumberFormatException ignored) {}
}
2017-02-06 19:01:32 +01:00
} else {
2017-05-11 20:25:07 +02:00
magiskVersionString = ret.get(0).split(":")[0];
2017-07-17 21:34:06 +02:00
ret = shell.sh("magisk -V");
2017-02-06 19:01:32 +01:00
try {
2017-05-11 20:25:07 +02:00
magiskVersionCode = Integer.parseInt(ret.get(0));
} catch (NumberFormatException ignored) {}
2017-02-06 19:01:32 +01:00
}
2017-07-17 21:34:06 +02:00
ret = shell.sh("getprop " + DISABLE_INDICATION_PROP);
2017-02-06 19:01:32 +01:00
try {
disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) {
disabled = false;
}
2017-07-17 21:34:06 +02:00
ret = shell.sh("getprop " + MAGISKHIDE_PROP);
try {
2017-07-01 11:38:33 +02:00
magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) {
2017-07-01 11:38:33 +02:00
magiskHide = true;
}
2017-07-17 21:34:06 +02:00
}
2017-08-11 19:31:34 +02:00
private void updateBlockInfo() {
2017-07-17 21:34:06 +02:00
List<String> res = shell.su(
2017-08-11 19:31:34 +02:00
"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",
2017-07-17 21:34:06 +02:00
"done",
"[ ! -z \"$BOOTIMAGE\" -a -L \"$BOOTIMAGE\" ] && BOOTIMAGE=`readlink $BOOTIMAGE`",
"echo \"$BOOTIMAGE\""
);
if (Utils.isValidShellResponse(res)) {
bootBlock = res.get(0);
} else {
2017-09-02 20:45:43 +02:00
blockList = shell.su("find /dev/block -type b | grep -vE 'dm-0|ram|loop'");
2017-07-17 21:34:06 +02:00
}
2017-02-06 19:01:32 +01:00
}
}