Directly stream apk into install session

This commit is contained in:
vvb2060 2022-02-17 15:07:34 +08:00 committed by John Wu
parent 6df42a4be7
commit d11038f3de
3 changed files with 28 additions and 35 deletions

View File

@ -26,7 +26,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -100,20 +99,16 @@ class DownloadService : NotificationService() {
val apk = subject.file.toFile() val apk = subject.file.toFile()
service.fetchFile(subject.stub.link).byteStream().writeTo(apk) service.fetchFile(subject.stub.link).byteStream().writeTo(apk)
// Patch // Patch and install
val patched = File(apk.parent, "patched.apk") val session = APKInstall.startSession(this)
val label = applicationInfo.nonLocalizedLabel session.openStream(this).use {
if (!HideAPK.patch(this, apk, patched, packageName, label)) { val label = applicationInfo.nonLocalizedLabel
throw IOException("HideAPK patch error") if (!HideAPK.patch(this, apk, it, packageName, label)) {
throw IOException("HideAPK patch error")
}
} }
apk.delete() apk.delete()
// Install
val session = APKInstall.startSession(this)
patched.inputStream().copyAndClose(session.openStream(this))
subject.intent = session.waitIntent() subject.intent = session.waitIntent()
patched.delete()
} else { } else {
ActivityTracker.foreground?.let { ActivityTracker.foreground?.let {
// Relaunch the process if we are foreground // Relaunch the process if we are foreground

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.core.tasks package com.topjohnwu.magisk.core.tasks
import android.app.Activity import android.app.Activity
import android.app.ProgressDialog
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.widget.Toast import android.widget.Toast
@ -26,8 +25,8 @@ import kotlinx.coroutines.Runnable
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.io.File import java.io.File
import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
import java.io.OutputStream
import java.security.SecureRandom import java.security.SecureRandom
object HideAPK { object HideAPK {
@ -66,29 +65,29 @@ object HideAPK {
fun patch( fun patch(
context: Context, context: Context,
apk: File, out: File, apk: File, out: OutputStream,
pkg: String, label: CharSequence pkg: String, label: CharSequence
): Boolean { ): Boolean {
val info = context.packageManager.getPackageArchiveInfo(apk.path, 0) ?: return false val info = context.packageManager.getPackageArchiveInfo(apk.path, 0) ?: return false
val name = info.applicationInfo.nonLocalizedLabel.toString() val name = info.applicationInfo.nonLocalizedLabel.toString()
try { try {
val jar = JarMap.open(apk, true) JarMap.open(apk, true).use { jar ->
val je = jar.getJarEntry(ANDROID_MANIFEST) val je = jar.getJarEntry(ANDROID_MANIFEST)
val xml = AXML(jar.getRawData(je)) val xml = AXML(jar.getRawData(je))
if (!xml.findAndPatch(APPLICATION_ID to pkg, name to label.toString())) if (!xml.findAndPatch(APPLICATION_ID to pkg, name to label.toString()))
return false return false
// Write apk changes // Write apk changes
jar.getOutputStream(je).write(xml.bytes) jar.getOutputStream(je).use { it.write(xml.bytes) }
val keys = Keygen(context) val keys = Keygen(context)
SignApk.sign(keys.cert, keys.key, jar, FileOutputStream(out)) SignApk.sign(keys.cert, keys.key, jar, out)
return true
}
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e)
return false return false
} }
return true
} }
private fun launchApp(activity: Activity, pkg: String) { private fun launchApp(activity: Activity, pkg: String) {
@ -116,19 +115,18 @@ object HideAPK {
} }
// Generate a new random package name and signature // Generate a new random package name and signature
val repack = File(activity.cacheDir, "patched.apk")
val pkg = genPackageName() val pkg = genPackageName()
Config.keyStoreRaw = "" Config.keyStoreRaw = ""
if (!patch(activity, stub, repack, pkg, label))
return false
// Install and auto launch app // Install and auto launch app
val session = APKInstall.startSession(activity, pkg, onFailure) { val session = APKInstall.startSession(activity, pkg, onFailure) {
launchApp(activity, pkg) launchApp(activity, pkg)
} }
try { try {
session.install(activity, repack) val success = session.openStream(activity).use {
patch(activity, stub, it, pkg, label)
}
if (!success) return false
} catch (e: IOException) { } catch (e: IOException) {
Timber.e(e) Timber.e(e)
return false return false
@ -139,7 +137,7 @@ object HideAPK {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
suspend fun hide(activity: Activity, label: String) { suspend fun hide(activity: Activity, label: String) {
val dialog = ProgressDialog(activity).apply { val dialog = android.app.ProgressDialog(activity).apply {
setTitle(activity.getString(R.string.hide_app_title)) setTitle(activity.getString(R.string.hide_app_title))
isIndeterminate = true isIndeterminate = true
setCancelable(false) setCancelable(false)
@ -157,7 +155,7 @@ object HideAPK {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
suspend fun restore(activity: Activity) { suspend fun restore(activity: Activity) {
val dialog = ProgressDialog(activity).apply { val dialog = android.app.ProgressDialog(activity).apply {
setTitle(activity.getString(R.string.restore_img_msg)) setTitle(activity.getString(R.string.restore_img_msg))
isIndeterminate = true isIndeterminate = true
setCancelable(false) setCancelable(false)

View File

@ -492,7 +492,7 @@ public class SignApk {
} }
public static void sign(X509Certificate cert, PrivateKey key, public static void sign(X509Certificate cert, PrivateKey key,
JarMap inputJar, FileOutputStream outputFile) throws Exception { JarMap inputJar, OutputStream outputStream) throws Exception {
int alignment = 4; int alignment = 4;
int hashes = 0; int hashes = 0;
@ -531,7 +531,7 @@ public class SignApk {
// This assumes outputChunks are array-backed. To avoid this assumption, the // This assumes outputChunks are array-backed. To avoid this assumption, the
// code could be rewritten to use FileChannel. // code could be rewritten to use FileChannel.
for (ByteBuffer outputChunk : outputChunks) { for (ByteBuffer outputChunk : outputChunks) {
outputFile.write(outputChunk.array(), outputStream.write(outputChunk.array(),
outputChunk.arrayOffset() + outputChunk.position(), outputChunk.remaining()); outputChunk.arrayOffset() + outputChunk.position(), outputChunk.remaining());
outputChunk.position(outputChunk.limit()); outputChunk.position(outputChunk.limit());
} }