From a5ea2145531331dd1ec9919b0ac7e3b3aa4bd118 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Thu, 25 Aug 2016 05:58:15 +0800 Subject: [PATCH] Rewrite all root method with own su library --- app/build.gradle | 1 - .../com/topjohnwu/magisk/LogFragment.java | 98 +++------- .../com/topjohnwu/magisk/MagiskFragment.java | 28 ++- .../com/topjohnwu/magisk/ModulesFragment.java | 55 +----- .../com/topjohnwu/magisk/WelcomeActivity.java | 19 +- .../com/topjohnwu/magisk/module/Module.java | 110 ++++++----- .../topjohnwu/magisk/rv/ModulesAdapter.java | 3 +- .../com/topjohnwu/magisk/utils/Shell.java | 172 ++++++++++++++++++ .../topjohnwu/magisk/utils/StreamGobbler.java | 52 ++++++ .../com/topjohnwu/magisk/utils/Utils.java | 110 ++++++----- 10 files changed, 411 insertions(+), 237 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/Shell.java create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java diff --git a/app/build.gradle b/app/build.gradle index e42cef960..435fc68e8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/com/topjohnwu/magisk/LogFragment.java b/app/src/main/java/com/topjohnwu/magisk/LogFragment.java index 581db13b3..4ecce58a6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/LogFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/LogFragment.java @@ -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 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 { + private class LogsManager extends AsyncTask { - 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 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 diff --git a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java index f091a90ef..0d257d61b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java @@ -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); diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java index 8de0d2f3e..e39a2326c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java @@ -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 listModules = new ArrayList<>(); - private static List listModulesCache = new ArrayList<>(); +// protected static List listModules = new ArrayList<>(); +// protected static List 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 listModules() { - return listModules; + return Utils.listModules; } } @@ -71,7 +72,7 @@ public class ModulesFragment extends Fragment { @Override protected List 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; } diff --git a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java index 72b9dbaa6..11ea18fe4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/WelcomeActivity.java @@ -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(); } } diff --git a/app/src/main/java/com/topjohnwu/magisk/module/Module.java b/app/src/main/java/com/topjohnwu/magisk/module/Module.java index 4c6dbec4e..2c0109373 100644 --- a/app/src/main/java/com/topjohnwu/magisk/module/Module.java +++ b/app/src/main/java/com/topjohnwu/magisk/module/Module.java @@ -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; + } + } \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/rv/ModulesAdapter.java b/app/src/main/java/com/topjohnwu/magisk/rv/ModulesAdapter.java index 28f846520..76245bab0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/rv/ModulesAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/rv/ModulesAdapter.java @@ -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 rootOutList = new ArrayList<>(); + + static { + List 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 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 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(); + } + 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 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); + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java b/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java new file mode 100644 index 000000000..8b40775ab --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/StreamGobbler.java @@ -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 writer = null; + + /** + *

StreamGobbler constructor

+ * + *

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)

+ * + * @param inputStream InputStream to read from + * @param outputList {@literal List} to write to, or null + */ + public StreamGobbler(InputStream inputStream, List 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 + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index d56359933..20f4a3118 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -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 result = Shell.SH.run(commands); + public static List listModules = new ArrayList<>(); + public static List listModulesCache = new ArrayList<>(); + public static List listLog; - StringBuilder builder = new StringBuilder(); - for (String s : result) { - builder.append(s); - } + public static class Init extends AsyncTask { - return builder.toString(); - } - - public static String su(String... commands) { - List 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 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 magisk = getModList("/magisk"); + List 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 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 getModList(String path) { + List 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 readFile(String path) { + List ret; + ret = Shell.sh("cat " + path); + if (ret.isEmpty() && Shell.rootAccess()) ret = Shell.su("cat " + path, "echo \' \'"); + return ret; + } + + + }