rewrite nonroot split installer and clean up PackageHelper
This commit is contained in:
parent
3896e7daad
commit
4efaaed304
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue