From d3f5f5ee59be4aa18bd957735146546d857d566d Mon Sep 17 00:00:00 2001
From: topjohnwu <topjohnwu@gmail.com>
Date: Fri, 3 May 2019 03:36:39 -0400
Subject: [PATCH] Rewrite RootUtils in Kotlin

---
 .../topjohnwu/magisk/utils/DownloadApp.java   |   2 +-
 .../com/topjohnwu/magisk/utils/PatchAPK.java  |   6 +-
 .../com/topjohnwu/magisk/utils/RootUtils.java | 149 ----------------
 .../com/topjohnwu/magisk/utils/RootUtils.kt   | 164 ++++++++++++++++++
 .../com/topjohnwu/magisk/utils/Utils.java     |  27 +--
 .../com/topjohnwu/magisk/utils/XAndroid.kt    |   5 +-
 .../java/com/topjohnwu/magisk/utils/XList.kt  |  14 ++
 .../magisk/view/dialogs/EnvFixDialog.java     |   6 +-
 8 files changed, 195 insertions(+), 178 deletions(-)
 delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java
 create mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt

diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java b/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java
index 39727e52d..e10d38420 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java
@@ -89,7 +89,7 @@ public class DownloadApp {
             // Make it world readable
             apk.setReadable(true, false);
             if (Shell.su("pm install " + apk).exec().isSuccess())
-                RootUtils.rmAndLaunch(app.getPackageName(),
+                RootUtils.Companion.rmAndLaunch(app.getPackageName(),
                         new ComponentName(BuildConfig.APPLICATION_ID,
                                 ClassMap.get(SplashActivity.class).getName()));
             progress.dismiss();
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java
index 0d77c058a..95389302f 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java
@@ -3,6 +3,8 @@ package com.topjohnwu.magisk.utils;
 import android.content.ComponentName;
 import android.widget.Toast;
 
+import androidx.core.app.NotificationCompat;
+
 import com.topjohnwu.magisk.App;
 import com.topjohnwu.magisk.BuildConfig;
 import com.topjohnwu.magisk.ClassMap;
@@ -27,8 +29,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.jar.JarEntry;
 
-import androidx.core.app.NotificationCompat;
-
 public class PatchAPK {
 
     public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
@@ -112,7 +112,7 @@ public class PatchAPK {
 
         Config.set(Config.Key.SU_MANAGER, pkg);
         Config.export();
-        RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID,
+        RootUtils.Companion.rmAndLaunch(BuildConfig.APPLICATION_ID,
                 new ComponentName(pkg, ClassMap.get(SplashActivity.class).getName()));
 
         return true;
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java
deleted file mode 100644
index 216c2a3d0..000000000
--- a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java
+++ /dev/null
@@ -1,149 +0,0 @@
-package com.topjohnwu.magisk.utils;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.annotation.NonNull;
-
-import com.topjohnwu.magisk.Config;
-import com.topjohnwu.magisk.Const;
-import com.topjohnwu.magisk.R;
-import com.topjohnwu.superuser.Shell;
-import com.topjohnwu.superuser.ShellUtils;
-import com.topjohnwu.superuser.io.SuFile;
-
-import java.io.InputStream;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.List;
-
-public class RootUtils extends Shell.Initializer {
-
-    public static void rmAndLaunch(String rm, ComponentName component) {
-        Shell.su(Utils.fmt("(rm_launch %s %s)&", rm, component.flattenToString())).exec();
-    }
-
-    public static void reboot() {
-        Shell.su("/system/bin/reboot" + (Config.recovery ? " recovery" : "")).submit();
-    }
-
-    public static void startActivity(Intent intent) {
-        if (intent.getComponent() == null)
-            return;
-        List<String> args = new ArrayList<>();
-        args.add("am");
-        args.add("start");
-        intentToCommand(intent, args);
-        Shell.su(Utils.argsToCommand(args)).exec();
-    }
-
-    public static void intentToCommand(Intent intent, List<String> args) {
-        if (intent.getAction() != null) {
-            args.add("-a");
-            args.add(intent.getAction());
-        }
-        if (intent.getComponent() != null) {
-            args.add("-n");
-            args.add(intent.getComponent().flattenToString());
-        }
-        if (intent.getData() != null) {
-            args.add("-d");
-            args.add(intent.getDataString());
-        }
-        if (intent.getCategories() != null) {
-            for (String cat : intent.getCategories()) {
-                args.add("-c");
-                args.add(cat);
-            }
-        }
-        if (intent.getType() != null) {
-            args.add("-t");
-            args.add(intent.getType());
-        }
-        Bundle extras = intent.getExtras();
-        if (extras != null) {
-            for (String key : extras.keySet()) {
-                Object val = extras.get(key);
-                if (val == null)
-                    continue;
-                Object value = val;
-                String arg;
-                if (val instanceof String)          arg = "--es";
-                else if (val instanceof Boolean)    arg = "--ez";
-                else if (val instanceof Integer)    arg = "--ei";
-                else if (val instanceof Long)       arg = "--el";
-                else if (val instanceof Float)      arg = "--ef";
-                else if (val instanceof Uri)        arg = "--eu";
-                else if (val instanceof ComponentName) {
-                    arg = "--ecn";
-                    value = ((ComponentName) val).flattenToString();
-                } else if (val instanceof ArrayList) {
-                    ArrayList arr = (ArrayList) val;
-                    if (arr.size() <= 0)
-                    /* Impossible to know the type due to type erasure */
-                        continue;
-
-                    if (arr.get(0) instanceof Integer)      arg = "--eial";
-                    else if (arr.get(0) instanceof Long)    arg = "--elal";
-                    else if (arr.get(0) instanceof Float)   arg = "--efal";
-                    else if (arr.get(0) instanceof String)  arg = "--esal";
-                    else                                    continue;  /* Unsupported */
-                    StringBuilder sb = new StringBuilder();
-                    for (Object o : arr) {
-                        sb.append(o.toString().replace(",", "\\,"));
-                        sb.append(',');
-                    }
-                    // Remove trailing comma
-                    sb.deleteCharAt(sb.length() - 1);
-                    value = sb;
-                } else if (val.getClass().isArray()) {
-                    if (val instanceof int[])           arg = "--eia";
-                    else if (val instanceof long[])     arg = "--ela";
-                    else if (val instanceof float[])    arg = "--efa";
-                    else if (val instanceof String[])   arg = "--esa";
-                    else                                continue;  /* Unsupported */
-                    StringBuilder sb = new StringBuilder();
-                    int len = Array.getLength(val);
-                    for (int i = 0; i < len; ++i) {
-                        sb.append(Array.get(val, i).toString().replace(",", "\\,"));
-                        sb.append(',');
-                    }
-                    // Remove trailing comma
-                    sb.deleteCharAt(sb.length() - 1);
-                    value = sb;
-                }
-                else continue;  /* Unsupported */
-
-                args.add(arg);
-                args.add(key);
-                args.add(value.toString());
-            }
-        }
-        args.add("-f");
-        args.add(String.valueOf(intent.getFlags()));
-    }
-
-    @Override
-    public boolean onInit(Context context, @NonNull Shell shell) {
-        Shell.Job job = shell.newJob();
-        if (shell.isRoot()) {
-            job.add(context.getResources().openRawResource(R.raw.util_functions))
-                .add(context.getResources().openRawResource(R.raw.utils));
-            Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
-            Config.loadMagiskInfo();
-        } else {
-            InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
-            job.add(nonroot);
-        }
-
-        job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
-
-        Config.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
-        Config.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
-        Config.recovery = Boolean.parseBoolean(ShellUtils.fastCmd("echo $RECOVERYMODE"));
-        return true;
-    }
-}
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt
new file mode 100644
index 000000000..baa9ed7dd
--- /dev/null
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt
@@ -0,0 +1,164 @@
+package com.topjohnwu.magisk.utils
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import com.topjohnwu.magisk.Config
+import com.topjohnwu.magisk.Const
+import com.topjohnwu.magisk.R
+import com.topjohnwu.superuser.Shell
+import com.topjohnwu.superuser.ShellUtils
+import com.topjohnwu.superuser.io.SuFile
+import java.util.*
+import java.lang.reflect.Array as RArray
+
+fun Intent.toCommand(args: MutableList<String>) {
+    if (action != null) {
+        args.add("-a")
+        args.add(action!!)
+    }
+    if (component != null) {
+        args.add("-n")
+        args.add(component!!.flattenToString())
+    }
+    if (data != null) {
+        args.add("-d")
+        args.add(dataString!!)
+    }
+    if (categories != null) {
+        for (cat in categories) {
+            args.add("-c")
+            args.add(cat)
+        }
+    }
+    if (type != null) {
+        args.add("-t")
+        args.add(type!!)
+    }
+    val extras = extras
+    if (extras != null) {
+        for (key in extras.keySet()) {
+            val v = extras.get(key) ?: continue
+            var value: Any = v
+            val arg: String
+            if (v is String)
+                arg = "--es"
+            else if (v is Boolean)
+                arg = "--ez"
+            else if (v is Int)
+                arg = "--ei"
+            else if (v is Long)
+                arg = "--el"
+            else if (v is Float)
+                arg = "--ef"
+            else if (v is Uri)
+                arg = "--eu"
+            else if (v is ComponentName) {
+                arg = "--ecn"
+                value = v.flattenToString()
+            } else if (v is ArrayList<*>) {
+                if (v.size <= 0)
+                /* Impossible to know the type due to type erasure */
+                    continue
+
+                arg = if (v[0] is Int)
+                    "--eial"
+                else if (v[0] is Long)
+                    "--elal"
+                else if (v[0] is Float)
+                    "--efal"
+                else if (v[0] is String)
+                    "--esal"
+                else
+                    continue  /* Unsupported */
+
+                val sb = StringBuilder()
+                for (o in v) {
+                    sb.append(o.toString().replace(",", "\\,"))
+                    sb.append(',')
+                }
+                // Remove trailing comma
+                sb.deleteCharAt(sb.length - 1)
+                value = sb
+            } else if (v.javaClass.isArray) {
+                arg = if (v is IntArray)
+                    "--eia"
+                else if (v is LongArray)
+                    "--ela"
+                else if (v is FloatArray)
+                    "--efa"
+                else if (v is Array<*> && v.isArrayOf<String>())
+                    "--esa"
+                else
+                    continue  /* Unsupported */
+
+                val sb = StringBuilder()
+                val len = RArray.getLength(v)
+                for (i in 0 until len) {
+                    sb.append(RArray.get(v, i)!!.toString().replace(",", "\\,"))
+                    sb.append(',')
+                }
+                // Remove trailing comma
+                sb.deleteCharAt(sb.length - 1)
+                value = sb
+            } else
+                continue  /* Unsupported */
+
+            args.add(arg)
+            args.add(key)
+            args.add(value.toString())
+        }
+    }
+    args.add("-f")
+    args.add(flags.toString())
+}
+
+fun startActivity(intent: Intent) {
+    if (intent.component == null)
+        return
+    val args = ArrayList<String>()
+    args.add("am")
+    args.add("start")
+    intent.toCommand(args)
+    Shell.su(args.toShellCmd()).exec()
+}
+
+class RootUtils : Shell.Initializer() {
+
+    override fun onInit(context: Context?, shell: Shell): Boolean {
+        context ?: return false
+
+        val job = shell.newJob()
+        if (shell.isRoot) {
+            job.add(context.rawResource(R.raw.util_functions))
+                    .add(context.rawResource(R.raw.utils))
+            Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk")
+            Config.loadMagiskInfo()
+        } else {
+            job.add(context.rawResource(R.raw.nonroot_utils))
+        }
+
+        job.add("mount_partitions",
+                "get_flags",
+                "run_migrations",
+                "export BOOTMODE=true")
+                .exec()
+
+        Config.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
+        Config.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
+        Config.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
+        return true
+    }
+
+    companion object {
+
+        fun rmAndLaunch(rm: String, component: ComponentName) {
+            Shell.su("(rm_launch $rm ${component.flattenToString()})").exec()
+        }
+
+        fun reboot() {
+            Shell.su("/system/bin/reboot ${if (Config.recovery) "recovery" else ""}").submit()
+        }
+    }
+}
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 1cbcb06d5..562fcadf0 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
@@ -12,6 +12,12 @@ import android.net.Uri;
 import android.provider.OpenableColumns;
 import android.widget.Toast;
 
+import androidx.work.Constraints;
+import androidx.work.ExistingPeriodicWorkPolicy;
+import androidx.work.NetworkType;
+import androidx.work.PeriodicWorkRequest;
+import androidx.work.WorkManager;
+
 import com.topjohnwu.magisk.App;
 import com.topjohnwu.magisk.BuildConfig;
 import com.topjohnwu.magisk.ClassMap;
@@ -25,17 +31,10 @@ import com.topjohnwu.superuser.Shell;
 import com.topjohnwu.superuser.internal.UiThreadHandler;
 import com.topjohnwu.superuser.io.SuFile;
 
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import androidx.work.Constraints;
-import androidx.work.ExistingPeriodicWorkPolicy;
-import androidx.work.NetworkType;
-import androidx.work.PeriodicWorkRequest;
-import androidx.work.WorkManager;
-
 public class Utils {
 
     public static void toast(CharSequence msg, int duration) {
@@ -166,18 +165,4 @@ public class Utils {
         }
     }
 
-    public static String argsToCommand(List<String> args) {
-        StringBuilder sb = new StringBuilder();
-        for (String s : args) {
-            if (s.contains(" ")) {
-                sb.append('"').append(s).append('"');
-            } else {
-                sb.append(s);
-            }
-            sb.append(' ');
-        }
-        sb.deleteCharAt(sb.length() - 1);
-        return sb.toString();
-    }
-
 }
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
index bf594c755..e722bd134 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
@@ -1,5 +1,6 @@
 package com.topjohnwu.magisk.utils
 
+import android.content.Context
 import android.content.pm.ApplicationInfo
 import android.content.pm.ComponentInfo
 import android.content.pm.PackageInfo
@@ -47,4 +48,6 @@ fun PackageManager.receivers(packageName: String) =
     getPackageInfo(packageName, GET_RECEIVERS).receivers
 
 fun PackageManager.providers(packageName: String) =
-    getPackageInfo(packageName, GET_PROVIDERS).providers
\ No newline at end of file
+    getPackageInfo(packageName, GET_PROVIDERS).providers
+
+fun Context.rawResource(id: Int) = resources.openRawResource(id)
diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt
index 705ee9bb3..26b5009fa 100644
--- a/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt
+++ b/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt
@@ -10,6 +10,20 @@ fun <T> MutableList<T>.update(newList: List<T>) {
     addAll(newList)
 }
 
+fun List<String>.toShellCmd() : String {
+    val sb = StringBuilder()
+    for (s in this) {
+        if (s.contains(" ")) {
+            sb.append('"').append(s).append('"')
+        } else {
+            sb.append(s)
+        }
+        sb.append(' ')
+    }
+    sb.deleteCharAt(sb.length - 1)
+    return sb.toString()
+}
+
 fun <T1, T2> ObservableList<T1>.sendUpdatesTo(
     target: DiffObservableList<T2>,
     mapper: (List<T1>) -> List<T2>
diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java
index 95e1364b6..aff566fb3 100644
--- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java
+++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java
@@ -4,6 +4,8 @@ import android.app.Activity;
 import android.app.ProgressDialog;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+
 import com.topjohnwu.magisk.R;
 import com.topjohnwu.magisk.tasks.MagiskInstaller;
 import com.topjohnwu.magisk.utils.RootUtils;
@@ -12,8 +14,6 @@ import com.topjohnwu.superuser.Shell;
 import com.topjohnwu.superuser.internal.UiThreadHandler;
 import com.topjohnwu.superuser.io.SuFile;
 
-import androidx.annotation.NonNull;
-
 public class EnvFixDialog extends CustomAlertDialog {
 
     public EnvFixDialog(@NonNull Activity activity) {
@@ -38,7 +38,7 @@ public class EnvFixDialog extends CustomAlertDialog {
                     pd.dismiss();
                     Utils.toast(success ? R.string.reboot_delay_toast : R.string.setup_fail, Toast.LENGTH_LONG);
                     if (success)
-                        UiThreadHandler.handler.postDelayed(RootUtils::reboot, 5000);
+                        UiThreadHandler.handler.postDelayed(RootUtils.Companion::reboot, 5000);
                 }
             }.exec();
         });