Use stub APK hiding method for Android 5.0+

At the same time, disable app hiding on devices lower than 5.0
to simplify the logic in the app. By doing so, a hidden app always
implies running as stub.
This commit is contained in:
topjohnwu 2021-01-26 04:17:37 -08:00
parent fba83e2330
commit 6ae2c9387d
4 changed files with 35 additions and 58 deletions

View File

@ -2,7 +2,6 @@ package com.topjohnwu.magisk.core.download
import android.content.Context
import androidx.core.net.toFile
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
@ -14,7 +13,7 @@ import java.io.File
private fun Context.patch(apk: File) {
val patched = File(apk.parent, "patched.apk")
HideAPK.patch(this, apk.path, patched.path, packageName, applicationInfo.nonLocalizedLabel)
HideAPK.patch(this, apk, patched, packageName, applicationInfo.nonLocalizedLabel)
apk.delete()
patched.renameTo(apk)
}
@ -28,24 +27,21 @@ private fun BaseDownloader.notifyHide(id: Int) {
}
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) {
if (!isRunningAsStub)
return
val apk = subject.file.toFile()
val id = subject.notifyID()
if (isRunningAsStub) {
// Move to upgrade location
apk.copyTo(DynAPK.update(this), overwrite = true)
apk.delete()
if (Info.stub!!.version < subject.stub.versionCode) {
notifyHide(id)
// Also upgrade stub
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
patch(apk)
} else {
// Simply relaunch the app
stopSelf()
relaunchApp(this)
}
} else if (packageName != BuildConfig.APPLICATION_ID) {
// Move to upgrade location
apk.copyTo(DynAPK.update(this), overwrite = true)
apk.delete()
if (Info.stub!!.version < subject.stub.versionCode) {
notifyHide(id)
// Also upgrade stub
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
patch(apk)
} else {
// Simply relaunch the app
stopSelf()
relaunchApp(this)
}
}

View File

@ -3,12 +3,14 @@ package com.topjohnwu.magisk.core.tasks
import android.app.ProgressDialog
import android.content.Context
import android.content.Intent
import android.os.Build.VERSION.SDK_INT
import android.widget.Toast
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.*
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.Provider
import com.topjohnwu.magisk.core.utils.AXML
import com.topjohnwu.magisk.core.utils.Keygen
import com.topjohnwu.magisk.data.repository.NetworkService
@ -67,11 +69,11 @@ object HideAPK {
fun patch(
context: Context,
apk: String, out: String,
apk: File, out: File,
pkg: String, label: CharSequence
): Boolean {
try {
val jar = JarMap.open(apk)
val jar = JarMap.open(apk, true)
val je = jar.getJarEntry(ANDROID_MANIFEST)
val xml = AXML(jar.getRawData(je))
@ -91,17 +93,12 @@ object HideAPK {
}
private suspend fun patchAndHide(context: Context, label: String): Boolean {
val src = if (!isRunningAsStub && SDK_INT >= 28) {
val stub = File(context.cacheDir, "stub.apk")
try {
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
} catch (e: IOException) {
Timber.e(e)
return false
}
stub.path
} else {
context.packageCodePath
val stub = File(context.cacheDir, "stub.apk")
try {
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
} catch (e: IOException) {
Timber.e(e)
return false
}
// Generate a new random package name and signature
@ -109,7 +106,7 @@ object HideAPK {
val pkg = genPackageName()
Config.keyStoreRaw = ""
if (!patch(context, src, repack.path, pkg, label))
if (!patch(context, stub, repack, pkg, label))
return false
// Install the application
@ -142,20 +139,8 @@ object HideAPK {
}
}
private suspend fun downloadAndRestore(context: Context): Boolean {
val apk = if (isRunningAsStub) {
DynAPK.current(context)
} else {
File(context.cacheDir, "manager.apk").also { apk ->
try {
svc.fetchFile(Info.remote.magisk.link).byteStream().writeTo(apk)
} catch (e: IOException) {
Timber.e(e)
return false
}
}
}
private fun restoreImpl(context: Context): Boolean {
val apk = DynAPK.current(context)
if (!Shell.su("adb_pm_install $apk").exec().isSuccess)
return false
@ -176,7 +161,7 @@ object HideAPK {
val dialog = ProgressDialog.show(context, context.getString(R.string.restore_img_msg), "", true)
GlobalScope.launch {
val result = withContext(Dispatchers.IO) {
downloadAndRestore(context)
restoreImpl(context)
}
if (!result) {
Utils.toast(R.string.restore_manager_fail_toast, Toast.LENGTH_LONG)

View File

@ -59,8 +59,12 @@ class SettingsViewModel(
))
if (Info.env.isActive) {
list.add(ClearRepoCache)
if (Const.USER_ID == 0 && Info.isConnected.get())
list.add(if (hidden) Restore else Hide)
if (Build.VERSION.SDK_INT >= 21 && Const.USER_ID == 0) {
if (hidden)
list.add(Restore)
else if (Info.isConnected.get())
list.add(Hide)
}
}
// Magisk

View File

@ -19,18 +19,10 @@ public abstract class JarMap implements Closeable {
LinkedHashMap<String, JarEntry> entryMap;
public static JarMap open(String file) throws IOException {
return new FileMap(new File(file), true, ZipFile.OPEN_READ);
}
public static JarMap open(File file, boolean verify) throws IOException {
return new FileMap(file, verify, ZipFile.OPEN_READ);
}
public static JarMap open(String file, boolean verify) throws IOException {
return new FileMap(new File(file), verify, ZipFile.OPEN_READ);
}
public static JarMap open(InputStream is, boolean verify) throws IOException {
return new StreamMap(is, verify);
}