diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c95d10abb..10d54c59d 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -32,7 +32,7 @@ -keep,allowobfuscation class * extends com.topjohnwu.magisk.base.DelegateWorker # BootSigner --keepclassmembers class com.topjohnwu.signing.BootSigner { *; } +-keep class a.a { *; } # Strip logging -assumenosideeffects class timber.log.Timber.Tree { *; } diff --git a/app/src/main/java/a/a.java b/app/src/main/java/a/a.java index 5ccb6738b..23927a021 100644 --- a/app/src/main/java/a/a.java +++ b/app/src/main/java/a/a.java @@ -1,18 +1,21 @@ package a; -import androidx.annotation.Keep; import androidx.core.app.AppComponentFactory; import com.topjohnwu.magisk.utils.PatchAPK; import com.topjohnwu.signing.BootSigner; -@Keep public class a extends AppComponentFactory { + @Deprecated public static boolean patchAPK(String in, String out, String pkg) { return PatchAPK.patch(in, out, pkg); } + public static boolean patchAPK(String in, String out, String pkg, String label) { + return PatchAPK.patch(in, out, pkg, label); + } + public static void main(String[] args) throws Exception { BootSigner.main(args); } diff --git a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt index bc8e85dff..f75a8a0c1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/download/ManagerUpgrade.kt @@ -4,7 +4,6 @@ import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.cmp -import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Restore import com.topjohnwu.magisk.model.entity.internal.Configuration.APK.Upgrade import com.topjohnwu.magisk.model.entity.internal.DownloadSubject @@ -12,7 +11,6 @@ import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.utils.PatchAPK import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.superuser.Shell -import timber.log.Timber import java.io.File private fun RemoteFileService.patchPackage(apk: File, id: Int) { @@ -24,17 +22,7 @@ private fun RemoteFileService.patchPackage(apk: File, id: Int) { .setContentText("") } val patched = File(apk.parent, "patched.apk") - try { - // Try using the new APK to patch itself - val loader = DynamicClassLoader(apk) - loader.loadClass("a.a") - .getMethod("patchAPK", String::class.java, String::class.java, String::class.java) - .invoke(null, apk.path, patched.path, packageName) - } catch (e: Exception) { - Timber.e(e) - // Fallback to use the current implementation - PatchAPK.patch(apk.path, patched.path, packageName) - } + PatchAPK.patch(apk, patched, packageName, applicationInfo.nonLocalizedLabel.toString()) apk.delete() patched.renameTo(apk) } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt index c67ce091a..9da82ee24 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt @@ -67,7 +67,8 @@ class SettingsFragment : BasePreferenceFragment() { val suCategory = findPreference("superuser")!! val hideManager = findPreference("hide")!! hideManager.setOnPreferenceClickListener { - PatchAPK.hideManager(requireContext()) + // TODO: Add UI to allow user to customize app name + PatchAPK.hideManager(requireContext(), "Manager") true } val restoreManager = findPreference("restore") @@ -326,4 +327,4 @@ class SettingsFragment : BasePreferenceFragment() { .setNegativeButton(R.string.close, null) .show() } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt index f70dc291f..edd1cff66 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.kt @@ -3,6 +3,7 @@ package com.topjohnwu.magisk.utils import android.content.Context import android.widget.Toast import com.topjohnwu.magisk.* +import com.topjohnwu.magisk.extensions.DynamicClassLoader import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.ui.SplashActivity import com.topjohnwu.magisk.view.Notifications @@ -46,52 +47,38 @@ object PatchAPK { } private fun findAndPatch(xml: ByteArray, from: String, to: String): Boolean { - if (from.length != to.length) + if (to.length > from.length) return false val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer() val offList = mutableListOf() var i = 0 - while (i < buf.length - from.length) { - var match = true - for (j in 0 until from.length) { + loop@ while (i < buf.length - from.length) { + for (j in from.indices) { if (buf.get(i + j) != from[j]) { - match = false - break + ++i + continue@loop } } - if (match) { - offList.add(i) - i += from.length - } - ++i + offList.add(i) + i += from.length } if (offList.isEmpty()) return false + + val toBuf = to.toCharArray().copyOf(from.length) for (off in offList) { buf.position(off) - buf.put(to) + buf.put(toBuf) } return true } - private fun findAndPatch(xml: ByteArray, a: Int, b: Int): Boolean { - val buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer() - val len = xml.size / 4 - for (i in 0 until len) { - if (buf.get(i) == a) { - buf.put(i, b) - return true - } - } - return false - } - - private fun patchAndHide(context: Context): Boolean { + private fun patchAndHide(context: Context, label: String): Boolean { // Generate a new app with random package name val repack = File(context.filesDir, "patched.apk") val pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length) - if (!patch(context.packageCodePath, repack.path, pkg)) + if (!patch(context.packageCodePath, repack.path, pkg, label)) return false // Install the application @@ -107,14 +94,15 @@ object PatchAPK { } @JvmStatic - fun patch(apk: String, out: String, pkg: String): Boolean { + @JvmOverloads + fun patch(apk: String, out: String, pkg: String, label: String = "Manager"): Boolean { try { val jar = JarMap(apk) val je = jar.getJarEntry(Const.ANDROID_MANIFEST) val xml = jar.getRawData(je) if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) || - !findAndPatch(xml, R.string.app_name, R.string.re_app_name)) + !findAndPatch(xml, "Magisk Manager", label)) return false // Write apk changes @@ -128,11 +116,32 @@ object PatchAPK { return true } - fun hideManager(context: Context) { + fun patch(apk: File, out: File, pkg: String, label: String): Boolean { + try { + // Try using the new APK to patch itself + val loader = DynamicClassLoader(apk) + val cls = loader.loadClass("a.a") + + for (m in cls.declaredMethods) { + val pars = m.parameterTypes + if (pars.size == 4 && pars[0] == String::class.java) { + return m.invoke(null, apk.path, out.path, pkg, label) as Boolean + } + } + throw Exception("No matching method found") + } catch (e: Exception) { + Timber.e(e) + // Fallback to use the current implementation + patch(apk.path, out.path, pkg, label) + } + return false + } + + fun hideManager(context: Context, label: String) { Completable.fromAction { val progress = Notifications.progress(context, context.getString(R.string.hide_manager_title)) Notifications.mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build()) - if (!patchAndHide(context)) + if (!patchAndHide(context, label)) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG) Notifications.mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID) }.subscribeK() diff --git a/shared/src/main/AndroidManifest.xml b/shared/src/main/AndroidManifest.xml index 73a5f831e..c42a2dd7f 100644 --- a/shared/src/main/AndroidManifest.xml +++ b/shared/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@