Commit 78daa2eb authored by topjohnwu's avatar topjohnwu

Do not use string resources for app label

This not only simplifies hiding stub APKs (no resource IDs involved),
but also opens the opportunity to allow users to customize whatever
app name they want after it is hidden.
parent 40eda05a
......@@ -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 { *; }
......
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);
}
......
......@@ -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)
}
......
......@@ -67,7 +67,8 @@ class SettingsFragment : BasePreferenceFragment() {
val suCategory = findPreference<PreferenceCategory>("superuser")!!
val hideManager = findPreference<Preference>("hide")!!
hideManager.setOnPreferenceClickListener {
PatchAPK.hideManager(requireContext())
// TODO: Add UI to allow user to customize app name
PatchAPK.hideManager(requireContext(), "Manager")
true
}
val restoreManager = findPreference<Preference>("restore")
......
......@@ -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<Int>()
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
}
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()
......
......@@ -8,7 +8,7 @@
<application
android:icon="@drawable/ic_launcher"
android:installLocation="internalOnly"
android:label="@string/app_name"
android:label="Magisk Manager"
android:supportsRtl="true">
<activity
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment