diff --git a/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java b/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java index b179f40dd..eb37770ef 100644 --- a/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/FlashActivity.java @@ -35,7 +35,7 @@ public class FlashActivity extends Activity { @OnClick(R.id.reboot) public void reboot() { - Shell.getRootShell(this).su_raw("reboot"); + Shell.getShell(this).su_raw("reboot"); } @Override diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java index cbfb557be..5dd06f903 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java @@ -26,7 +26,6 @@ import android.widget.Spinner; import android.widget.TextView; import com.topjohnwu.magisk.asyncs.CheckUpdates; -import com.topjohnwu.magisk.asyncs.ParallelTask; import com.topjohnwu.magisk.components.AlertDialogBuilder; import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.SnackbarMaker; @@ -105,25 +104,18 @@ public class MagiskFragment extends Fragment collapse(); } - @OnClick(R.id.detect_bootimage) - public void toAutoDetect() { - if (magiskManager.bootBlock != null) { - spinner.setSelection(0); - } - } - @OnClick(R.id.install_button) public void install() { String bootImage = null; - if (magiskManager.blockList != null) { - int idx = spinner.getSelectedItemPosition(); + if (Shell.rootAccess()) { if (magiskManager.bootBlock != null) { bootImage = magiskManager.bootBlock; } else { + int idx = spinner.getSelectedItemPosition(); if (idx > 0) { bootImage = magiskManager.blockList.get(idx - 1); } else { - SnackbarMaker.make(getActivity(), R.string.manual_boot_image, Snackbar.LENGTH_LONG); + SnackbarMaker.make(getActivity(), R.string.manual_boot_image, Snackbar.LENGTH_LONG).show(); return; } } @@ -135,27 +127,32 @@ public class MagiskFragment extends Fragment .setMessage(getString(R.string.repo_install_msg, filename)) .setCancelable(true) .setPositiveButton(Shell.rootAccess() ? R.string.install : R.string.download, - (dialogInterface, i) -> Utils.dlAndReceive( - getActivity(), - new DownloadReceiver() { - private String boot = finalBootImage; - private boolean enc = keepEncChkbox.isChecked(); - private boolean verity = keepVerityChkbox.isChecked(); + (d, i) -> + Utils.dlAndReceive( + getActivity(), + new DownloadReceiver() { + private String boot = finalBootImage; + private boolean enc = keepEncChkbox.isChecked(); + private boolean verity = keepVerityChkbox.isChecked(); - @Override - public void onDownloadDone(Uri uri, Context context) { - if (Shell.rootAccess()) { - new SetInstallFlags(boot, enc, verity) - .setCallBack(() -> startActivity(new Intent(context, FlashActivity.class).setData(uri))) - .exec(context); - } else { - Utils.showUriSnack(getActivity(), uri); - } - } - }, - magiskManager.magiskLink, - Utils.getLegalFilename(filename))) - .setNeutralButton(R.string.release_notes, (dialog, which) -> { + @Override + public void onDownloadDone(Uri uri, Context context) { + if (Shell.rootAccess()) { + magiskManager.shell.su_raw( + "rm -f /dev/.magisk", + "echo \"BOOTIMAGE=" + boot + "\" >> /dev/.magisk", + "echo \"KEEPFORCEENCRYPT=" + String.valueOf(enc) + "\" >> /dev/.magisk", + "echo \"KEEPVERITY=" + String.valueOf(verity) + "\" >> /dev/.magisk" + ); + startActivity(new Intent(context, FlashActivity.class).setData(uri)); + } else { + Utils.showUriSnack(getActivity(), uri); + } + } + }, + magiskManager.magiskLink, + Utils.getLegalFilename(filename))) + .setNeutralButton(R.string.release_notes, (d, i) -> { if (magiskManager.releaseNoteLink != null) { Intent openReleaseNoteLink = new Intent(Intent.ACTION_VIEW, Uri.parse(magiskManager.releaseNoteLink)); openReleaseNoteLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -166,28 +163,6 @@ public class MagiskFragment extends Fragment .show(); } - private static class SetInstallFlags extends ParallelTask { - - private String boot; - private boolean enc, verity; - - SetInstallFlags(String boot, boolean enc, boolean verity) { - this.boot = boot; - this.enc = enc; - this.verity = verity; - } - - @Override - protected Void doInBackground(Context... contexts) { - Shell.getRootShell(contexts[0]).su_raw("rm -f /dev/.magisk", - (boot != null) ? "echo \"BOOTIMAGE=" + boot + "\" >> /dev/.magisk" : "", - "echo \"KEEPFORCEENCRYPT=" + String.valueOf(enc) + "\" >> /dev/.magisk", - "echo \"KEEPVERITY=" + String.valueOf(verity) + "\" >> /dev/.magisk" - ); - return null; - } - } - @OnClick(R.id.uninstall_button) public void uninstall() { new AlertDialogBuilder(getActivity()) @@ -225,7 +200,7 @@ public class MagiskFragment extends Fragment @Override public void onFinish() { progress.setMessage(getString(R.string.reboot_countdown, 0)); - magiskManager.rootShell.su_raw( + magiskManager.shell.su_raw( "mv -f " + uninstaller + " /cache/" + MagiskManager.UNINSTALLER, "mv -f " + utils + " /data/magisk/" + MagiskManager.UTIL_FUNCTIONS, "reboot" @@ -314,8 +289,6 @@ public class MagiskFragment extends Fragment updateCheckUI(); } else if (event == magiskManager.safetyNetDone) { updateSafetyNetUI(); - } else if (event == magiskManager.blockDetectionDone) { - updateInstallUI(); } } @@ -327,11 +300,8 @@ public class MagiskFragment extends Fragment updateCheckUI(); if (magiskManager.safetyNetDone.isTriggered) updateSafetyNetUI(); - if (magiskManager.blockDetectionDone.isTriggered || !Shell.rootAccess()) - updateInstallUI(); magiskManager.updateCheckDone.register(this); magiskManager.safetyNetDone.register(this); - magiskManager.blockDetectionDone.register(this); getActivity().setTitle(R.string.magisk); } @@ -339,7 +309,6 @@ public class MagiskFragment extends Fragment public void onStop() { magiskManager.updateCheckDone.unRegister(this); magiskManager.safetyNetDone.unRegister(this); - magiskManager.blockDetectionDone.unRegister(this); super.onStop(); } @@ -351,6 +320,8 @@ public class MagiskFragment extends Fragment private void updateUI() { ((MainActivity) getActivity()).checkHideSection(); + magiskManager.updateMagiskInfo(); + final int ROOT = 0x1, NETWORK = 0x2, UPTODATE = 0x4; int status = 0; status |= Shell.rootAccess() ? ROOT : 0; @@ -362,14 +333,9 @@ public class MagiskFragment extends Fragment installOptionCard.setVisibility(Utils.checkBits(status, NETWORK, ROOT) ? View.VISIBLE : View.GONE); installButton.setVisibility(Utils.checkBits(status, NETWORK) ? View.VISIBLE : View.GONE); uninstallButton.setVisibility(Utils.checkBits(status, UPTODATE, ROOT) ? View.VISIBLE : View.GONE); - updateVersionUI(); - } - private void updateVersionUI() { int image, color; - magiskManager.updateMagiskInfo(); - if (magiskManager.magiskVersionCode < 0) { color = colorBad; image = R.drawable.ic_cancel; @@ -405,6 +371,25 @@ public class MagiskFragment extends Fragment rootStatusIcon.setImageResource(image); rootStatusIcon.setColorFilter(color); + + if (!Shell.rootAccess()) { + installText.setText(R.string.download); + } else { + installText.setText(R.string.download_install); + + List items = new ArrayList<>(); + if (magiskManager.bootBlock != null) { + items.add(getString(R.string.auto_detect, magiskManager.bootBlock)); + spinner.setEnabled(false); + } else { + items.add(getString(R.string.cannot_auto_detect)); + items.addAll(magiskManager.blockList); + } + ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), + android.R.layout.simple_spinner_item, items); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + } } private void updateCheckUI() { @@ -428,28 +413,6 @@ public class MagiskFragment extends Fragment mSwipeRefreshLayout.setRefreshing(false); } - private void updateInstallUI() { - if (!Shell.rootAccess()) { - installText.setText(R.string.download); - } else { - installText.setText(R.string.download_install); - - List items = new ArrayList<>(); - if (magiskManager.bootBlock != null) { - items.add(getString(R.string.auto_detect, magiskManager.bootBlock)); - spinner.setEnabled(false); - } else { - items.add(getString(R.string.cannot_auto_detect)); - items.addAll(magiskManager.blockList); - } - ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), - android.R.layout.simple_spinner_item, items); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - spinner.setAdapter(adapter); - toAutoDetect(); - } - } - private void updateSafetyNetUI() { int image, color; safetyNetProgress.setVisibility(View.GONE); diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java index 081bf14d5..92b6872c0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java @@ -142,7 +142,7 @@ public class MagiskLogFragment extends Fragment { mode = (int) params[0]; switch (mode) { case 0: - List logList = Utils.readFile(magiskManager.rootShell, MAGISK_LOG); + List logList = Utils.readFile(magiskManager.shell, MAGISK_LOG); if (Utils.isValidShellResponse(logList)) { StringBuilder llog = new StringBuilder(15 * 10 * 1024); @@ -154,7 +154,7 @@ public class MagiskLogFragment extends Fragment { return ""; case 1: - magiskManager.rootShell.su_raw("echo > " + MAGISK_LOG); + magiskManager.shell.su_raw("echo > " + MAGISK_LOG); SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); return ""; @@ -184,7 +184,7 @@ public class MagiskLogFragment extends Fragment { return false; } - List in = Utils.readFile(magiskManager.rootShell, MAGISK_LOG); + List in = Utils.readFile(magiskManager.shell, MAGISK_LOG); if (Utils.isValidShellResponse(in)) { try (FileWriter out = new FileWriter(targetFile)) { diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java b/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java index d41a0dee8..f9fc54b85 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java +++ b/app/src/main/java/com/topjohnwu/magisk/MagiskManager.java @@ -38,7 +38,6 @@ public class MagiskManager extends Application { public static final String NOTIFICATION_CHANNEL = "magisk_update_notice"; // Events - public final CallbackEvent blockDetectionDone = new CallbackEvent<>(); public final CallbackEvent magiskHideDone = new CallbackEvent<>(); public final CallbackEvent reloadMainActivity = new CallbackEvent<>(); public final CallbackEvent moduleLoadDone = new CallbackEvent<>(); @@ -88,7 +87,7 @@ public class MagiskManager extends Application { // Global resources public SharedPreferences prefs; public SuDatabaseHelper suDB; - public Shell rootShell; + public Shell shell; private static Handler mHandler = new Handler(); @@ -97,7 +96,7 @@ public class MagiskManager extends Application { super.onCreate(); new File(getApplicationInfo().dataDir).mkdirs(); /* Create the app data directory */ prefs = PreferenceManager.getDefaultSharedPreferences(this); - rootShell = Shell.getRootShell(); + shell = Shell.getShell(); } public void toast(String msg, int duration) { @@ -121,11 +120,12 @@ public class MagiskManager extends Application { updateNotification = prefs.getBoolean("notification", true); initSU(); updateMagiskInfo(); + updateBlockInfo(); // Initialize busybox File busybox = new File(getApplicationInfo().dataDir + "/busybox/busybox"); if (!busybox.exists() || !TextUtils.equals(prefs.getString("busybox_version", ""), BUSYBOX_VERSION)) { busybox.getParentFile().mkdirs(); - rootShell.su_raw( + shell.su_raw( "cp -f " + new File(getApplicationInfo().nativeLibraryDir, "libbusybox.so") + " " + busybox, "chmod -R 755 " + busybox.getParent(), busybox + " --install -s " + busybox.getParent() @@ -137,7 +137,7 @@ public class MagiskManager extends Application { .putBoolean("magiskhide", magiskHide) .putBoolean("notification", updateNotification) .putBoolean("hosts", new File("/magisk/.core/hosts").exists()) - .putBoolean("disable", Utils.itemExist(rootShell, MAGISK_DISABLE_FILE)) + .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)) @@ -148,7 +148,7 @@ public class MagiskManager extends Application { .putString("busybox_version", BUSYBOX_VERSION) .apply(); // Add busybox to PATH - rootShell.su_raw("PATH=$PATH:" + busybox.getParent()); + shell.su_raw("PATH=$PATH:" + busybox.getParent()); // Create notification channel on Android O if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -170,7 +170,7 @@ public class MagiskManager extends Application { public void initSU() { initSUConfig(); - List ret = Shell.sh("su -v"); + List ret = shell.sh("su -v"); if (Utils.isValidShellResponse(ret)) { suVersion = ret.get(0); isSuClient = suVersion.toUpperCase().contains("MAGISK"); @@ -184,9 +184,9 @@ public class MagiskManager extends Application { public void updateMagiskInfo() { List ret; - ret = Shell.sh("magisk -v"); + ret = shell.sh("magisk -v"); if (!Utils.isValidShellResponse(ret)) { - ret = Shell.sh("getprop magisk.version"); + ret = shell.sh("getprop magisk.version"); if (Utils.isValidShellResponse(ret)) { try { magiskVersionString = ret.get(0); @@ -195,24 +195,39 @@ public class MagiskManager extends Application { } } else { magiskVersionString = ret.get(0).split(":")[0]; - ret = Shell.sh("magisk -V"); + ret = shell.sh("magisk -V"); try { magiskVersionCode = Integer.parseInt(ret.get(0)); } catch (NumberFormatException ignored) {} } - ret = Shell.sh("getprop " + DISABLE_INDICATION_PROP); + 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); + ret = shell.sh("getprop " + MAGISKHIDE_PROP); try { magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0; } catch (NumberFormatException e) { magiskHide = true; } - + } + + public void updateBlockInfo() { + List res = shell.su( + "for BLOCK in boot_a BOOT_A kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do", + "BOOTIMAGE=`ls /dev/block/by-name/$BLOCK || ls /dev/block/platform/*/by-name/$BLOCK || ls /dev/block/platform/*/*/by-name/$BLOCK` 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"); + } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java index 8e2a42ee7..82559d742 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java @@ -149,9 +149,9 @@ public class SettingsActivity extends Activity { case "disable": enabled = prefs.getBoolean("disable", false); if (enabled) { - Utils.createFile(magiskManager.rootShell, MagiskManager.MAGISK_DISABLE_FILE); + Utils.createFile(magiskManager.shell, MagiskManager.MAGISK_DISABLE_FILE); } else { - Utils.removeItem(magiskManager.rootShell, MagiskManager.MAGISK_DISABLE_FILE); + Utils.removeItem(magiskManager.shell, MagiskManager.MAGISK_DISABLE_FILE); } Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show(); break; @@ -175,11 +175,11 @@ public class SettingsActivity extends Activity { case "hosts": enabled = prefs.getBoolean("hosts", false); if (enabled) { - magiskManager.rootShell.su_raw( + magiskManager.shell.su_raw( "cp -af /system/etc/hosts /magisk/.core/hosts", "mount -o bind /magisk/.core/hosts /system/etc/hosts"); } else { - magiskManager.rootShell.su_raw( + magiskManager.shell.su_raw( "umount -l /system/etc/hosts", "rm -f /magisk/.core/hosts"); } diff --git a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java b/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java index 36174ce38..e0e5b43f7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/SplashActivity.java @@ -8,7 +8,6 @@ import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; -import com.topjohnwu.magisk.asyncs.GetBootBlocks; import com.topjohnwu.magisk.asyncs.LoadApps; import com.topjohnwu.magisk.asyncs.LoadModules; import com.topjohnwu.magisk.asyncs.LoadRepos; @@ -25,12 +24,13 @@ public class SplashActivity extends Activity{ super.onCreate(savedInstanceState); - // Init the info and configs and root shell + // Init the info and configs and root sh getApplicationContext().init(); // Now fire all async tasks - new GetBootBlocks(this).exec(); - new LoadModules(this).setCallBack(() -> new LoadRepos(this).exec()).exec(); + new LoadModules(this) + .setCallBack(() -> new LoadRepos(this).exec()) + .exec(); new LoadApps(this).exec(); if (Utils.checkNetworkStatus(this)) { diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java index e35d19818..eaebdaa3a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/adapters/ModulesAdapter.java @@ -38,7 +38,7 @@ public class ModulesAdapter extends RecyclerView.Adapter { private boolean unzipAndCheck() throws Exception { ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android"); - List ret = Utils.readFile(magiskManager.rootShell, mCheckFile.getPath()); + List ret = Utils.readFile(magiskManager.shell, mCheckFile.getPath()); return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK"); } @@ -84,7 +84,7 @@ public class FlashZip extends ParallelTask { copyToCache(); if (!unzipAndCheck()) return 0; mList.add(magiskManager.getString(R.string.zip_install_progress_msg, mFilename)); - magiskManager.rootShell.su(mList, + magiskManager.shell.su(mList, "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile + " && echo 'Success!' || echo 'Failed!'" ); @@ -99,7 +99,7 @@ public class FlashZip extends ParallelTask { // -1 = error, manual install; 0 = invalid zip; 1 = success @Override protected void onPostExecute(Integer result) { - magiskManager.rootShell.su_raw( + magiskManager.shell.su_raw( "rm -rf " + mCachedFile.getParent() + "/*", "rm -rf " + MagiskManager.TMP_FOLDER_PATH ); diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/GetBootBlocks.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/GetBootBlocks.java deleted file mode 100644 index 0a83aac1b..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/asyncs/GetBootBlocks.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.topjohnwu.magisk.asyncs; - -import android.app.Activity; - -import com.topjohnwu.magisk.utils.Utils; - -public class GetBootBlocks extends ParallelTask { - - public GetBootBlocks(Activity context) { - super(context); - } - - @Override - protected Void doInBackground(Void... params) { - magiskManager.blockList = magiskManager.rootShell.su( - "find /dev/block -type b -maxdepth 1 | grep -v -E \"loop|ram|dm-0\"" - ); - if (magiskManager.bootBlock == null) { - magiskManager.bootBlock = Utils.detectBootImage(magiskManager.rootShell); - } - return null; - } - - @Override - protected void onPostExecute(Void v) { - magiskManager.blockDetectionDone.trigger(); - super.onPostExecute(v); - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java index 2ada5ba51..18911d6a3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java +++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/LoadModules.java @@ -21,10 +21,10 @@ public class LoadModules extends ParallelTask { magiskManager.moduleMap = new ValueSortedMap<>(); - for (String path : Utils.getModList(magiskManager.rootShell, MagiskManager.MAGISK_PATH)) { + for (String path : Utils.getModList(magiskManager.shell, MagiskManager.MAGISK_PATH)) { Logger.dev("LoadModules: Adding modules from " + path); try { - Module module = new Module(magiskManager.rootShell, path); + Module module = new Module(magiskManager.shell, path); magiskManager.moduleMap.put(module.getId(), module); } catch (BaseModule.CacheModException ignored) {} } diff --git a/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java b/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java index b0fdbe712..9bd1f2f34 100644 --- a/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java +++ b/app/src/main/java/com/topjohnwu/magisk/asyncs/MagiskHide.java @@ -15,7 +15,7 @@ public class MagiskHide extends ParallelTask { @Override protected Void doInBackground(Object... params) { String command = (String) params[0]; - List ret = magiskManager.rootShell.su("magiskhide --" + command); + List ret = magiskManager.shell.su("magiskhide --" + command); if (isList) { magiskManager.magiskHideList = ret; } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java b/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java index fae15417b..b187f30f7 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Logger.java @@ -37,13 +37,13 @@ public class Logger { dev(String.format(Locale.US, fmt, args)); } - public static void shell(boolean root, String line) { + public static void shell(String line) { if (MagiskManager.shellLogging) { - Log.d(DEBUG_TAG, (root ? "MANAGERSU: " : "MANAGERSH: ") + line); + Log.d(DEBUG_TAG, "SHELL: " + line); } } - public static void shell(boolean root, String fmt, Object... args) { - shell(root, String.format(Locale.US, fmt, args)); + public static void shell(String fmt, Object... args) { + shell(String.format(Locale.US, fmt, args)); } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Shell.java b/app/src/main/java/com/topjohnwu/magisk/utils/Shell.java index 1aa3fe995..b3383858c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Shell.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Shell.java @@ -2,11 +2,12 @@ package com.topjohnwu.magisk.utils; import android.content.Context; +import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** @@ -18,145 +19,133 @@ public class Shell { // -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted public static int rootStatus; - private final Process rootShell; - private final DataOutputStream rootSTDIN; - private final DataInputStream rootSTDOUT; + private final Process shellProcess; + private final DataOutputStream STDIN; + private final DataInputStream STDOUT; private boolean isValid; private Shell() { - Process process; + rootStatus = 1; + Process process = null; + DataOutputStream in = null; + DataInputStream out = null; + try { process = Runtime.getRuntime().exec("su"); + in = new DataOutputStream(process.getOutputStream()); + out = new DataInputStream(process.getInputStream()); } catch (IOException e) { - // No root rootStatus = 0; - rootShell = null; - rootSTDIN = null; - rootSTDOUT = null; - isValid = false; - return; } - rootStatus = 1; - isValid = true; - rootShell = process; - rootSTDIN = new DataOutputStream(rootShell.getOutputStream()); - rootSTDOUT = new DataInputStream(rootShell.getInputStream()); - - su_raw("umask 022"); - List ret = su("echo -BOC-", "id"); - - if (ret.isEmpty()) { - // Something wrong with root, not allowed? - rootStatus = -1; - return; - } - - for (String line : ret) { - if (line.contains("uid=")) { - // id command is working, let's see if we are actually root - rootStatus = line.contains("uid=0") ? 1 : -1; - return; - } else if (!line.contains("-BOC-")) { - rootStatus = -1; - return; + while (true) { + if (rootAccess()) { + try { + in.write(("id\n").getBytes("UTF-8")); + in.flush(); + String s = new BufferedReader(new InputStreamReader(out)).readLine(); + if (s.isEmpty() || !s.contains("uid=0")) { + in.close(); + out.close(); + process.destroy(); + throw new IOException(); + } + } catch (IOException e) { + rootStatus = -1; + continue; + } + break; + } else { + // Try to gain non-root sh + try { + process = Runtime.getRuntime().exec("sh"); + in = new DataOutputStream(process.getOutputStream()); + out = new DataInputStream(process.getInputStream()); + } catch (IOException e) { + // Nothing works.... + shellProcess = null; + STDIN = null; + STDOUT = null; + isValid = false; + return; + } + break; } } + + isValid = true; + shellProcess = process; + STDIN = in; + STDOUT = out; + sh_raw("umask 022"); } - public static Shell getRootShell() { + public static Shell getShell() { return new Shell(); } - public static Shell getRootShell(Context context) { - return Utils.getMagiskManager(context).rootShell; + public static Shell getShell(Context context) { + return Utils.getMagiskManager(context).shell; } public static boolean rootAccess() { return rootStatus > 0; } - public static List sh(String... commands) { - List res = Collections.synchronizedList(new ArrayList()); - - try { - Process process = Runtime.getRuntime().exec("sh"); - DataOutputStream STDIN = new DataOutputStream(process.getOutputStream()); - StreamGobbler STDOUT = new StreamGobbler(process.getInputStream(), res); - - STDOUT.start(); - - try { - for (String write : commands) { - STDIN.write((write + "\n").getBytes("UTF-8")); - STDIN.flush(); - Logger.shell(false, write); - } - STDIN.write("exit\n".getBytes("UTF-8")); - STDIN.flush(); - } catch (IOException e) { - if (!e.getMessage().contains("EPIPE")) { - throw e; - } - } - - process.waitFor(); - - try { - STDIN.close(); - } catch (IOException e) { - // might be closed already - } - STDOUT.join(); - process.destroy(); - - } catch (IOException | InterruptedException e) { - // shell probably not found - res = null; - } - - return res; - } - - public List su(String... commands) { - if (!isValid) return null; + public List sh(String... commands) { List res = new ArrayList<>(); - su(res, commands); + if (!isValid) return res; + sh(res, commands); return res; } - public void su_raw(String... commands) { + public void sh_raw(String... commands) { if (!isValid) return; - synchronized (rootShell) { + synchronized (shellProcess) { try { for (String command : commands) { - rootSTDIN.write((command + "\n").getBytes("UTF-8")); - rootSTDIN.flush(); + STDIN.write((command + "\n").getBytes("UTF-8")); + STDIN.flush(); } } catch (IOException e) { e.printStackTrace(); - rootShell.destroy(); + shellProcess.destroy(); isValid = false; } } } - public void su(List output, String... commands) { + public void sh(List output, String... commands) { if (!isValid) return; try { - rootShell.exitValue(); + shellProcess.exitValue(); isValid = false; return; // The process is dead, return } catch (IllegalThreadStateException ignored) { // This should be the expected result } - synchronized (rootShell) { - StreamGobbler STDOUT = new StreamGobbler(rootSTDOUT, output, true); - STDOUT.start(); - su_raw(commands); - su_raw("echo \'-root-done-\'"); - try { STDOUT.join(); } catch (InterruptedException ignored) {} + synchronized (shellProcess) { + StreamGobbler out = new StreamGobbler(this.STDOUT, output); + out.start(); + sh_raw(commands); + sh_raw("echo \'-shell-done-\'"); + try { out.join(); } catch (InterruptedException ignored) {} } } + + public List su(String... commands) { + if (!rootAccess()) return sh(); + return sh(commands); + } + + public void su_raw(String... commands) { + if (!rootAccess()) return; + sh_raw(commands); + } + + public void su(List output, String... commands) { + if (!rootAccess()) return; + sh(output, commands); + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java b/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java index e4178f8ad..014407b49 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java @@ -21,7 +21,7 @@ public class StreamGobbler extends Thread { /** *

StreamGobbler constructor

* - *

We use this class because shell STDOUT and STDERR should be read as quickly as + *

We use this class because sh STDOUT and STDERR should be read as quickly as * possible to prevent a deadlock from occurring, or Process.waitFor() never * returning (as the buffer is full, pausing the native process)

* @@ -38,21 +38,16 @@ public class StreamGobbler extends Thread { writer = outputList; } - public StreamGobbler(InputStream inputStream, List outputList, boolean root) { - this(inputStream, outputList); - isRoot = root; - } - @Override public void run() { // keep reading the InputStream until it ends (or an error occurs) try { String line; while ((line = reader.readLine()) != null) { - if (TextUtils.equals(line, "-root-done-")) + if (TextUtils.equals(line, "-shell-done-")) return; writer.add(line); - Logger.shell(isRoot, line); + Logger.shell(line); } } catch (IOException e) { // reader probably closed, expected exit condition