Rewrite all root method with own su library
This commit is contained in:
parent
e18f4c843a
commit
a5ea214553
@ -25,7 +25,6 @@ dependencies {
|
||||
compile 'com.android.support:recyclerview-v7:24.2.0'
|
||||
compile 'com.android.support:cardview-v7:24.2.0'
|
||||
compile 'com.android.support:design:24.2.0'
|
||||
compile 'eu.chainfire:libsuperuser:1.0.0.201607041850'
|
||||
|
||||
compile 'com.jakewharton:butterknife:8.2.1'
|
||||
apt 'com.jakewharton:butterknife-compiler:8.2.1'
|
||||
|
@ -25,16 +25,15 @@ import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.WelcomeActivity;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import butterknife.BindView;
|
||||
@ -42,7 +41,7 @@ import butterknife.ButterKnife;
|
||||
|
||||
public class LogFragment extends Fragment {
|
||||
|
||||
private static final File MAGISK_LOG = new File("/cache/magisk.log");
|
||||
private static final String MAGISK_LOG = "/cache/magisk.log";
|
||||
|
||||
@BindView(R.id.txtLog) TextView txtLog;
|
||||
@BindView(R.id.svLog) ScrollView svLog;
|
||||
@ -100,7 +99,7 @@ public class LogFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void reloadErrorLog() {
|
||||
new LogsReader().execute(MAGISK_LOG);
|
||||
new LogsManager(true).execute();
|
||||
svLog.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -116,17 +115,8 @@ public class LogFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
try {
|
||||
new FileOutputStream(MAGISK_LOG).close();
|
||||
MAGISK_LOG.delete();
|
||||
txtLog.setText(R.string.log_is_empty);
|
||||
|
||||
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||
|
||||
reloadErrorLog();
|
||||
} catch (IOException e) {
|
||||
Toast.makeText(getActivity(), getResources().getString(R.string.logs_clear_failed) + "\n" + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
new LogsManager(false).execute();
|
||||
reloadErrorLog();
|
||||
}
|
||||
|
||||
private void send() {
|
||||
@ -179,16 +169,13 @@ public class LogFragment extends Fragment {
|
||||
dir.mkdir();
|
||||
|
||||
File targetFile = new File(dir, filename);
|
||||
List<String> in = Utils.readFile(MAGISK_LOG);
|
||||
|
||||
try {
|
||||
FileInputStream in = new FileInputStream(MAGISK_LOG);
|
||||
FileOutputStream out = new FileOutputStream(targetFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, len);
|
||||
FileWriter out = new FileWriter(targetFile);
|
||||
for (String line : in) {
|
||||
out.write(line + "\n");
|
||||
}
|
||||
in.close();
|
||||
out.close();
|
||||
|
||||
Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
|
||||
@ -199,30 +186,12 @@ public class LogFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private class LogsReader extends AsyncTask<File, Integer, String> {
|
||||
private class LogsManager extends AsyncTask<Void, Integer, String> {
|
||||
|
||||
private static final int MAX_LOG_SIZE = 1000 * 1024; // 1000 KB
|
||||
|
||||
private long skipLargeFile(BufferedReader is, long length) throws IOException {
|
||||
if (length < MAX_LOG_SIZE)
|
||||
return 0;
|
||||
|
||||
long skipped = length - MAX_LOG_SIZE;
|
||||
long yetToSkip = skipped;
|
||||
do {
|
||||
yetToSkip -= is.skip(yetToSkip);
|
||||
} while (yetToSkip > 0);
|
||||
|
||||
int c;
|
||||
do {
|
||||
c = is.read();
|
||||
if (c == -1)
|
||||
break;
|
||||
skipped++;
|
||||
} while (c != '\n');
|
||||
|
||||
return skipped;
|
||||
private boolean readLog;
|
||||
|
||||
public LogsManager(boolean read) {
|
||||
readLog = read;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -231,40 +200,33 @@ public class LogFragment extends Fragment {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(File... log) {
|
||||
protected String doInBackground(Void... voids) {
|
||||
// Ensure initialize is done
|
||||
try {
|
||||
WelcomeActivity.initialize.get();
|
||||
Utils.initialize.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Thread.currentThread().setPriority(Thread.NORM_PRIORITY + 2);
|
||||
if (readLog) {
|
||||
List<String> logList = Utils.readFile(MAGISK_LOG);
|
||||
|
||||
StringBuilder llog = new StringBuilder(15 * 10 * 1024);
|
||||
try {
|
||||
File logfile = log[0];
|
||||
BufferedReader br;
|
||||
br = new BufferedReader(new FileReader(logfile));
|
||||
long skipped = skipLargeFile(br, logfile.length());
|
||||
if (skipped > 0) {
|
||||
llog.append("-----------------\n");
|
||||
llog.append("Log too long");
|
||||
llog.append("\n-----------------\n\n");
|
||||
StringBuilder llog = new StringBuilder(15 * 10 * 1024);
|
||||
for (String s : logList) {
|
||||
llog.append(s).append("\n");
|
||||
}
|
||||
|
||||
char[] temp = new char[1024];
|
||||
int read;
|
||||
while ((read = br.read(temp)) > 0) {
|
||||
llog.append(temp, 0, read);
|
||||
return llog.toString();
|
||||
} else {
|
||||
if (Utils.removeFile(MAGISK_LOG)) {
|
||||
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Snackbar.make(txtLog, R.string.logs_clear_failed, Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
llog.append("Cannot read log:\n");
|
||||
llog.append(e.getMessage());
|
||||
return "";
|
||||
}
|
||||
|
||||
return llog.toString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4,6 +4,7 @@ import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -12,9 +13,12 @@ import android.widget.ImageView;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import butterknife.BindColor;
|
||||
import butterknife.BindView;
|
||||
@ -57,12 +61,18 @@ public class MagiskFragment extends Fragment {
|
||||
View view = inflater.inflate(R.layout.magisk_fragment, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
try {
|
||||
Utils.initialize.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
updateStatus();
|
||||
|
||||
rootToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
|
||||
Utils.su(b ? "setprop magisk.root 1" : "setprop magisk.root 0");
|
||||
Shell.su(b ? "setprop magisk.root 1" : "setprop magisk.root 0");
|
||||
updateStatus();
|
||||
}
|
||||
});
|
||||
@ -70,7 +80,7 @@ public class MagiskFragment extends Fragment {
|
||||
selinuxToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
|
||||
Utils.su(b ? "setenforce 1" : "setenforce 0");
|
||||
Shell.su(b ? "setenforce 1" : "setenforce 0");
|
||||
updateStatus();
|
||||
}
|
||||
});
|
||||
@ -87,8 +97,8 @@ public class MagiskFragment extends Fragment {
|
||||
}
|
||||
|
||||
private void updateStatus() {
|
||||
String selinux = Utils.sh("getenforce");
|
||||
String version = Utils.sh("getprop magisk.version");
|
||||
String selinux = Shell.sh("getenforce").get(0);
|
||||
String version = Shell.sh("getprop magisk.version").get(0);
|
||||
|
||||
if (TextUtils.isEmpty(version)) {
|
||||
magiskStatusContainer.setBackgroundColor(grey500);
|
||||
@ -134,9 +144,9 @@ public class MagiskFragment extends Fragment {
|
||||
|
||||
safetyNetStatusIcon.setImageResource(statusError);
|
||||
|
||||
if (!Utils.rootAccess) {
|
||||
if (!Shell.rootAccess()) {
|
||||
rootStatusContainer.setBackgroundColor(red500);
|
||||
rootStatusIcon.setImageResource(statusError);
|
||||
rootStatusIcon.setImageResource(statusUnknown);
|
||||
rootStatus.setTextColor(red500);
|
||||
rootStatus.setText(R.string.root_system);
|
||||
|
||||
@ -146,7 +156,7 @@ public class MagiskFragment extends Fragment {
|
||||
safetyNetStatus.setText(R.string.root_system_info);
|
||||
} else {
|
||||
rootStatusContainer.setBackgroundColor(green500);
|
||||
rootStatusIcon.setImageResource(statusOK);
|
||||
rootStatusIcon.setImageResource(statusError);
|
||||
rootStatus.setTextColor(green500);
|
||||
rootStatus.setText(R.string.root_mounted);
|
||||
|
||||
@ -154,14 +164,14 @@ public class MagiskFragment extends Fragment {
|
||||
}
|
||||
} else {
|
||||
rootStatusContainer.setBackgroundColor(green500);
|
||||
rootStatusIcon.setImageResource(red500);
|
||||
rootStatusIcon.setImageResource(statusOK);
|
||||
|
||||
rootStatus.setTextColor(green500);
|
||||
rootToggle.setChecked(false);
|
||||
|
||||
safetyNetStatusIcon.setImageResource(statusOK);
|
||||
|
||||
if (!Utils.rootAccess) {
|
||||
if (!Shell.rootAccess()) {
|
||||
rootStatusContainer.setBackgroundColor(red500);
|
||||
rootStatusIcon.setImageResource(statusError);
|
||||
rootStatus.setTextColor(red500);
|
||||
|
@ -14,6 +14,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
@ -29,8 +30,8 @@ public class ModulesFragment extends Fragment {
|
||||
private static final String MAGISK_PATH = "/magisk";
|
||||
private static final String MAGISK_CACHE_PATH = "/cache/magisk";
|
||||
|
||||
private static List<Module> listModules = new ArrayList<>();
|
||||
private static List<Module> listModulesCache = new ArrayList<>();
|
||||
// protected static List<Module> listModules = new ArrayList<>();
|
||||
// protected static List<Module> listModulesCache = new ArrayList<>();
|
||||
|
||||
@BindView(R.id.progressBar) ProgressBar progressBar;
|
||||
@BindView(R.id.pager) ViewPager viewPager;
|
||||
@ -40,8 +41,8 @@ public class ModulesFragment extends Fragment {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
listModules.clear();
|
||||
listModulesCache.clear();
|
||||
// listModules.clear();
|
||||
// listModulesCache.clear();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -62,7 +63,7 @@ public class ModulesFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
protected List<Module> listModules() {
|
||||
return listModules;
|
||||
return Utils.listModules;
|
||||
}
|
||||
|
||||
}
|
||||
@ -71,7 +72,7 @@ public class ModulesFragment extends Fragment {
|
||||
|
||||
@Override
|
||||
protected List<Module> listModules() {
|
||||
return listModulesCache;
|
||||
return Utils.listModulesCache;
|
||||
}
|
||||
|
||||
}
|
||||
@ -82,51 +83,11 @@ public class ModulesFragment extends Fragment {
|
||||
protected Void doInBackground(Void... voids) {
|
||||
// Ensure initialize is done
|
||||
try {
|
||||
WelcomeActivity.initialize.get();
|
||||
Utils.initialize.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
File[] magisk = new File(MAGISK_PATH).listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
return file.isDirectory();
|
||||
}
|
||||
});
|
||||
|
||||
File[] magiskCache = new File(MAGISK_CACHE_PATH).listFiles(new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File file) {
|
||||
return file.isDirectory();
|
||||
}
|
||||
});
|
||||
|
||||
if (magisk != null) {
|
||||
for (File mod : magisk) {
|
||||
Module m = new Module(mod);
|
||||
if (m.isValid()) {
|
||||
try {
|
||||
m.parse();
|
||||
listModules.add(m);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (magiskCache != null) {
|
||||
for (File mod : magiskCache) {
|
||||
Module m = new Module(mod);
|
||||
if (m.isValid()) {
|
||||
try {
|
||||
m.parse();
|
||||
listModulesCache.add(m);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -16,18 +16,22 @@ import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class WelcomeActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
|
||||
|
||||
private static final String SELECTED_ITEM_ID = "SELECTED_ITEM_ID";
|
||||
public static Init initialize;
|
||||
public static View view;
|
||||
private final Handler mDrawerHandler = new Handler();
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
@ -46,9 +50,10 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
|
||||
}
|
||||
initialize = new Init();
|
||||
|
||||
initialize.execute();
|
||||
Utils.initialize = new Utils.Init();
|
||||
|
||||
Utils.initialize.execute();
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
@ -152,12 +157,6 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
// Check root access
|
||||
Utils.checkRoot();
|
||||
// Permission for java code to read /cache files
|
||||
if (Utils.rootAccess) {
|
||||
Utils.su("chmod 755 /cache", "chmod 644 /cache/magisk.log");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -165,7 +164,7 @@ public class WelcomeActivity extends AppCompatActivity implements NavigationView
|
||||
protected void onPostExecute(Void v) {
|
||||
super.onPostExecute(v);
|
||||
|
||||
if (!Utils.rootAccess) {
|
||||
if (!Shell.rootAccess()) {
|
||||
Snackbar.make(view, R.string.no_root_access, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package com.topjohnwu.magisk.module;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@ -8,71 +10,21 @@ import java.io.FileReader;
|
||||
|
||||
public class Module {
|
||||
|
||||
private final boolean isValid;
|
||||
|
||||
private File mRemoveFile;
|
||||
private File mDisableFile;
|
||||
|
||||
private File mPropFile;
|
||||
private String mRemoveFile;
|
||||
private String mDisableFile;
|
||||
|
||||
private String mName;
|
||||
private String mVersion;
|
||||
private String mDescription;
|
||||
|
||||
public Module(File file) {
|
||||
this.isValid = new File(file + "/module.prop").exists();
|
||||
private boolean mEnable;
|
||||
private boolean mRemove;
|
||||
|
||||
if (!isValid) return;
|
||||
|
||||
mPropFile = new File(file + "/module.prop");
|
||||
mRemoveFile = new File(file + "/remove");
|
||||
mDisableFile = new File(file + "/disable");
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValid && mPropFile != null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public void createDisableFile() {
|
||||
Utils.su("touch " + mDisableFile.getPath());
|
||||
}
|
||||
|
||||
public void removeDisableFile() {
|
||||
Utils.su("rm -f " + mDisableFile.getPath());
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return ! mDisableFile.exists();
|
||||
}
|
||||
|
||||
public void createRemoveFile() {
|
||||
Utils.su("touch " + mRemoveFile.getPath());
|
||||
}
|
||||
|
||||
public void deleteRemoveFile() {
|
||||
Utils.su("rm -f " + mRemoveFile.getPath());
|
||||
}
|
||||
|
||||
public boolean willBeRemoved() {
|
||||
return mRemoveFile.exists();
|
||||
}
|
||||
|
||||
public void parse() throws Exception {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(mPropFile));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
public Module(String path) {
|
||||
mRemoveFile = path + "/remove";
|
||||
mDisableFile = path + "/disable";
|
||||
for (String line : Utils.readFile(path + "/module.prop")) {
|
||||
String[] parts = line.split("=", 2);
|
||||
if (parts.length != 2) {
|
||||
continue;
|
||||
@ -96,6 +48,46 @@ public class Module {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reader.close();
|
||||
|
||||
mEnable = !Utils.fileExist(mDisableFile);
|
||||
mRemove = Utils.fileExist(mRemoveFile);
|
||||
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return mVersion;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public void createDisableFile() {
|
||||
mEnable = !Utils.createFile(mDisableFile);
|
||||
}
|
||||
|
||||
public void removeDisableFile() {
|
||||
mEnable = Utils.removeFile(mDisableFile);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return mEnable;
|
||||
}
|
||||
|
||||
public void createRemoveFile() {
|
||||
mRemove = Utils.createFile(mRemoveFile);
|
||||
}
|
||||
|
||||
public void deleteRemoveFile() {
|
||||
mRemove = !Utils.removeFile(mRemoveFile);
|
||||
}
|
||||
|
||||
public boolean willBeRemoved() {
|
||||
return mRemove;
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
@ -101,7 +102,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
if (!Utils.rootAccess) {
|
||||
if (!Shell.rootAccess()) {
|
||||
checkBox.setEnabled(false);
|
||||
delete.setEnabled(false);
|
||||
}
|
||||
|
172
app/src/main/java/com/topjohnwu/magisk/utils/Shell.java
Normal file
172
app/src/main/java/com/topjohnwu/magisk/utils/Shell.java
Normal file
@ -0,0 +1,172 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Modified by topjohnwu, based on Chainfire's libsuperuser
|
||||
*/
|
||||
|
||||
public class Shell {
|
||||
|
||||
|
||||
public static String suPath;
|
||||
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted; 2 = improperly rooted;
|
||||
public static int rootStatus = 0;
|
||||
|
||||
private static Process rootShell;
|
||||
private static DataOutputStream rootSTDIN;
|
||||
private static StreamGobbler rootSTDOUT;
|
||||
private static List<String> rootOutList = new ArrayList<>();
|
||||
|
||||
static {
|
||||
List<String> ret = sh("getprop magisk.supath");
|
||||
if(!ret.isEmpty()) {
|
||||
suPath = ret.get(0) + "/su";
|
||||
} else {
|
||||
suPath = "su";
|
||||
rootStatus = 2;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean rootAccess() {
|
||||
return rootStatus > 0;
|
||||
}
|
||||
|
||||
public static void startRoot() {
|
||||
rootStatus = rootStatus == 0 ? 1 : 2;
|
||||
|
||||
try {
|
||||
rootShell = Runtime.getRuntime().exec(suPath);
|
||||
rootSTDIN = new DataOutputStream(rootShell.getOutputStream());
|
||||
rootSTDOUT = new StreamGobbler(rootShell.getInputStream(), rootOutList);
|
||||
rootSTDOUT.start();
|
||||
} catch (IOException e) {
|
||||
// runtime error! No binary found!
|
||||
rootStatus = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
List<String> ret = su("echo -BOC-", "id");
|
||||
if (ret == null) {
|
||||
// Something wrong with root install, not enabled?
|
||||
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") ? rootStatus : -1;
|
||||
return;
|
||||
} else if (!line.contains("-BOC-")) {
|
||||
rootStatus = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
if (rootAccess()) {
|
||||
rootSTDIN.write("exit\n".getBytes("UTF-8"));
|
||||
rootSTDIN.flush();
|
||||
rootSTDIN.flush();
|
||||
rootShell.waitFor();
|
||||
rootSTDIN.close();
|
||||
rootSTDOUT.join();
|
||||
rootShell.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public static 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();
|
||||
}
|
||||
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 static List<String> su(String... commands) {
|
||||
|
||||
if(!Shell.rootAccess()) return null;
|
||||
|
||||
rootOutList.clear();
|
||||
|
||||
try {
|
||||
try {
|
||||
for (String write : commands) {
|
||||
rootSTDIN.write((write + "\n").getBytes("UTF-8"));
|
||||
rootSTDIN.flush();
|
||||
}
|
||||
rootSTDIN.write(("echo \'-done-\'\n").getBytes("UTF-8"));
|
||||
rootSTDIN.flush();
|
||||
} catch (IOException e) {
|
||||
if (!e.getMessage().contains("EPIPE")) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// Process terminated, it means the interactive shell cannot be initialized
|
||||
rootShell.exitValue();
|
||||
return null;
|
||||
} catch (IllegalThreadStateException e) {
|
||||
// Process still running, gobble output until done
|
||||
if (rootOutList != null && !rootOutList.isEmpty()) {
|
||||
if (rootOutList.get(rootOutList.size() - 1).equals("-done-")) {
|
||||
rootOutList.remove(rootOutList.size() - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rootSTDOUT.join(100);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
// shell probably not found
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ArrayList<>(rootOutList);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Modified by topjohnwu, based on Chainfire's libsuperuser
|
||||
*/
|
||||
|
||||
public class StreamGobbler extends Thread {
|
||||
|
||||
private BufferedReader reader = null;
|
||||
private List<String> writer = null;
|
||||
|
||||
/**
|
||||
* <p>StreamGobbler constructor</p>
|
||||
*
|
||||
* <p>We use this class because shell 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)</p>
|
||||
*
|
||||
* @param inputStream InputStream to read from
|
||||
* @param outputList {@literal List<String>} to write to, or null
|
||||
*/
|
||||
public StreamGobbler(InputStream inputStream, List<String> outputList) {
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
writer = outputList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// keep reading the InputStream until it ends (or an error occurs)
|
||||
try {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
writer.add(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// reader probably closed, expected exit condition
|
||||
}
|
||||
|
||||
// make sure our stream is closed and resources will be freed
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
// read already closed
|
||||
}
|
||||
}
|
||||
}
|
@ -1,56 +1,82 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import eu.chainfire.libsuperuser.Shell;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static final String suPath = sh("getprop magisk.supath");
|
||||
public static boolean rootAccess = false;
|
||||
public static Init initialize;
|
||||
|
||||
public static String sh(String... commands) {
|
||||
List<String> result = Shell.SH.run(commands);
|
||||
public static List<Module> listModules = new ArrayList<>();
|
||||
public static List<Module> listModulesCache = new ArrayList<>();
|
||||
public static List<String> listLog;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : result) {
|
||||
builder.append(s);
|
||||
}
|
||||
public static class Init extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String su(String... commands) {
|
||||
List<String> result = Shell.run(Utils.suPath + "/su", commands, null, false);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : result) {
|
||||
builder.append(s);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static void checkRoot() {
|
||||
String [] availableTestCommands = new String[] {"echo -BOC-", "id"};
|
||||
List<String> ret = Shell.run(Utils.suPath + "/su", availableTestCommands, null, false);
|
||||
if (ret == null)
|
||||
return;
|
||||
|
||||
// Taken from libsuperuser
|
||||
|
||||
// this is only one of many ways this can be done
|
||||
|
||||
for (String line : ret) {
|
||||
if (line.contains("uid=")) {
|
||||
// id command is working, let's see if we are actually root
|
||||
rootAccess = line.contains("uid=0");
|
||||
} else if (line.contains("-BOC-")) {
|
||||
// if we end up here, at least the su command starts some kind
|
||||
// of shell, let's hope it has root privileges - no way to know without
|
||||
// additional native binaries
|
||||
rootAccess = true;
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
Shell.startRoot();
|
||||
listModules.clear();
|
||||
listModulesCache.clear();
|
||||
List<String> magisk = getModList("/magisk");
|
||||
List<String> magiskCache = getModList("/cache/magisk");
|
||||
if (!magisk.isEmpty()) {
|
||||
for (String mod : magisk) {
|
||||
listModules.add(new Module(mod));
|
||||
}
|
||||
}
|
||||
|
||||
if (!magiskCache.isEmpty()) {
|
||||
for (String mod : magiskCache) {
|
||||
listModulesCache.add(new Module(mod));
|
||||
}
|
||||
}
|
||||
listLog = readFile("/cache/magisk.log");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean fileExist(String path) {
|
||||
List<String> ret;
|
||||
ret = Shell.sh("if [ -f " + path + " ]; then echo true; else echo false; fi");
|
||||
if (!Boolean.parseBoolean(ret.get(0)) && Shell.rootAccess()) ret = Shell.su("if [ -f " + path + " ]; then echo true; else echo false; fi");
|
||||
return Boolean.parseBoolean(ret.get(0));
|
||||
}
|
||||
|
||||
public static boolean createFile(String path) {
|
||||
if (!Shell.rootAccess()) {
|
||||
return false;
|
||||
} else {
|
||||
return Boolean.parseBoolean(Shell.su("touch " + path + " 2>/dev/null; if [ -f " + path + " ]; then echo true; else echo false; fi").get(0));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean removeFile(String path) {
|
||||
if (!Shell.rootAccess()) {
|
||||
return false;
|
||||
} else {
|
||||
return Boolean.parseBoolean(Shell.su("rm -f " + path + " 2>/dev/null; if [ -f " + path + " ]; then echo false; else echo true; fi").get(0));
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getModList(String path) {
|
||||
List<String> ret;
|
||||
ret = Shell.sh("find " + path + " -type d -maxdepth 1 | while read ITEM ; do if [ -f $ITEM/module.prop ]; then echo $ITEM; fi; done");
|
||||
if (ret.isEmpty() && Shell.rootAccess()) ret = Shell.su("find " + path + " -type d -maxdepth 1 | while read ITEM ; do if [ -f $ITEM/module.prop ]; then echo $ITEM; fi; done");
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static List<String> readFile(String path) {
|
||||
List<String> ret;
|
||||
ret = Shell.sh("cat " + path);
|
||||
if (ret.isEmpty() && Shell.rootAccess()) ret = Shell.su("cat " + path, "echo \' \'");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user