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

View File

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

View File

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