forked from MarcoBuster/Magisk
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:
parent
fba83e2330
commit
6ae2c9387d
|
@ -2,7 +2,6 @@ package com.topjohnwu.magisk.core.download
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.DynAPK
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.core.Info
|
import com.topjohnwu.magisk.core.Info
|
||||||
|
@ -14,7 +13,7 @@ import java.io.File
|
||||||
|
|
||||||
private fun Context.patch(apk: File) {
|
private fun Context.patch(apk: File) {
|
||||||
val patched = File(apk.parent, "patched.apk")
|
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()
|
apk.delete()
|
||||||
patched.renameTo(apk)
|
patched.renameTo(apk)
|
||||||
}
|
}
|
||||||
|
@ -28,24 +27,21 @@ private fun BaseDownloader.notifyHide(id: Int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) {
|
suspend fun BaseDownloader.handleAPK(subject: Subject.Manager) {
|
||||||
|
if (!isRunningAsStub)
|
||||||
|
return
|
||||||
val apk = subject.file.toFile()
|
val apk = subject.file.toFile()
|
||||||
val id = subject.notifyID()
|
val id = subject.notifyID()
|
||||||
if (isRunningAsStub) {
|
// Move to upgrade location
|
||||||
// Move to upgrade location
|
apk.copyTo(DynAPK.update(this), overwrite = true)
|
||||||
apk.copyTo(DynAPK.update(this), overwrite = true)
|
apk.delete()
|
||||||
apk.delete()
|
if (Info.stub!!.version < subject.stub.versionCode) {
|
||||||
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) {
|
|
||||||
notifyHide(id)
|
notifyHide(id)
|
||||||
|
// Also upgrade stub
|
||||||
|
service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
|
||||||
patch(apk)
|
patch(apk)
|
||||||
|
} else {
|
||||||
|
// Simply relaunch the app
|
||||||
|
stopSelf()
|
||||||
|
relaunchApp(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,14 @@ package com.topjohnwu.magisk.core.tasks
|
||||||
import android.app.ProgressDialog
|
import android.app.ProgressDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build.VERSION.SDK_INT
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
import com.topjohnwu.magisk.BuildConfig.APPLICATION_ID
|
||||||
import com.topjohnwu.magisk.DynAPK
|
import com.topjohnwu.magisk.DynAPK
|
||||||
import com.topjohnwu.magisk.R
|
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.AXML
|
||||||
import com.topjohnwu.magisk.core.utils.Keygen
|
import com.topjohnwu.magisk.core.utils.Keygen
|
||||||
import com.topjohnwu.magisk.data.repository.NetworkService
|
import com.topjohnwu.magisk.data.repository.NetworkService
|
||||||
|
@ -67,11 +69,11 @@ object HideAPK {
|
||||||
|
|
||||||
fun patch(
|
fun patch(
|
||||||
context: Context,
|
context: Context,
|
||||||
apk: String, out: String,
|
apk: File, out: File,
|
||||||
pkg: String, label: CharSequence
|
pkg: String, label: CharSequence
|
||||||
): Boolean {
|
): Boolean {
|
||||||
try {
|
try {
|
||||||
val jar = JarMap.open(apk)
|
val jar = JarMap.open(apk, true)
|
||||||
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
val je = jar.getJarEntry(ANDROID_MANIFEST)
|
||||||
val xml = AXML(jar.getRawData(je))
|
val xml = AXML(jar.getRawData(je))
|
||||||
|
|
||||||
|
@ -91,17 +93,12 @@ object HideAPK {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun patchAndHide(context: Context, label: String): Boolean {
|
private suspend fun patchAndHide(context: Context, label: String): Boolean {
|
||||||
val src = if (!isRunningAsStub && SDK_INT >= 28) {
|
val stub = File(context.cacheDir, "stub.apk")
|
||||||
val stub = File(context.cacheDir, "stub.apk")
|
try {
|
||||||
try {
|
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
|
||||||
svc.fetchFile(Info.remote.stub.link).byteStream().writeTo(stub)
|
} catch (e: IOException) {
|
||||||
} catch (e: IOException) {
|
Timber.e(e)
|
||||||
Timber.e(e)
|
return false
|
||||||
return false
|
|
||||||
}
|
|
||||||
stub.path
|
|
||||||
} else {
|
|
||||||
context.packageCodePath
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new random package name and signature
|
// Generate a new random package name and signature
|
||||||
|
@ -109,7 +106,7 @@ object HideAPK {
|
||||||
val pkg = genPackageName()
|
val pkg = genPackageName()
|
||||||
Config.keyStoreRaw = ""
|
Config.keyStoreRaw = ""
|
||||||
|
|
||||||
if (!patch(context, src, repack.path, pkg, label))
|
if (!patch(context, stub, repack, pkg, label))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
// Install the application
|
// Install the application
|
||||||
|
@ -142,20 +139,8 @@ object HideAPK {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun downloadAndRestore(context: Context): Boolean {
|
private fun restoreImpl(context: Context): Boolean {
|
||||||
val apk = if (isRunningAsStub) {
|
val apk = DynAPK.current(context)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Shell.su("adb_pm_install $apk").exec().isSuccess)
|
if (!Shell.su("adb_pm_install $apk").exec().isSuccess)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -176,7 +161,7 @@ object HideAPK {
|
||||||
val dialog = ProgressDialog.show(context, context.getString(R.string.restore_img_msg), "", true)
|
val dialog = ProgressDialog.show(context, context.getString(R.string.restore_img_msg), "", true)
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
downloadAndRestore(context)
|
restoreImpl(context)
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Utils.toast(R.string.restore_manager_fail_toast, Toast.LENGTH_LONG)
|
Utils.toast(R.string.restore_manager_fail_toast, Toast.LENGTH_LONG)
|
||||||
|
|
|
@ -59,8 +59,12 @@ class SettingsViewModel(
|
||||||
))
|
))
|
||||||
if (Info.env.isActive) {
|
if (Info.env.isActive) {
|
||||||
list.add(ClearRepoCache)
|
list.add(ClearRepoCache)
|
||||||
if (Const.USER_ID == 0 && Info.isConnected.get())
|
if (Build.VERSION.SDK_INT >= 21 && Const.USER_ID == 0) {
|
||||||
list.add(if (hidden) Restore else Hide)
|
if (hidden)
|
||||||
|
list.add(Restore)
|
||||||
|
else if (Info.isConnected.get())
|
||||||
|
list.add(Hide)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Magisk
|
// Magisk
|
||||||
|
|
|
@ -19,18 +19,10 @@ public abstract class JarMap implements Closeable {
|
||||||
|
|
||||||
LinkedHashMap<String, JarEntry> entryMap;
|
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 {
|
public static JarMap open(File file, boolean verify) throws IOException {
|
||||||
return new FileMap(file, verify, ZipFile.OPEN_READ);
|
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 {
|
public static JarMap open(InputStream is, boolean verify) throws IOException {
|
||||||
return new StreamMap(is, verify);
|
return new StreamMap(is, verify);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user