Merge shells

This commit is contained in:
topjohnwu 2017-07-18 03:34:06 +08:00
parent 2ea046cd80
commit 6933bcf7bb
14 changed files with 188 additions and 255 deletions

View File

@ -35,7 +35,7 @@ public class FlashActivity extends Activity {
@OnClick(R.id.reboot) @OnClick(R.id.reboot)
public void reboot() { public void reboot() {
Shell.getRootShell(this).su_raw("reboot"); Shell.getShell(this).su_raw("reboot");
} }
@Override @Override

View File

@ -26,7 +26,6 @@ import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.CheckUpdates; import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.components.AlertDialogBuilder; import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.Fragment; import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.components.SnackbarMaker; import com.topjohnwu.magisk.components.SnackbarMaker;
@ -105,25 +104,18 @@ public class MagiskFragment extends Fragment
collapse(); collapse();
} }
@OnClick(R.id.detect_bootimage)
public void toAutoDetect() {
if (magiskManager.bootBlock != null) {
spinner.setSelection(0);
}
}
@OnClick(R.id.install_button) @OnClick(R.id.install_button)
public void install() { public void install() {
String bootImage = null; String bootImage = null;
if (magiskManager.blockList != null) { if (Shell.rootAccess()) {
int idx = spinner.getSelectedItemPosition();
if (magiskManager.bootBlock != null) { if (magiskManager.bootBlock != null) {
bootImage = magiskManager.bootBlock; bootImage = magiskManager.bootBlock;
} else { } else {
int idx = spinner.getSelectedItemPosition();
if (idx > 0) { if (idx > 0) {
bootImage = magiskManager.blockList.get(idx - 1); bootImage = magiskManager.blockList.get(idx - 1);
} else { } 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; return;
} }
} }
@ -135,27 +127,32 @@ public class MagiskFragment extends Fragment
.setMessage(getString(R.string.repo_install_msg, filename)) .setMessage(getString(R.string.repo_install_msg, filename))
.setCancelable(true) .setCancelable(true)
.setPositiveButton(Shell.rootAccess() ? R.string.install : R.string.download, .setPositiveButton(Shell.rootAccess() ? R.string.install : R.string.download,
(dialogInterface, i) -> Utils.dlAndReceive( (d, i) ->
getActivity(), Utils.dlAndReceive(
new DownloadReceiver() { getActivity(),
private String boot = finalBootImage; new DownloadReceiver() {
private boolean enc = keepEncChkbox.isChecked(); private String boot = finalBootImage;
private boolean verity = keepVerityChkbox.isChecked(); private boolean enc = keepEncChkbox.isChecked();
private boolean verity = keepVerityChkbox.isChecked();
@Override @Override
public void onDownloadDone(Uri uri, Context context) { public void onDownloadDone(Uri uri, Context context) {
if (Shell.rootAccess()) { if (Shell.rootAccess()) {
new SetInstallFlags(boot, enc, verity) magiskManager.shell.su_raw(
.setCallBack(() -> startActivity(new Intent(context, FlashActivity.class).setData(uri))) "rm -f /dev/.magisk",
.exec(context); "echo \"BOOTIMAGE=" + boot + "\" >> /dev/.magisk",
} else { "echo \"KEEPFORCEENCRYPT=" + String.valueOf(enc) + "\" >> /dev/.magisk",
Utils.showUriSnack(getActivity(), uri); "echo \"KEEPVERITY=" + String.valueOf(verity) + "\" >> /dev/.magisk"
} );
} startActivity(new Intent(context, FlashActivity.class).setData(uri));
}, } else {
magiskManager.magiskLink, Utils.showUriSnack(getActivity(), uri);
Utils.getLegalFilename(filename))) }
.setNeutralButton(R.string.release_notes, (dialog, which) -> { }
},
magiskManager.magiskLink,
Utils.getLegalFilename(filename)))
.setNeutralButton(R.string.release_notes, (d, i) -> {
if (magiskManager.releaseNoteLink != null) { if (magiskManager.releaseNoteLink != null) {
Intent openReleaseNoteLink = new Intent(Intent.ACTION_VIEW, Uri.parse(magiskManager.releaseNoteLink)); Intent openReleaseNoteLink = new Intent(Intent.ACTION_VIEW, Uri.parse(magiskManager.releaseNoteLink));
openReleaseNoteLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); openReleaseNoteLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -166,28 +163,6 @@ public class MagiskFragment extends Fragment
.show(); .show();
} }
private static class SetInstallFlags extends ParallelTask<Context, Void, Void> {
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) @OnClick(R.id.uninstall_button)
public void uninstall() { public void uninstall() {
new AlertDialogBuilder(getActivity()) new AlertDialogBuilder(getActivity())
@ -225,7 +200,7 @@ public class MagiskFragment extends Fragment
@Override @Override
public void onFinish() { public void onFinish() {
progress.setMessage(getString(R.string.reboot_countdown, 0)); progress.setMessage(getString(R.string.reboot_countdown, 0));
magiskManager.rootShell.su_raw( magiskManager.shell.su_raw(
"mv -f " + uninstaller + " /cache/" + MagiskManager.UNINSTALLER, "mv -f " + uninstaller + " /cache/" + MagiskManager.UNINSTALLER,
"mv -f " + utils + " /data/magisk/" + MagiskManager.UTIL_FUNCTIONS, "mv -f " + utils + " /data/magisk/" + MagiskManager.UTIL_FUNCTIONS,
"reboot" "reboot"
@ -314,8 +289,6 @@ public class MagiskFragment extends Fragment
updateCheckUI(); updateCheckUI();
} else if (event == magiskManager.safetyNetDone) { } else if (event == magiskManager.safetyNetDone) {
updateSafetyNetUI(); updateSafetyNetUI();
} else if (event == magiskManager.blockDetectionDone) {
updateInstallUI();
} }
} }
@ -327,11 +300,8 @@ public class MagiskFragment extends Fragment
updateCheckUI(); updateCheckUI();
if (magiskManager.safetyNetDone.isTriggered) if (magiskManager.safetyNetDone.isTriggered)
updateSafetyNetUI(); updateSafetyNetUI();
if (magiskManager.blockDetectionDone.isTriggered || !Shell.rootAccess())
updateInstallUI();
magiskManager.updateCheckDone.register(this); magiskManager.updateCheckDone.register(this);
magiskManager.safetyNetDone.register(this); magiskManager.safetyNetDone.register(this);
magiskManager.blockDetectionDone.register(this);
getActivity().setTitle(R.string.magisk); getActivity().setTitle(R.string.magisk);
} }
@ -339,7 +309,6 @@ public class MagiskFragment extends Fragment
public void onStop() { public void onStop() {
magiskManager.updateCheckDone.unRegister(this); magiskManager.updateCheckDone.unRegister(this);
magiskManager.safetyNetDone.unRegister(this); magiskManager.safetyNetDone.unRegister(this);
magiskManager.blockDetectionDone.unRegister(this);
super.onStop(); super.onStop();
} }
@ -351,6 +320,8 @@ public class MagiskFragment extends Fragment
private void updateUI() { private void updateUI() {
((MainActivity) getActivity()).checkHideSection(); ((MainActivity) getActivity()).checkHideSection();
magiskManager.updateMagiskInfo();
final int ROOT = 0x1, NETWORK = 0x2, UPTODATE = 0x4; final int ROOT = 0x1, NETWORK = 0x2, UPTODATE = 0x4;
int status = 0; int status = 0;
status |= Shell.rootAccess() ? ROOT : 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); installOptionCard.setVisibility(Utils.checkBits(status, NETWORK, ROOT) ? View.VISIBLE : View.GONE);
installButton.setVisibility(Utils.checkBits(status, NETWORK) ? 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); uninstallButton.setVisibility(Utils.checkBits(status, UPTODATE, ROOT) ? View.VISIBLE : View.GONE);
updateVersionUI();
}
private void updateVersionUI() {
int image, color; int image, color;
magiskManager.updateMagiskInfo();
if (magiskManager.magiskVersionCode < 0) { if (magiskManager.magiskVersionCode < 0) {
color = colorBad; color = colorBad;
image = R.drawable.ic_cancel; image = R.drawable.ic_cancel;
@ -405,6 +371,25 @@ public class MagiskFragment extends Fragment
rootStatusIcon.setImageResource(image); rootStatusIcon.setImageResource(image);
rootStatusIcon.setColorFilter(color); rootStatusIcon.setColorFilter(color);
if (!Shell.rootAccess()) {
installText.setText(R.string.download);
} else {
installText.setText(R.string.download_install);
List<String> 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<String> 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() { private void updateCheckUI() {
@ -428,28 +413,6 @@ public class MagiskFragment extends Fragment
mSwipeRefreshLayout.setRefreshing(false); mSwipeRefreshLayout.setRefreshing(false);
} }
private void updateInstallUI() {
if (!Shell.rootAccess()) {
installText.setText(R.string.download);
} else {
installText.setText(R.string.download_install);
List<String> 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<String> 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() { private void updateSafetyNetUI() {
int image, color; int image, color;
safetyNetProgress.setVisibility(View.GONE); safetyNetProgress.setVisibility(View.GONE);

View File

@ -142,7 +142,7 @@ public class MagiskLogFragment extends Fragment {
mode = (int) params[0]; mode = (int) params[0];
switch (mode) { switch (mode) {
case 0: case 0:
List<String> logList = Utils.readFile(magiskManager.rootShell, MAGISK_LOG); List<String> logList = Utils.readFile(magiskManager.shell, MAGISK_LOG);
if (Utils.isValidShellResponse(logList)) { if (Utils.isValidShellResponse(logList)) {
StringBuilder llog = new StringBuilder(15 * 10 * 1024); StringBuilder llog = new StringBuilder(15 * 10 * 1024);
@ -154,7 +154,7 @@ public class MagiskLogFragment extends Fragment {
return ""; return "";
case 1: 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(); SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return ""; return "";
@ -184,7 +184,7 @@ public class MagiskLogFragment extends Fragment {
return false; return false;
} }
List<String> in = Utils.readFile(magiskManager.rootShell, MAGISK_LOG); List<String> in = Utils.readFile(magiskManager.shell, MAGISK_LOG);
if (Utils.isValidShellResponse(in)) { if (Utils.isValidShellResponse(in)) {
try (FileWriter out = new FileWriter(targetFile)) { try (FileWriter out = new FileWriter(targetFile)) {

View File

@ -38,7 +38,6 @@ public class MagiskManager extends Application {
public static final String NOTIFICATION_CHANNEL = "magisk_update_notice"; public static final String NOTIFICATION_CHANNEL = "magisk_update_notice";
// Events // Events
public final CallbackEvent<Void> blockDetectionDone = new CallbackEvent<>();
public final CallbackEvent<Void> magiskHideDone = new CallbackEvent<>(); public final CallbackEvent<Void> magiskHideDone = new CallbackEvent<>();
public final CallbackEvent<Void> reloadMainActivity = new CallbackEvent<>(); public final CallbackEvent<Void> reloadMainActivity = new CallbackEvent<>();
public final CallbackEvent<Void> moduleLoadDone = new CallbackEvent<>(); public final CallbackEvent<Void> moduleLoadDone = new CallbackEvent<>();
@ -88,7 +87,7 @@ public class MagiskManager extends Application {
// Global resources // Global resources
public SharedPreferences prefs; public SharedPreferences prefs;
public SuDatabaseHelper suDB; public SuDatabaseHelper suDB;
public Shell rootShell; public Shell shell;
private static Handler mHandler = new Handler(); private static Handler mHandler = new Handler();
@ -97,7 +96,7 @@ public class MagiskManager extends Application {
super.onCreate(); super.onCreate();
new File(getApplicationInfo().dataDir).mkdirs(); /* Create the app data directory */ new File(getApplicationInfo().dataDir).mkdirs(); /* Create the app data directory */
prefs = PreferenceManager.getDefaultSharedPreferences(this); prefs = PreferenceManager.getDefaultSharedPreferences(this);
rootShell = Shell.getRootShell(); shell = Shell.getShell();
} }
public void toast(String msg, int duration) { public void toast(String msg, int duration) {
@ -121,11 +120,12 @@ public class MagiskManager extends Application {
updateNotification = prefs.getBoolean("notification", true); updateNotification = prefs.getBoolean("notification", true);
initSU(); initSU();
updateMagiskInfo(); updateMagiskInfo();
updateBlockInfo();
// Initialize busybox // Initialize busybox
File busybox = new File(getApplicationInfo().dataDir + "/busybox/busybox"); File busybox = new File(getApplicationInfo().dataDir + "/busybox/busybox");
if (!busybox.exists() || !TextUtils.equals(prefs.getString("busybox_version", ""), BUSYBOX_VERSION)) { if (!busybox.exists() || !TextUtils.equals(prefs.getString("busybox_version", ""), BUSYBOX_VERSION)) {
busybox.getParentFile().mkdirs(); busybox.getParentFile().mkdirs();
rootShell.su_raw( shell.su_raw(
"cp -f " + new File(getApplicationInfo().nativeLibraryDir, "libbusybox.so") + " " + busybox, "cp -f " + new File(getApplicationInfo().nativeLibraryDir, "libbusybox.so") + " " + busybox,
"chmod -R 755 " + busybox.getParent(), "chmod -R 755 " + busybox.getParent(),
busybox + " --install -s " + busybox.getParent() busybox + " --install -s " + busybox.getParent()
@ -137,7 +137,7 @@ public class MagiskManager extends Application {
.putBoolean("magiskhide", magiskHide) .putBoolean("magiskhide", magiskHide)
.putBoolean("notification", updateNotification) .putBoolean("notification", updateNotification)
.putBoolean("hosts", new File("/magisk/.core/hosts").exists()) .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) .putBoolean("su_reauth", suReauth)
.putString("su_request_timeout", String.valueOf(suRequestTimeout)) .putString("su_request_timeout", String.valueOf(suRequestTimeout))
.putString("su_auto_response", String.valueOf(suResponseType)) .putString("su_auto_response", String.valueOf(suResponseType))
@ -148,7 +148,7 @@ public class MagiskManager extends Application {
.putString("busybox_version", BUSYBOX_VERSION) .putString("busybox_version", BUSYBOX_VERSION)
.apply(); .apply();
// Add busybox to PATH // Add busybox to PATH
rootShell.su_raw("PATH=$PATH:" + busybox.getParent()); shell.su_raw("PATH=$PATH:" + busybox.getParent());
// Create notification channel on Android O // Create notification channel on Android O
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -170,7 +170,7 @@ public class MagiskManager extends Application {
public void initSU() { public void initSU() {
initSUConfig(); initSUConfig();
List<String> ret = Shell.sh("su -v"); List<String> ret = shell.sh("su -v");
if (Utils.isValidShellResponse(ret)) { if (Utils.isValidShellResponse(ret)) {
suVersion = ret.get(0); suVersion = ret.get(0);
isSuClient = suVersion.toUpperCase().contains("MAGISK"); isSuClient = suVersion.toUpperCase().contains("MAGISK");
@ -184,9 +184,9 @@ public class MagiskManager extends Application {
public void updateMagiskInfo() { public void updateMagiskInfo() {
List<String> ret; List<String> ret;
ret = Shell.sh("magisk -v"); ret = shell.sh("magisk -v");
if (!Utils.isValidShellResponse(ret)) { if (!Utils.isValidShellResponse(ret)) {
ret = Shell.sh("getprop magisk.version"); ret = shell.sh("getprop magisk.version");
if (Utils.isValidShellResponse(ret)) { if (Utils.isValidShellResponse(ret)) {
try { try {
magiskVersionString = ret.get(0); magiskVersionString = ret.get(0);
@ -195,24 +195,39 @@ public class MagiskManager extends Application {
} }
} else { } else {
magiskVersionString = ret.get(0).split(":")[0]; magiskVersionString = ret.get(0).split(":")[0];
ret = Shell.sh("magisk -V"); ret = shell.sh("magisk -V");
try { try {
magiskVersionCode = Integer.parseInt(ret.get(0)); magiskVersionCode = Integer.parseInt(ret.get(0));
} catch (NumberFormatException ignored) {} } catch (NumberFormatException ignored) {}
} }
ret = Shell.sh("getprop " + DISABLE_INDICATION_PROP); ret = shell.sh("getprop " + DISABLE_INDICATION_PROP);
try { try {
disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0; disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
disabled = false; disabled = false;
} }
ret = Shell.sh("getprop " + MAGISKHIDE_PROP); ret = shell.sh("getprop " + MAGISKHIDE_PROP);
try { try {
magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0; magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
magiskHide = true; magiskHide = true;
} }
}
public void updateBlockInfo() {
List<String> 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");
}
} }
} }

View File

@ -149,9 +149,9 @@ public class SettingsActivity extends Activity {
case "disable": case "disable":
enabled = prefs.getBoolean("disable", false); enabled = prefs.getBoolean("disable", false);
if (enabled) { if (enabled) {
Utils.createFile(magiskManager.rootShell, MagiskManager.MAGISK_DISABLE_FILE); Utils.createFile(magiskManager.shell, MagiskManager.MAGISK_DISABLE_FILE);
} else { } 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(); Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break; break;
@ -175,11 +175,11 @@ public class SettingsActivity extends Activity {
case "hosts": case "hosts":
enabled = prefs.getBoolean("hosts", false); enabled = prefs.getBoolean("hosts", false);
if (enabled) { if (enabled) {
magiskManager.rootShell.su_raw( magiskManager.shell.su_raw(
"cp -af /system/etc/hosts /magisk/.core/hosts", "cp -af /system/etc/hosts /magisk/.core/hosts",
"mount -o bind /magisk/.core/hosts /system/etc/hosts"); "mount -o bind /magisk/.core/hosts /system/etc/hosts");
} else { } else {
magiskManager.rootShell.su_raw( magiskManager.shell.su_raw(
"umount -l /system/etc/hosts", "umount -l /system/etc/hosts",
"rm -f /magisk/.core/hosts"); "rm -f /magisk/.core/hosts");
} }

View File

@ -8,7 +8,6 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import com.topjohnwu.magisk.asyncs.GetBootBlocks;
import com.topjohnwu.magisk.asyncs.LoadApps; import com.topjohnwu.magisk.asyncs.LoadApps;
import com.topjohnwu.magisk.asyncs.LoadModules; import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.asyncs.LoadRepos; import com.topjohnwu.magisk.asyncs.LoadRepos;
@ -25,12 +24,13 @@ public class SplashActivity extends Activity{
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// Init the info and configs and root shell // Init the info and configs and root sh
getApplicationContext().init(); getApplicationContext().init();
// Now fire all async tasks // Now fire all async tasks
new GetBootBlocks(this).exec(); new LoadModules(this)
new LoadModules(this).setCallBack(() -> new LoadRepos(this).exec()).exec(); .setCallBack(() -> new LoadRepos(this).exec())
.exec();
new LoadApps(this).exec(); new LoadApps(this).exec();
if (Utils.checkNetworkStatus(this)) { if (Utils.checkNetworkStatus(this)) {

View File

@ -38,7 +38,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override @Override
public void onBindViewHolder(final ViewHolder holder, int position) { public void onBindViewHolder(final ViewHolder holder, int position) {
Context context = holder.itemView.getContext(); Context context = holder.itemView.getContext();
Shell rootShell = Shell.getRootShell(context); Shell rootShell = Shell.getShell(context);
final Module module = mList.get(position); final Module module = mList.get(position);
String version = module.getVersion(); String version = module.getVersion();

View File

@ -63,7 +63,7 @@ public class FlashZip extends ParallelTask<Void, String, Integer> {
private boolean unzipAndCheck() throws Exception { private boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android"); ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
List<String> ret = Utils.readFile(magiskManager.rootShell, mCheckFile.getPath()); List<String> ret = Utils.readFile(magiskManager.shell, mCheckFile.getPath());
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK"); return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
} }
@ -84,7 +84,7 @@ public class FlashZip extends ParallelTask<Void, String, Integer> {
copyToCache(); copyToCache();
if (!unzipAndCheck()) return 0; if (!unzipAndCheck()) return 0;
mList.add(magiskManager.getString(R.string.zip_install_progress_msg, mFilename)); 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 + "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile +
" && echo 'Success!' || echo 'Failed!'" " && echo 'Success!' || echo 'Failed!'"
); );
@ -99,7 +99,7 @@ public class FlashZip extends ParallelTask<Void, String, Integer> {
// -1 = error, manual install; 0 = invalid zip; 1 = success // -1 = error, manual install; 0 = invalid zip; 1 = success
@Override @Override
protected void onPostExecute(Integer result) { protected void onPostExecute(Integer result) {
magiskManager.rootShell.su_raw( magiskManager.shell.su_raw(
"rm -rf " + mCachedFile.getParent() + "/*", "rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + MagiskManager.TMP_FOLDER_PATH "rm -rf " + MagiskManager.TMP_FOLDER_PATH
); );

View File

@ -1,29 +0,0 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.utils.Utils;
public class GetBootBlocks extends ParallelTask<Void, Void, Void> {
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);
}
}

View File

@ -21,10 +21,10 @@ public class LoadModules extends ParallelTask<Void, Void, Void> {
magiskManager.moduleMap = new ValueSortedMap<>(); 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); Logger.dev("LoadModules: Adding modules from " + path);
try { try {
Module module = new Module(magiskManager.rootShell, path); Module module = new Module(magiskManager.shell, path);
magiskManager.moduleMap.put(module.getId(), module); magiskManager.moduleMap.put(module.getId(), module);
} catch (BaseModule.CacheModException ignored) {} } catch (BaseModule.CacheModException ignored) {}
} }

View File

@ -15,7 +15,7 @@ public class MagiskHide extends ParallelTask<Object, Void, Void> {
@Override @Override
protected Void doInBackground(Object... params) { protected Void doInBackground(Object... params) {
String command = (String) params[0]; String command = (String) params[0];
List<String> ret = magiskManager.rootShell.su("magiskhide --" + command); List<String> ret = magiskManager.shell.su("magiskhide --" + command);
if (isList) { if (isList) {
magiskManager.magiskHideList = ret; magiskManager.magiskHideList = ret;
} }

View File

@ -37,13 +37,13 @@ public class Logger {
dev(String.format(Locale.US, fmt, args)); dev(String.format(Locale.US, fmt, args));
} }
public static void shell(boolean root, String line) { public static void shell(String line) {
if (MagiskManager.shellLogging) { 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) { public static void shell(String fmt, Object... args) {
shell(root, String.format(Locale.US, fmt, args)); shell(String.format(Locale.US, fmt, args));
} }
} }

View File

@ -2,11 +2,12 @@ package com.topjohnwu.magisk.utils;
import android.content.Context; import android.content.Context;
import java.io.BufferedReader;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -18,145 +19,133 @@ public class Shell {
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted // -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted
public static int rootStatus; public static int rootStatus;
private final Process rootShell; private final Process shellProcess;
private final DataOutputStream rootSTDIN; private final DataOutputStream STDIN;
private final DataInputStream rootSTDOUT; private final DataInputStream STDOUT;
private boolean isValid; private boolean isValid;
private Shell() { private Shell() {
Process process; rootStatus = 1;
Process process = null;
DataOutputStream in = null;
DataInputStream out = null;
try { try {
process = Runtime.getRuntime().exec("su"); process = Runtime.getRuntime().exec("su");
in = new DataOutputStream(process.getOutputStream());
out = new DataInputStream(process.getInputStream());
} catch (IOException e) { } catch (IOException e) {
// No root
rootStatus = 0; rootStatus = 0;
rootShell = null;
rootSTDIN = null;
rootSTDOUT = null;
isValid = false;
return;
} }
rootStatus = 1; while (true) {
isValid = true; if (rootAccess()) {
rootShell = process; try {
rootSTDIN = new DataOutputStream(rootShell.getOutputStream()); in.write(("id\n").getBytes("UTF-8"));
rootSTDOUT = new DataInputStream(rootShell.getInputStream()); in.flush();
String s = new BufferedReader(new InputStreamReader(out)).readLine();
su_raw("umask 022"); if (s.isEmpty() || !s.contains("uid=0")) {
List<String> ret = su("echo -BOC-", "id"); in.close();
out.close();
if (ret.isEmpty()) { process.destroy();
// Something wrong with root, not allowed? throw new IOException();
rootStatus = -1; }
return; } catch (IOException e) {
} rootStatus = -1;
continue;
for (String line : ret) { }
if (line.contains("uid=")) { break;
// id command is working, let's see if we are actually root } else {
rootStatus = line.contains("uid=0") ? 1 : -1; // Try to gain non-root sh
return; try {
} else if (!line.contains("-BOC-")) { process = Runtime.getRuntime().exec("sh");
rootStatus = -1; in = new DataOutputStream(process.getOutputStream());
return; 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(); return new Shell();
} }
public static Shell getRootShell(Context context) { public static Shell getShell(Context context) {
return Utils.getMagiskManager(context).rootShell; return Utils.getMagiskManager(context).shell;
} }
public static boolean rootAccess() { public static boolean rootAccess() {
return rootStatus > 0; return rootStatus > 0;
} }
public static List<String> sh(String... commands) { public List<String> sh(String... commands) {
List<String> res = Collections.synchronizedList(new ArrayList<String>());
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<String> su(String... commands) {
if (!isValid) return null;
List<String> res = new ArrayList<>(); List<String> res = new ArrayList<>();
su(res, commands); if (!isValid) return res;
sh(res, commands);
return res; return res;
} }
public void su_raw(String... commands) { public void sh_raw(String... commands) {
if (!isValid) return; if (!isValid) return;
synchronized (rootShell) { synchronized (shellProcess) {
try { try {
for (String command : commands) { for (String command : commands) {
rootSTDIN.write((command + "\n").getBytes("UTF-8")); STDIN.write((command + "\n").getBytes("UTF-8"));
rootSTDIN.flush(); STDIN.flush();
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
rootShell.destroy(); shellProcess.destroy();
isValid = false; isValid = false;
} }
} }
} }
public void su(List<String> output, String... commands) { public void sh(List<String> output, String... commands) {
if (!isValid) return; if (!isValid) return;
try { try {
rootShell.exitValue(); shellProcess.exitValue();
isValid = false; isValid = false;
return; // The process is dead, return return; // The process is dead, return
} catch (IllegalThreadStateException ignored) { } catch (IllegalThreadStateException ignored) {
// This should be the expected result // This should be the expected result
} }
synchronized (rootShell) { synchronized (shellProcess) {
StreamGobbler STDOUT = new StreamGobbler(rootSTDOUT, output, true); StreamGobbler out = new StreamGobbler(this.STDOUT, output);
STDOUT.start(); out.start();
su_raw(commands); sh_raw(commands);
su_raw("echo \'-root-done-\'"); sh_raw("echo \'-shell-done-\'");
try { STDOUT.join(); } catch (InterruptedException ignored) {} try { out.join(); } catch (InterruptedException ignored) {}
} }
} }
public List<String> 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<String> output, String... commands) {
if (!rootAccess()) return;
sh(output, commands);
}
} }

View File

@ -21,7 +21,7 @@ public class StreamGobbler extends Thread {
/** /**
* <p>StreamGobbler constructor</p> * <p>StreamGobbler constructor</p>
* *
* <p>We use this class because shell STDOUT and STDERR should be read as quickly as * <p>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 * possible to prevent a deadlock from occurring, or Process.waitFor() never
* returning (as the buffer is full, pausing the native process)</p> * returning (as the buffer is full, pausing the native process)</p>
* *
@ -38,21 +38,16 @@ public class StreamGobbler extends Thread {
writer = outputList; writer = outputList;
} }
public StreamGobbler(InputStream inputStream, List<String> outputList, boolean root) {
this(inputStream, outputList);
isRoot = root;
}
@Override @Override
public void run() { public void run() {
// keep reading the InputStream until it ends (or an error occurs) // keep reading the InputStream until it ends (or an error occurs)
try { try {
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
if (TextUtils.equals(line, "-root-done-")) if (TextUtils.equals(line, "-shell-done-"))
return; return;
writer.add(line); writer.add(line);
Logger.shell(isRoot, line); Logger.shell(line);
} }
} catch (IOException e) { } catch (IOException e) {
// reader probably closed, expected exit condition // reader probably closed, expected exit condition