From 4efaaed304411717236d14a71666c194a93772fe Mon Sep 17 00:00:00 2001 From: X1nto Date: Sat, 13 Feb 2021 17:10:57 +0400 Subject: [PATCH] rewrite nonroot split installer and clean up PackageHelper --- .../core/downloader/VancedDownloader.kt | 4 +- .../com/vanced/manager/utils/PackageHelper.kt | 266 +++++------------- 2 files changed, 71 insertions(+), 199 deletions(-) diff --git a/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt b/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt index 2ecdeac2..a5010943 100644 --- a/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt +++ b/app/src/main/java/com/vanced/manager/core/downloader/VancedDownloader.kt @@ -12,7 +12,7 @@ import com.vanced.manager.utils.AppUtils.vancedRootPkg import com.vanced.manager.utils.DownloadHelper.download import com.vanced.manager.utils.DownloadHelper.downloadProgress import com.vanced.manager.utils.PackageHelper.downloadStockCheck -import com.vanced.manager.utils.PackageHelper.installVanced +import com.vanced.manager.utils.PackageHelper.installSplitApkFiles import com.vanced.manager.utils.PackageHelper.installVancedRoot import java.io.File @@ -128,6 +128,6 @@ object VancedDownloader { if (variant == "root") installVancedRoot(context) else - installVanced(context) + installSplitApkFiles(context, "vanced") } } \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/utils/PackageHelper.kt b/app/src/main/java/com/vanced/manager/utils/PackageHelper.kt index 1a8f8d01..1491d4a7 100644 --- a/app/src/main/java/com/vanced/manager/utils/PackageHelper.kt +++ b/app/src/main/java/com/vanced/manager/utils/PackageHelper.kt @@ -23,11 +23,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.io.* -import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern -import kotlin.collections.ArrayList -import kotlin.collections.HashMap object PackageHelper { @@ -162,43 +159,42 @@ object PackageHelper { } fun install(path: String, context: Context) { + val callbackIntent = Intent(context, AppInstallerService::class.java) + val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0) + val packageInstaller = context.packageManager.packageInstaller + val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) + val sessionId: Int + var session: PackageInstaller.Session? = null try { - val callbackIntent = Intent(context, AppInstallerService::class.java) - val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0) - val packageInstaller = context.packageManager.packageInstaller - val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) - val sessionId = packageInstaller.createSession(params) - val session = packageInstaller.openSession(sessionId) + sessionId = packageInstaller.createSession(params) + session = packageInstaller.openSession(sessionId) val inputStream: InputStream = FileInputStream(path) val outputStream = session.openWrite("install", 0, -1) val buffer = ByteArray(65536) - var c: Int - while (inputStream.read(buffer).also { c = it } != -1) { - outputStream.write(buffer, 0, c) + var length: Int + while (inputStream.read(buffer).also { length = it } > 0) { + outputStream.write(buffer, 0, length) } session.fsync(outputStream) inputStream.close() outputStream.close() session.commit(pendingIntent.intentSender) - } catch (e: IOException) { - log(INSTALLER_TAG, e.stackTraceToString()) - sendFailure(e.stackTraceToString(), context) - sendCloseDialog(context) - } catch (e: SecurityException) { + } catch (e: Exception) { log(INSTALLER_TAG, e.stackTraceToString()) sendFailure(e.stackTraceToString(), context) sendCloseDialog(context) + } finally { + session?.close() } - } - private fun installRootMusic(files: ArrayList, context: Context): Boolean { + private fun installRootMusic(files: List, context: Context): Boolean { files.forEach { apk -> if (apk.name != "root.apk") { - val newPath = "/data/local/tmp/${apk.file?.name}" + val newPath = "/data/local/tmp/${apk.name}" //moving apk to tmp folder in order to avoid permission denials - Shell.su("mv ${apk.file?.path} $newPath").exec() + Shell.su("mv ${apk.path} $newPath").exec() val command = Shell.su("pm install $newPath").exec() Shell.su("rm $newPath").exec() if (command.isSuccess) { @@ -216,11 +212,11 @@ object PackageHelper { private fun installRootApp(context: Context, app: String, appVerCode: Int, pkg: String, modApkBool: (fileName: String) -> Boolean) = CoroutineScope(Dispatchers.IO).launch { Shell.getShell { val apkFilesPath = context.getExternalFilesDir("$app/root")?.path - val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) } - if (fileInfoList != null) { - val modApk: FileInfo? = fileInfoList.lastOrNull { modApkBool(it.name) } + val files = File(apkFilesPath.toString()).listFiles()?.toList() + if (files != null) { + val modApk: File? = files.lastOrNull { modApkBool(it.name) } if (modApk != null) { - if (overwriteBase(modApk, fileInfoList, appVerCode, pkg, app, context)) { + if (overwriteBase(modApk, files, appVerCode, pkg, app, context)) { setInstallerPackage(context, pkg, playStorePkg) log(INSTALLER_TAG, "Finished installation") sendRefresh(context) @@ -261,107 +257,34 @@ object PackageHelper { } } - fun installVanced(context: Context): Int { - val apkFolderPath = context.getExternalFilesDir("vanced/nonroot")?.path.toString() + "/" - val nameSizeMap = HashMap() - var totalSize: Long = 0 - var sessionId = 0 - val folder = File(apkFolderPath) - val listOfFiles = folder.listFiles() + fun installSplitApkFiles( + context: Context, + appName: String + ) { + val packageInstaller = context.packageManager.packageInstaller + val folder = File(context.getExternalFilesDir("$appName/nonroot")?.path.toString()) + var session: PackageInstaller.Session? = null + val sessionId: Int + val sessionParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) + val callbackIntent = Intent(context, AppInstallerService::class.java) + val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0) try { - for (listOfFile in listOfFiles!!) { - if (listOfFile.isFile) { - log(INSTALLER_TAG, "installApk: " + listOfFile.name) - nameSizeMap[listOfFile.name] = listOfFile.length() - totalSize += listOfFile.length() + sessionId = packageInstaller.createSession(sessionParams) + session = packageInstaller.openSession(sessionId) + folder.listFiles()?.forEach { apk -> + val inputStream = FileInputStream(apk) + val outputStream = session.openWrite(apk.name, 0, apk.length()) + val buffer = ByteArray(65536) + var length: Int + while (inputStream.read(buffer).also { length = it } > 0) { + outputStream.write(buffer, 0, length) } + session.fsync(outputStream) + inputStream.close() + outputStream.close() } - } catch (e: Exception) { - log(INSTALLER_TAG, e.stackTraceToString()) - sendFailure(e.stackTraceToString(), context) - sendCloseDialog(context) - return -1 - } - val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL) - installParams.setSize(totalSize) - try { - sessionId = context.packageManager.packageInstaller.createSession(installParams) - log(INSTALLER_TAG,"Success: created install session [$sessionId]") - for ((key, value) in nameSizeMap) { - doWriteSession(sessionId, apkFolderPath + key, value, key, context) - } - doCommitSession(sessionId, context) - log(INSTALLER_TAG,"Success") - } catch (e: Exception) { - log(INSTALLER_TAG, e.stackTraceToString()) - sendFailure(e.stackTraceToString(), context) - sendCloseDialog(context) - } - return sessionId - } - - private fun doWriteSession(sessionId: Int, inPath: String?, sizeBytes: Long, splitName: String, context: Context): Int { - var inPathToUse = inPath - var sizeBytesToUse = sizeBytes - if ("-" == inPathToUse) { - inPathToUse = null - } else if (inPathToUse != null) { - val file = File(inPathToUse) - if (file.isFile) - sizeBytesToUse = file.length() - } - var session: PackageInstaller.Session? = null - var inputStream: InputStream? = null - var out: OutputStream? = null - try { - session = context.packageManager.packageInstaller.openSession(sessionId) - if (inPathToUse != null) { - inputStream = FileInputStream(inPathToUse) - } - out = session.openWrite(splitName, 0, sizeBytesToUse) - var total = 0 - val buffer = ByteArray(65536) - var c: Int - while (true) { - c = inputStream!!.read(buffer) - if (c == -1) - break - total += c - out.write(buffer, 0, c) - } - session.fsync(out) - log(INSTALLER_TAG, "Success: streamed $total bytes") - return PackageInstaller.STATUS_SUCCESS - } catch (e: IOException) { - sendFailure(e.stackTraceToString(), context) - sendCloseDialog(context) - log(INSTALLER_TAG, "Error: failed to write; " + e.message) - return PackageInstaller.STATUS_FAILURE - } finally { - try { - out?.close() - inputStream?.close() - session?.close() - } catch (e: IOException) { - log(INSTALLER_TAG, e.stackTraceToString()) - sendFailure(e.stackTraceToString(), context) - sendCloseDialog(context) - } - } - } - - private fun doCommitSession(sessionId: Int, context: Context) { - var session: PackageInstaller.Session? = null - try { - session = context.packageManager.packageInstaller.openSession(sessionId) - val callbackIntent = Intent(context, AppInstallerService::class.java) - val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0) session.commit(pendingIntent.intentSender) - session.close() - log(INSTALLER_TAG, "install request sent") - log(INSTALLER_TAG, "doCommitSession: " + context.packageManager.packageInstaller.mySessions) - log(INSTALLER_TAG, "doCommitSession: after session commit ") - } catch (e: IOException) { + } catch (e: Exception) { log(INSTALLER_TAG, e.stackTraceToString()) sendFailure(e.stackTraceToString(), context) sendCloseDialog(context) @@ -370,7 +293,7 @@ object PackageHelper { } } - private fun installSplitApkFiles(apkFiles: ArrayList, context: Context) : Boolean { + private fun installSplitApkFilesRoot(apkFiles: List?, context: Context) : Boolean { var sessionId: Int? val filenames = arrayOf("black.apk", "dark.apk", "blue.apk", "pink.apk", "hash.json") log(INSTALLER_TAG, "installing split apk files: $apkFiles") @@ -381,12 +304,12 @@ object PackageHelper { sessionIdMatcher.find() sessionId = Integer.parseInt(sessionIdMatcher.group(1)!!) } - apkFiles.forEach { apkFile -> + apkFiles?.forEach { apkFile -> if (!filenames.any { apkFile.name == it }) { - log(INSTALLER_TAG, "installing APK: ${apkFile.name} ${apkFile.fileSize}") - val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name) + log(INSTALLER_TAG, "installing APK: ${apkFile.name} ${apkFile.length()}") + val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.length()}", "$sessionId", apkFile.name) val process: Process = Runtime.getRuntime().exec(command) - val inputPipe = apkFile.getInputStream() + val inputPipe = FileInputStream(apkFile) try { process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) } } catch (e: Exception) { @@ -412,55 +335,11 @@ object PackageHelper { return false } - private fun SimpleDateFormat.tryParse(str: String) = try { - parse(str) != null - } catch (e: Exception) { - false - } - - private fun getFileInfoList(splitApkPath: String): ArrayList { - val parentFile = File(splitApkPath) - val result = ArrayList() - - if (parentFile.exists() && parentFile.canRead()) { - val listFiles = parentFile.listFiles() ?: return ArrayList() - listFiles.mapTo(result) { - FileInfo(it.name, it.length(), it) - } - return result - } - val longLines = Shell.su("ls -l $splitApkPath").exec().out - val pattern = Pattern.compile(" +") - val formatter = SimpleDateFormat("HH:mm", Locale.getDefault()) - longLinesLoop@ for (line in longLines) { - val matcher = pattern.matcher(line) - for (i in 0 until 4) - if (!matcher.find()) - continue@longLinesLoop - val startSizeStr = matcher.end() - matcher.find() - val endSizeStr = matcher.start() - val fileSizeStr = line.substring(startSizeStr, endSizeStr) - while (true) { - val testTimeStr: String = - line.substring(matcher.end(), line.indexOf(' ', matcher.end())) - if (formatter.tryParse(testTimeStr)) { - //found time, so apk is next - val fileName = line.substring(line.indexOf(' ', matcher.end()) + 1) - if (fileName.endsWith("apk")) - result.add(FileInfo(fileName, fileSizeStr.toLong(), File(splitApkPath, fileName))) - break - } - matcher.find() - } - } - return result - } //overwrite stock Vanced/Music private fun overwriteBase( - apkFile: FileInfo, - baseApkFiles: ArrayList, + apkFile: File, + baseApkFiles: List, versionCode: Int, pkg: String, app: String, @@ -468,17 +347,15 @@ object PackageHelper { ): Boolean { if (checkVersion(versionCode, baseApkFiles, pkg, context)) { val path = getPackageDir(context, pkg) - apkFile.file?.let { - val apath = it.absolutePath + val apath = apkFile.absolutePath - setupFolder("$apkInstallPath/${app.capitalize(Locale.ROOT)}") - if (path != null) { - val apkFPath = "$apkInstallPath/${app.capitalize(Locale.ROOT)}/base.apk" - if (moveAPK(apath, apkFPath, pkg, context)) { - if (chConV(apkFPath, context)) { - if (setupScript(apkFPath, path, app, pkg, context)) { - return linkApp(apkFPath, pkg, path) - } + setupFolder("$apkInstallPath/${app.capitalize(Locale.ROOT)}") + if (path != null) { + val apkFPath = "$apkInstallPath/${app.capitalize(Locale.ROOT)}/base.apk" + if (moveAPK(apath, apkFPath, pkg, context)) { + if (chConV(apkFPath, context)) { + if (setupScript(apkFPath, path, app, pkg, context)) { + return linkApp(apkFPath, pkg, path) } } } @@ -517,7 +394,7 @@ object PackageHelper { } //check version and perform action based on result - private fun checkVersion(versionCode: Int, baseApkFiles: ArrayList, pkg: String, context: Context): Boolean { + private fun checkVersion(versionCode: Int, baseApkFiles: List, pkg: String, context: Context): Boolean { log(INSTALLER_TAG, "Checking stock version") val path = getPackageDir(context, pkg) if (path != null) { @@ -551,10 +428,10 @@ object PackageHelper { } //uninstall current update and install base that works with patch - private fun fixHigherVer(apkFiles: ArrayList, pkg: String, context: Context) : Boolean { + private fun fixHigherVer(apkFiles: List, pkg: String, context: Context) : Boolean { log(INSTALLER_TAG, "Downgrading stock") if (uninstallRootApk(pkg)) { - return if (pkg == vancedRootPkg) installSplitApkFiles(apkFiles, context) else installRootMusic(apkFiles, context) + return if (pkg == vancedRootPkg) installSplitApkFilesRoot(apkFiles, context) else installRootMusic(apkFiles, context) } sendFailure(listOf("Failed_Uninstall").toMutableList(), context) sendCloseDialog(context) @@ -562,9 +439,9 @@ object PackageHelper { } //install stock youtube matching vanced version - private fun installStock(baseApkFiles: ArrayList, pkg: String, context: Context): Boolean { + private fun installStock(baseApkFiles: List, pkg: String, context: Context): Boolean { log(INSTALLER_TAG, "Installing stock") - return if (pkg == vancedRootPkg) installSplitApkFiles(baseApkFiles, context) else installRootMusic(baseApkFiles, context) + return if (pkg == vancedRootPkg) installSplitApkFilesRoot(baseApkFiles, context) else installRootMusic(baseApkFiles, context) } //set chcon to apk_data_file @@ -650,18 +527,13 @@ object PackageHelper { } //get path of the installed youtube - fun getPackageDir(context: Context, pkg: String): String? - { + fun getPackageDir(context: Context, pkg: String): String? { val p = getPkgInfo(pkg, context) - return if(p != null) - { + return if (p != null) { p.applicationInfo.sourceDir - } - else - { + } else { val execRes = Shell.su("dumpsys package $pkg | grep codePath").exec() - if(execRes.isSuccess) - { + if (execRes.isSuccess) { val result = execRes.out for (line in result) {