rewrite nonroot split installer and clean up PackageHelper

This commit is contained in:
X1nto 2021-02-13 17:10:57 +04:00
parent 3896e7daad
commit 4efaaed304
2 changed files with 71 additions and 199 deletions

View File

@ -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")
}
}

View File

@ -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<FileInfo>, context: Context): Boolean {
private fun installRootMusic(files: List<File>, 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<String, Long>()
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<FileInfo>, context: Context) : Boolean {
private fun installSplitApkFilesRoot(apkFiles: List<File>?, 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<FileInfo> {
val parentFile = File(splitApkPath)
val result = ArrayList<FileInfo>()
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<FileInfo>,
apkFile: File,
baseApkFiles: List<File>,
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<FileInfo>, pkg: String, context: Context): Boolean {
private fun checkVersion(versionCode: Int, baseApkFiles: List<File>, 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<FileInfo>, pkg: String, context: Context) : Boolean {
private fun fixHigherVer(apkFiles: List<File>, 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<FileInfo>, pkg: String, context: Context): Boolean {
private fun installStock(baseApkFiles: List<File>, 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)
{