start migrating packagemanager to di

This commit is contained in:
X1nto 2022-03-05 12:39:07 +04:00
parent e010ed0e80
commit a1662bab5a
17 changed files with 702 additions and 213 deletions

View File

@ -19,6 +19,7 @@ class ManagerApplication : Application() {
datasourceModule,
downloaderModule,
installerModule,
managerModule,
networkModule,
repositoryModule,
serviceModule,

View File

@ -1,8 +1,6 @@
package com.vanced.manager.di
import android.content.Context
import com.vanced.manager.repository.source.PkgInfoDatasource
import com.vanced.manager.repository.source.PkgInfoDatasourceImpl
import com.vanced.manager.repository.source.PreferenceDatasource
import com.vanced.manager.repository.source.PreferenceDatasourceImpl
import org.koin.android.ext.koin.androidContext
@ -10,14 +8,6 @@ import org.koin.dsl.module
val datasourceModule = module {
fun providePkgInfoDatasource(
context: Context
): PkgInfoDatasource {
return PkgInfoDatasourceImpl(
packageManager = context.packageManager
)
}
fun providePreferenceDatasource(
context: Context
): PreferenceDatasource {
@ -29,6 +19,5 @@ val datasourceModule = module {
)
}
single { providePkgInfoDatasource(androidContext()) }
single { providePreferenceDatasource(androidContext()) }
}

View File

@ -4,36 +4,48 @@ import android.content.Context
import com.vanced.manager.installer.impl.MicrogInstaller
import com.vanced.manager.installer.impl.MusicInstaller
import com.vanced.manager.installer.impl.VancedInstaller
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.RootPackageManager
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val installerModule = module {
fun provideVancedInstaller(
context: Context
context: Context,
nonrootPackageManager: NonrootPackageManager,
rootPackageManager: RootPackageManager
): VancedInstaller {
return VancedInstaller(
context = context
context = context,
nonrootPackageManager = nonrootPackageManager,
rootPackageManager = rootPackageManager
)
}
fun provideMusicInstaller(
context: Context
context: Context,
nonrootPackageManager: NonrootPackageManager,
rootPackageManager: RootPackageManager
): MusicInstaller {
return MusicInstaller(
context = context
context = context,
nonrootPackageManager = nonrootPackageManager,
rootPackageManager = rootPackageManager
)
}
fun provideMicrogInstaller(
context: Context
context: Context,
nonrootPackageManager: NonrootPackageManager,
): MicrogInstaller {
return MicrogInstaller(
context = context
context = context,
nonrootPackageManager = nonrootPackageManager
)
}
single { provideVancedInstaller(androidContext()) }
single { provideMusicInstaller(androidContext()) }
single { provideMicrogInstaller(androidContext()) }
single { provideVancedInstaller(androidContext(), get(), get()) }
single { provideMusicInstaller(androidContext(), get(), get()) }
single { provideMicrogInstaller(androidContext(), get()) }
}

View File

@ -0,0 +1,25 @@
package com.vanced.manager.di
import android.content.Context
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.RootPackageManager
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val managerModule = module {
fun provideNonrootPackageManager(
context: Context
): NonrootPackageManager {
return NonrootPackageManager(
context = context
)
}
fun provideRootPackageManager(): RootPackageManager {
return RootPackageManager()
}
single { provideNonrootPackageManager(androidContext()) }
single { provideRootPackageManager() }
}

View File

@ -5,7 +5,8 @@ import com.vanced.manager.repository.AppRepository
import com.vanced.manager.repository.AppRepositoryImpl
import com.vanced.manager.repository.PreferenceRepository
import com.vanced.manager.repository.PreferenceRepositoryImpl
import com.vanced.manager.repository.source.PkgInfoDatasource
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.RootPackageManager
import com.vanced.manager.repository.source.PreferenceDatasource
import org.koin.dsl.module
@ -13,11 +14,13 @@ val repositoryModule = module {
fun provideGithubRepository(
githubService: GithubService,
pkgInfoDatasource: PkgInfoDatasource
nonrootPackageManager: NonrootPackageManager,
rootPackageManager: RootPackageManager,
): AppRepository {
return AppRepositoryImpl(
githubService = githubService,
pkgInfoDatasource = pkgInfoDatasource
nonrootPackageManager = nonrootPackageManager,
rootPackageManager = rootPackageManager
)
}
@ -29,6 +32,6 @@ val repositoryModule = module {
)
}
single { provideGithubRepository(get(), get()) }
single { provideGithubRepository(get(), get(), get()) }
single { providePreferenceRepository(get()) }
}

View File

@ -1,11 +1,11 @@
package com.vanced.manager.installer.base
import com.vanced.manager.installer.util.PMRootResult
import com.vanced.manager.repository.manager.PackageManagerResult
abstract class AppInstaller {
abstract fun install(appVersions: List<String>?)
abstract suspend fun install(appVersions: List<String>?)
abstract fun installRoot(appVersions: List<String>?): PMRootResult<Nothing>
abstract suspend fun installRoot(appVersions: List<String>?): PackageManagerResult<Nothing>
}

View File

@ -3,23 +3,22 @@ package com.vanced.manager.installer.impl
import android.content.Context
import com.vanced.manager.downloader.util.getMicrogPath
import com.vanced.manager.installer.base.AppInstaller
import com.vanced.manager.installer.util.PM
import com.vanced.manager.installer.util.PMRootResult
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.PackageManagerResult
import java.io.File
class MicrogInstaller(
private val context: Context
private val context: Context,
private val nonrootPackageManager: NonrootPackageManager,
) : AppInstaller() {
override fun install(
appVersions: List<String>?
) {
val musicApk = File(getMicrogPath(context) + "/microg.apk")
override suspend fun install(appVersions: List<String>?) {
val musicApk = File(getMicrogPath(context), "microg.apk")
PM.installApp(musicApk, context)
nonrootPackageManager.installApp(musicApk)
}
override fun installRoot(appVersions: List<String>?): PMRootResult<Nothing> {
override suspend fun installRoot(appVersions: List<String>?): PackageManagerResult<Nothing> {
throw IllegalAccessException("Vanced microG does not have a root installer")
}

View File

@ -5,23 +5,23 @@ import com.vanced.manager.domain.model.AppData
import com.vanced.manager.downloader.util.getStockYoutubeMusicPath
import com.vanced.manager.downloader.util.getVancedYoutubeMusicPath
import com.vanced.manager.installer.base.AppInstaller
import com.vanced.manager.installer.util.PM
import com.vanced.manager.installer.util.PMRoot
import com.vanced.manager.installer.util.PMRootResult
import com.vanced.manager.installer.util.RootPatchHelper
import com.vanced.manager.preferences.holder.managerVariantPref
import com.vanced.manager.preferences.holder.musicVersionPref
import com.vanced.manager.preferences.holder.vancedVersionPref
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.PackageManagerResult
import com.vanced.manager.repository.manager.RootPackageManager
import com.vanced.manager.util.getLatestOrProvidedAppVersion
import java.io.File
class MusicInstaller(
private val context: Context
private val context: Context,
private val rootPackageManager: RootPackageManager,
private val nonrootPackageManager: NonrootPackageManager,
) : AppInstaller() {
override fun install(
appVersions: List<String>?
) {
override suspend fun install(appVersions: List<String>?) {
val absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions)
val musicApk = File(
@ -32,33 +32,33 @@ class MusicInstaller(
) + "/music.apk"
)
PM.installApp(musicApk, context)
nonrootPackageManager.installApp(musicApk)
}
override fun installRoot(appVersions: List<String>?): PMRootResult<Nothing> {
override suspend fun installRoot(appVersions: List<String>?): PackageManagerResult<Nothing> {
val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
val stockPath = getStockYoutubeMusicPath(absoluteVersion, context) + "/base.apk"
val vancedPath = getVancedYoutubeMusicPath(absoluteVersion, "root", context) + "/base.apk"
val stock = File(getStockYoutubeMusicPath(absoluteVersion, context), "base.apk")
val vanced = File(getVancedYoutubeMusicPath(absoluteVersion, "root", context), "base.apk")
val prepareStock = RootPatchHelper.prepareStock(
stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC,
stockVersion = absoluteVersion
) {
PMRoot.installApp(stockPath)
rootPackageManager.installApp(stock)
}
if (prepareStock.isError)
return prepareStock
val patchStock = RootPatchHelper.patchStock(
patchPath = vancedPath,
patchPath = vanced.absolutePath,
stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC,
app = APP_KEY
)
if (patchStock.isError)
return patchStock
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
companion object {

View File

@ -5,41 +5,41 @@ import com.vanced.manager.domain.model.AppData
import com.vanced.manager.downloader.util.getStockYoutubePath
import com.vanced.manager.downloader.util.getVancedYoutubePath
import com.vanced.manager.installer.base.AppInstaller
import com.vanced.manager.installer.util.PM
import com.vanced.manager.installer.util.PMRoot
import com.vanced.manager.installer.util.PMRootResult
import com.vanced.manager.installer.util.RootPatchHelper
import com.vanced.manager.preferences.holder.vancedVersionPref
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.PackageManagerResult
import com.vanced.manager.repository.manager.RootPackageManager
import com.vanced.manager.util.getLatestOrProvidedAppVersion
import java.io.File
class VancedInstaller(
private val context: Context
private val context: Context,
private val rootPackageManager: RootPackageManager,
private val nonrootPackageManager: NonrootPackageManager,
) : AppInstaller() {
override fun install(
appVersions: List<String>?
) {
override suspend fun install(appVersions: List<String>?) {
val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
val apks = File(getVancedYoutubePath(absoluteVersion, "nonroot", context))
.listFiles()
PM.installSplitApp(apks!!, context)
nonrootPackageManager.installSplitApp(apks!!)
}
override fun installRoot(appVersions: List<String>?): PMRootResult<Nothing> {
override suspend fun installRoot(appVersions: List<String>?): PackageManagerResult<Nothing> {
val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
val stockApks = File(getStockYoutubePath(absoluteVersion, context))
.listFiles()?.map { it.absolutePath }
.listFiles()
val vancedBaseApk = getVancedYoutubePath(absoluteVersion, "root", context) + "/base.apk"
val prepareStock = RootPatchHelper.prepareStock(
stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE,
stockVersion = absoluteVersion,
) {
PMRoot.installSplitApp(stockApks!!)
rootPackageManager.installSplitApp(stockApks!!)
}
if (prepareStock.isError)
return prepareStock
@ -52,7 +52,7 @@ class VancedInstaller(
if (patchStock.isError)
return patchStock
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
companion object {

View File

@ -3,6 +3,9 @@ package com.vanced.manager.installer.util
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileOutputStream
import com.vanced.manager.repository.manager.PackageManagerResult
import com.vanced.manager.repository.manager.PackageManagerStatus
import com.vanced.manager.repository.manager.getEnumForInstallFailed
import com.vanced.manager.util.errString
import com.vanced.manager.util.outString
import java.io.File
@ -10,11 +13,11 @@ import java.io.IOException
object PMRoot {
fun installApp(apkPath: String): PMRootResult<Nothing> {
fun installApp(apkPath: String): PackageManagerResult<Nothing> {
val apk = File(apkPath)
val tmpApk = copyApkToTemp(apk).getOrElse { exception ->
return PMRootResult.Error(
PMRootStatus.SESSION_FAILED_COPY,
return PackageManagerResult.Error(
PackageManagerStatus.SESSION_FAILED_COPY,
exception.stackTraceToString()
)
}
@ -25,28 +28,28 @@ object PMRoot {
if (!install.isSuccess) {
val errString = install.errString
return PMRootResult.Error(getEnumForInstallFailed(errString), errString)
return PackageManagerResult.Error(getEnumForInstallFailed(errString), errString)
}
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun installSplitApp(apkPaths: List<String>): PMRootResult<Nothing> {
fun installSplitApp(apkPaths: List<String>): PackageManagerResult<Nothing> {
val installCreate = Shell.su("pm", "install-create", "-r").exec()
if (!installCreate.isSuccess)
return PMRootResult.Error(PMRootStatus.SESSION_FAILED_CREATE, installCreate.errString)
return PackageManagerResult.Error(PackageManagerStatus.SESSION_FAILED_CREATE, installCreate.errString)
val sessionId = installCreate.outString
if (sessionId.toIntOrNull() == null)
return PMRootResult.Error(PMRootStatus.SESSION_INVALID_ID, installCreate.errString)
return PackageManagerResult.Error(PackageManagerStatus.SESSION_INVALID_ID, installCreate.errString)
for (apkPath in apkPaths) {
val apk = File(apkPath)
val tmpApk = copyApkToTemp(apk).getOrElse { exception ->
return PMRootResult.Error(
PMRootStatus.SESSION_FAILED_COPY,
return PackageManagerResult.Error(
PackageManagerStatus.SESSION_FAILED_COPY,
exception.stackTraceToString()
)
}
@ -58,87 +61,87 @@ object PMRoot {
tmpApk.delete()
if (!installWrite.isSuccess)
return PMRootResult.Error(PMRootStatus.SESSION_FAILED_WRITE, installWrite.errString)
return PackageManagerResult.Error(PackageManagerStatus.SESSION_FAILED_WRITE, installWrite.errString)
}
val installCommit = Shell.su("pm", "install-commit", sessionId).exec()
if (!installCommit.isSuccess) {
val errString = installCommit.errString
return PMRootResult.Error(getEnumForInstallFailed(errString), errString)
return PackageManagerResult.Error(getEnumForInstallFailed(errString), errString)
}
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun uninstallApp(pkg: String): PMRootResult<Nothing> {
fun uninstallApp(pkg: String): PackageManagerResult<Nothing> {
val uninstall = Shell.su("pm", "uninstall", pkg).exec()
if (!uninstall.isSuccess)
return PMRootResult.Error(PMRootStatus.UNINSTALL_FAILED, uninstall.errString)
return PackageManagerResult.Error(PackageManagerStatus.UNINSTALL_FAILED, uninstall.errString)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun setInstallerPackage(targetPkg: String, installerPkg: String): PMRootResult<Nothing> {
fun setInstallerPackage(targetPkg: String, installerPkg: String): PackageManagerResult<Nothing> {
val setInstaller = Shell.su("pm", "set-installer", targetPkg, installerPkg)
.exec()
if (!setInstaller.isSuccess)
return PMRootResult.Error(
PMRootStatus.ACTION_FAILED_SET_INSTALLER,
return PackageManagerResult.Error(
PackageManagerStatus.SET_FAILED_INSTALLER,
setInstaller.errString
)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun forceStopApp(pkg: String): PMRootResult<Nothing> {
fun forceStopApp(pkg: String): PackageManagerResult<Nothing> {
val stopApp = Shell.su("am", "force-stop", pkg).exec()
if (!stopApp.isSuccess)
return PMRootResult.Error(PMRootStatus.ACTION_FAILED_FORCE_STOP_APP, stopApp.errString)
return PackageManagerResult.Error(PackageManagerStatus.APP_FAILED_FORCE_STOP, stopApp.errString)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun getPackageVersionName(pkg: String): PMRootResult<String> {
fun getPackageVersionName(pkg: String): PackageManagerResult<String> {
val keyword = "versionName="
val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec()
if (!dumpsys.isSuccess)
return PMRootResult.Error(
PMRootStatus.ACTION_FAILED_GET_PACKAGE_VERSION_NAME,
return PackageManagerResult.Error(
PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_NAME,
dumpsys.errString
)
return PMRootResult.Success(dumpsys.outString.removePrefix(keyword))
return PackageManagerResult.Success(dumpsys.outString.removePrefix(keyword))
}
fun getPackageVersionCode(pkg: String): PMRootResult<Long> {
fun getPackageVersionCode(pkg: String): PackageManagerResult<Long> {
val keyword = "versionCode="
val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec()
if (!dumpsys.isSuccess)
return PMRootResult.Error(
PMRootStatus.ACTION_FAILED_GET_PACKAGE_VERSION_CODE,
return PackageManagerResult.Error(
PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_CODE,
dumpsys.errString
)
return PMRootResult.Success(
return PackageManagerResult.Success(
dumpsys.outString.removePrefix(keyword).substringAfter("minSdk")
.toLong()
)
}
fun getPackageDir(pkg: String): PMRootResult<String> {
fun getPackageDir(pkg: String): PackageManagerResult<String> {
val keyword = "path: "
val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec()
if (!dumpsys.isSuccess)
return PMRootResult.Error(PMRootStatus.ACTION_FAILED_GET_PACKAGE_DIR, dumpsys.errString)
return PackageManagerResult.Error(PackageManagerStatus.GET_FAILED_PACKAGE_DIR, dumpsys.errString)
return PMRootResult.Success(dumpsys.outString.removePrefix(keyword))
return PackageManagerResult.Success(dumpsys.outString.removePrefix(keyword))
}
}
@ -159,16 +162,4 @@ private fun copyApkToTemp(apk: File): Result<SuFile> {
}
return Result.success(tmpApk)
}
private fun getEnumForInstallFailed(outString: String) =
when {
outString.contains("INSTALL_FAILED_ABORTED") -> PMRootStatus.INSTALL_FAILED_ABORTED
outString.contains("INSTALL_FAILED_ALREADY_EXISTS") -> PMRootStatus.INSTALL_FAILED_ALREADY_EXISTS
outString.contains("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE") -> PMRootStatus.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE
outString.contains("INSTALL_FAILED_INSUFFICIENT_STORAGE") -> PMRootStatus.INSTALL_FAILED_INSUFFICIENT_STORAGE
outString.contains("INSTALL_FAILED_INVALID_APK") -> PMRootStatus.INSTALL_FAILED_INVALID_APK
outString.contains("INSTALL_FAILED_VERSION_DOWNGRADE") -> PMRootStatus.INSTALL_FAILED_VERSION_DOWNGRADE
outString.contains("INSTALL_PARSE_FAILED_NO_CERTIFICATES") -> PMRootStatus.INSTALL_FAILED_PARSE_NO_CERTIFICATES
else -> PMRootStatus.INSTALL_FAILED_UNKNOWN
}
}

View File

@ -5,6 +5,8 @@ import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileOutputStream
import com.vanced.manager.io.ManagerSuFile
import com.vanced.manager.io.SUIOException
import com.vanced.manager.repository.manager.PackageManagerResult
import com.vanced.manager.repository.manager.PackageManagerStatus
import com.vanced.manager.util.errString
import java.io.File
import java.io.IOException
@ -15,7 +17,7 @@ object Patcher {
app: String,
stockPackage: String,
stockPath: String,
): PMRootResult<Nothing> {
): PackageManagerResult<Nothing> {
val postFsDataScriptPath = getAppPostFsScriptPath(app)
val serviceDScriptPath = getAppServiceDScriptPath(app)
@ -24,22 +26,22 @@ object Patcher {
val copyServiceDScript = copyScriptToDestination(postFsDataScript, postFsDataScriptPath)
if (copyServiceDScript.isFailure)
return PMRootResult.Error(
PMRootStatus.SCRIPT_FAILED_SETUP_POST_FS,
return PackageManagerResult.Error(
PackageManagerStatus.SCRIPT_FAILED_SETUP_POST_FS,
copyServiceDScript.exceptionOrNull()!!.stackTraceToString()
)
val copyPostFsDataScript = copyScriptToDestination(serviceDScript, serviceDScriptPath)
if (copyPostFsDataScript.isFailure)
return PMRootResult.Error(
PMRootStatus.SCRIPT_FAILED_SETUP_SERVICE_D,
return PackageManagerResult.Error(
PackageManagerStatus.SCRIPT_FAILED_SETUP_SERVICE_D,
copyPostFsDataScript.exceptionOrNull()!!.stackTraceToString()
)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun movePatchToDataAdb(patchPath: String, app: String): PMRootResult<Nothing> {
fun movePatchToDataAdb(patchPath: String, app: String): PackageManagerResult<Nothing> {
val newPatchPath = getAppPatchPath(app)
val patchApk = File(patchPath)
@ -53,42 +55,42 @@ object Patcher {
try {
patchApk.copyTo(newPatchApk)
} catch (e: IOException) {
return PMRootResult.Error(PMRootStatus.PATCH_FAILED_COPY, e.stackTraceToString())
return PackageManagerResult.Error(PackageManagerStatus.PATCH_FAILED_COPY, e.stackTraceToString())
}
val chmod = Shell.su("chmod", "644", newPatchPath).exec()
if (!chmod.isSuccess)
return PMRootResult.Error(PMRootStatus.PATCH_FAILED_CHMOD, chmod.errString)
return PackageManagerResult.Error(PackageManagerStatus.PATCH_FAILED_CHMOD, chmod.errString)
val chown = Shell.su("chown", "system:system", newPatchPath).exec()
if (!chmod.isSuccess)
return PMRootResult.Error(PMRootStatus.PATCH_FAILED_CHOWN, chown.errString)
return PackageManagerResult.Error(PackageManagerStatus.PATCH_FAILED_CHOWN, chown.errString)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun chconPatch(app: String): PMRootResult<Nothing> {
fun chconPatch(app: String): PackageManagerResult<Nothing> {
val chcon = Shell.su("chcon u:object_r:apk_data_file:s0 ${getAppPatchPath(app)}").exec()
if (!chcon.isSuccess)
return PMRootResult.Error(PMRootStatus.PATCH_FAILED_CHCON, chcon.errString)
return PackageManagerResult.Error(PackageManagerStatus.PATCH_FAILED_CHCON, chcon.errString)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun linkPatch(app: String, stockPackage: String, stockPath: String): PMRootResult<Nothing> {
fun linkPatch(app: String, stockPackage: String, stockPath: String): PackageManagerResult<Nothing> {
val umount =
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $stockPackage | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """)
.exec()
if (!umount.isSuccess)
return PMRootResult.Error(PMRootStatus.LINK_FAILED_UNMOUNT, umount.errString)
return PackageManagerResult.Error(PackageManagerStatus.LINK_FAILED_UNMOUNT, umount.errString)
val mount =
Shell.su("su", "-mm", "-c", """"mount -o bind ${getAppPatchPath(app)} $stockPath"""")
.exec()
if (!mount.isSuccess)
return PMRootResult.Error(PMRootStatus.LINK_FAILED_MOUNT, mount.errString)
return PackageManagerResult.Error(PackageManagerStatus.LINK_FAILED_MOUNT, mount.errString)
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun destroyPatch(app: String) =
@ -132,11 +134,11 @@ private fun cleanPatchFiles(
postFsPath: String,
serviceDPath: String,
patchPath: String,
): PMRootResult<Nothing> {
): PackageManagerResult<Nothing> {
val files = mapOf(
postFsPath to PMRootStatus.SCRIPT_FAILED_DESTROY_POST_FS,
serviceDPath to PMRootStatus.SCRIPT_FAILED_DESTROY_SERVICE_D,
patchPath to PMRootStatus.PATCH_FAILED_DESTROY,
postFsPath to PackageManagerStatus.SCRIPT_FAILED_DESTROY_POST_FS,
serviceDPath to PackageManagerStatus.SCRIPT_FAILED_DESTROY_SERVICE_D,
patchPath to PackageManagerStatus.PATCH_FAILED_DESTROY,
)
for ((filePath, errorStatusType) in files) {
@ -145,11 +147,11 @@ private fun cleanPatchFiles(
if (exists()) delete()
}
} catch (e: SUIOException) {
return PMRootResult.Error(errorStatusType, e.stackTraceToString())
return PackageManagerResult.Error(errorStatusType, e.stackTraceToString())
}
}
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
private fun copyScriptToDestination(

View File

@ -1,8 +1,11 @@
package com.vanced.manager.installer.util
import com.vanced.manager.repository.manager.PackageManagerResult
import com.vanced.manager.repository.manager.getOrElse
object RootPatchHelper {
fun cleanPatches(app: String): PMRootResult<Nothing> {
fun cleanPatches(app: String): PackageManagerResult<Nothing> {
val cleanOldPatches = Patcher.destroyOldPatch(app)
if (cleanOldPatches.isError)
return cleanOldPatches
@ -11,14 +14,14 @@ object RootPatchHelper {
if (cleanOldPatches.isError)
return cleanPatches
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
inline fun prepareStock(
stockPackage: String,
stockVersion: String,
install: () -> PMRootResult<Nothing>
): PMRootResult<Nothing> {
install: () -> PackageManagerResult<Nothing>
): PackageManagerResult<Nothing> {
val stockYoutubeVersion = PMRoot.getPackageVersionName(stockPackage)
.getOrElse { null }
if (stockYoutubeVersion != stockVersion) {
@ -31,14 +34,14 @@ object RootPatchHelper {
return installStock
}
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
fun patchStock(
patchPath: String,
stockPackage: String,
app: String
): PMRootResult<Nothing> {
): PackageManagerResult<Nothing> {
val movePatch = Patcher.movePatchToDataAdb(patchPath, app)
if (movePatch.isError)
return movePatch
@ -51,14 +54,14 @@ object RootPatchHelper {
.getOrElse { error -> return error }!!
val setupScript = Patcher.setupScript(app, stockPackage, stockPackageDir)
if (setupScript is PMRootResult.Error)
if (setupScript is PackageManagerResult.Error)
return setupScript
val linkPatch = Patcher.linkPatch(app, stockPackage, stockPackageDir)
if (linkPatch is PMRootResult.Error)
if (linkPatch is PackageManagerResult.Error)
return linkPatch
return PMRootResult.Success()
return PackageManagerResult.Success(null)
}
}

View File

@ -6,7 +6,9 @@ import com.vanced.manager.domain.model.AppState
import com.vanced.manager.domain.model.AppType
import com.vanced.manager.network.GithubService
import com.vanced.manager.network.dto.GithubReleaseDto
import com.vanced.manager.repository.source.PkgInfoDatasource
import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.repository.manager.PackageManager
import com.vanced.manager.repository.manager.RootPackageManager
interface AppRepository {
@ -26,15 +28,18 @@ interface AppRepository {
class AppRepositoryImpl(
private val githubService: GithubService,
private val pkgInfoDatasource: PkgInfoDatasource,
private val nonrootPackageManager: NonrootPackageManager,
private val rootPackageManager: RootPackageManager,
) : AppRepository {
override suspend fun getVancedYoutubeNonroot(): App {
val githubRelease = githubService.getVancedYoutubeRelease()
val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE)
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE)
val installedVersionCode =
nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE).getValueOrNull()
val installedVersionName =
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE).getValueOrNull()
return App(
name = AppData.NAME_VANCED_YOUTUBE,
iconResId = AppData.ICON_VANCED_YOUTUBE,
@ -55,9 +60,9 @@ class AppRepositoryImpl(
val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode =
pkgInfoDatasource.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE)
rootPackageManager.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE).getValueOrNull()
val installedVersionName =
pkgInfoDatasource.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE)
rootPackageManager.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE).getValueOrNull()
return App(
name = AppData.NAME_VANCED_YOUTUBE,
iconResId = AppData.ICON_VANCED_YOUTUBE,
@ -78,9 +83,9 @@ class AppRepositoryImpl(
val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode =
pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC)
nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC).getValueOrNull()
val installedVersionName =
pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC)
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC).getValueOrNull()
return App(
name = AppData.NAME_VANCED_YOUTUBE_MUSIC,
iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC,
@ -101,9 +106,9 @@ class AppRepositoryImpl(
val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode =
pkgInfoDatasource.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC)
rootPackageManager.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC).getValueOrNull()
val installedVersionName =
pkgInfoDatasource.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC)
rootPackageManager.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC).getValueOrNull()
return App(
name = AppData.NAME_VANCED_YOUTUBE_MUSIC,
iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC,
@ -123,8 +128,10 @@ class AppRepositoryImpl(
val githubRelease = githubService.getVancedMicrogRelease()
val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_MICROG)
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_MICROG)
val installedVersionCode =
nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_MICROG).getValueOrNull()
val installedVersionName =
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_MICROG).getValueOrNull()
return App(
name = AppData.NAME_VANCED_MICROG,
iconResId = AppData.ICON_VANCED_MICROG,
@ -144,8 +151,10 @@ class AppRepositoryImpl(
val githubRelease = githubService.getVancedManagerRelease()
val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_MANAGER)
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_MANAGER)
val installedVersionCode =
nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_MANAGER).getValueOrNull()
val installedVersionName =
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_MANAGER).getValueOrNull()
return App(
name = AppData.NAME_VANCED_MANAGER,
iconResId = AppData.ICON_VANCED_MANAGER,

View File

@ -0,0 +1,486 @@
package com.vanced.manager.repository.manager
import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.Build
import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileInputStream
import com.topjohnwu.superuser.io.SuFileOutputStream
import com.vanced.manager.installer.service.AppInstallService
import com.vanced.manager.installer.service.AppUninstallService
import com.vanced.manager.util.SuException
import com.vanced.manager.util.awaitOutputOrThrow
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import kotlin.jvm.Throws
interface PackageManager {
suspend fun getVersionCode(packageName: String): PackageManagerResult<Int>
suspend fun getVersionName(packageName: String): PackageManagerResult<String>
suspend fun getInstallationDir(packageName: String): PackageManagerResult<String>
suspend fun setInstaller(targetPackage: String, installerPackage: String): PackageManagerResult<Nothing>
suspend fun forceStop(packageName: String): PackageManagerResult<Nothing>
suspend fun installApp(apk: File): PackageManagerResult<Nothing>
suspend fun installSplitApp(apks: Array<File>): PackageManagerResult<Nothing>
suspend fun uninstallApp(packageName: String): PackageManagerResult<Nothing>
}
class NonrootPackageManager(
private val context: Context
) : PackageManager {
@SuppressLint("WrongConstant")
@Suppress("DEPRECATION")
override suspend fun getVersionCode(packageName: String): PackageManagerResult<Int> {
return try {
val packageInfo = context.packageManager.getPackageInfo(packageName, FLAG_NOTHING)
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode.and(VERSION_IGNORE_MAJOR).toInt()
} else {
packageInfo.versionCode
}
PackageManagerResult.Success(versionCode)
} catch (e: android.content.pm.PackageManager.NameNotFoundException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_CODE,
message = e.stackTraceToString()
)
}
}
@SuppressLint("WrongConstant")
override suspend fun getVersionName(packageName: String): PackageManagerResult<String> {
return try {
val versionName = context.packageManager
.getPackageInfo(packageName, FLAG_NOTHING)
.versionName
PackageManagerResult.Success(versionName)
} catch (e: android.content.pm.PackageManager.NameNotFoundException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_NAME,
message = e.stackTraceToString()
)
}
}
@SuppressLint("WrongConstant")
override suspend fun getInstallationDir(packageName: String): PackageManagerResult<String> {
return try {
val installationDir = context.packageManager
.getPackageInfo(packageName, FLAG_NOTHING)
.applicationInfo
.sourceDir
PackageManagerResult.Success(installationDir)
} catch (e: android.content.pm.PackageManager.NameNotFoundException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_DIR,
message = e.stackTraceToString()
)
}
}
override suspend fun setInstaller(
targetPackage: String,
installerPackage: String
): PackageManagerResult<Nothing> {
return PackageManagerResult.Error(
status = PackageManagerStatus.SET_FAILED_INSTALLER,
message = "Unsupported"
)
}
override suspend fun forceStop(packageName: String): PackageManagerResult<Nothing> {
return PackageManagerResult.Error(
status = PackageManagerStatus.APP_FAILED_FORCE_STOP,
message = "Unsupported"
)
}
override suspend fun installApp(apk: File): PackageManagerResult<Nothing> {
return createInstallationSession {
writeApkToSession(apk)
}
}
override suspend fun installSplitApp(apks: Array<File>): PackageManagerResult<Nothing> {
return createInstallationSession {
for (apk in apks) {
writeApkToSession(apk)
}
}
}
override suspend fun uninstallApp(packageName: String): PackageManagerResult<Nothing> {
val packageInstaller = context.packageManager.packageInstaller
val pendingIntent = PendingIntent.getService(
context,
0,
Intent(context, AppUninstallService::class.java),
intentFlags
).intentSender
packageInstaller.uninstall(packageName, pendingIntent)
return PackageManagerResult.Success(null)
}
private inline fun createInstallationSession(
block: PackageInstaller.Session.() -> Unit
): PackageManagerResult<Nothing> {
val packageInstaller = context.packageManager.packageInstaller
val sessionParams = PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setInstallReason(android.content.pm.PackageManager.INSTALL_REASON_USER)
}
}
val pendingIntent = PendingIntent.getService(
context,
0,
Intent(context, AppInstallService::class.java),
intentFlags
).intentSender
val sessionId: Int
try {
sessionId = packageInstaller.createSession(sessionParams)
} catch (e: IOException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_CREATE,
message = e.stackTraceToString()
)
}
val session: PackageInstaller.Session
try {
session = packageInstaller.openSession(sessionId)
} catch (e: IOException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_OPEN,
message = e.stackTraceToString()
)
} catch (e: SecurityException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_OPEN,
message = e.stackTraceToString()
)
}
try {
session.use {
it.block()
it.commit(pendingIntent)
}
} catch (e: IOException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_WRITE,
message = e.stackTraceToString()
)
} catch (e: SecurityException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_COMMIT,
message = e.stackTraceToString()
)
}
return PackageManagerResult.Success(null)
}
private fun PackageInstaller.Session.writeApkToSession(apk: File) {
apk.inputStream().use { inputStream ->
openWrite(apk.name, 0, apk.length()).use { outputStream ->
inputStream.copyTo(outputStream, byteArraySize)
fsync(outputStream)
}
}
}
private val intentFlags: Int
get() {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
PendingIntent.FLAG_MUTABLE
else
0
}
private companion object {
const val byteArraySize = 1024 * 1024
const val FLAG_NOTHING = 0
const val VERSION_IGNORE_MAJOR = 0xFFFFFFFF
}
}
class RootPackageManager : PackageManager {
override suspend fun getVersionCode(packageName: String): PackageManagerResult<Int> {
return try {
val keyword = "versionCode="
val dumpsys = Shell.su("dumpsys", "package", packageName, "|", "grep", keyword).awaitOutputOrThrow()
val versionCode = dumpsys.removePrefix(keyword).substringAfter("minSdk").toInt()
PackageManagerResult.Success(versionCode)
} catch (e: SuException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_CODE,
message = e.stderrOut
)
} catch (e: java.lang.NumberFormatException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_CODE,
message = e.stackTraceToString()
)
}
}
override suspend fun getVersionName(packageName: String): PackageManagerResult<String> {
return try {
val keyword = "versionName="
val dumpsys = Shell.su("dumpsys", "package", packageName, "|", "grep", keyword).awaitOutputOrThrow()
val versionName = dumpsys.removePrefix(keyword)
PackageManagerResult.Success(versionName)
} catch (e: SuException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_NAME,
message = e.stderrOut
)
}
}
override suspend fun getInstallationDir(packageName: String): PackageManagerResult<String> {
return try {
val keyword = "path: "
val dumpsys = Shell.su("dumpsys", "package", packageName, "|", "grep", keyword).awaitOutputOrThrow()
val installationDir = dumpsys.removePrefix(keyword)
PackageManagerResult.Success(installationDir)
} catch (e: SuException) {
PackageManagerResult.Error(
status = PackageManagerStatus.GET_FAILED_PACKAGE_DIR,
message = e.stderrOut
)
}
}
override suspend fun setInstaller(
targetPackage: String,
installerPackage: String
): PackageManagerResult<Nothing> {
try {
Shell.su("pm", "set-installer", targetPackage, installerPackage).awaitOutputOrThrow()
} catch (e: SuException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SET_FAILED_INSTALLER,
message = e.stderrOut
)
}
return PackageManagerResult.Success(null)
}
override suspend fun forceStop(packageName: String): PackageManagerResult<Nothing> {
return try {
Shell.su("am", "force-stop", packageName).awaitOutputOrThrow()
PackageManagerResult.Success(null)
} catch (e: SuException) {
PackageManagerResult.Error(
status = PackageManagerStatus.APP_FAILED_FORCE_STOP,
message = e.stderrOut
)
}
}
override suspend fun installApp(apk: File): PackageManagerResult<Nothing> {
var tempApk: File? = null
try {
tempApk = copyApkToTemp(apk)
Shell.su("pm", "install", "-r", tempApk.absolutePath).awaitOutputOrThrow()
} catch (e: IOException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_COPY,
message = e.stackTraceToString()
)
} catch (e: SuException) {
return PackageManagerResult.Error(
status = getEnumForInstallFailed(e.stderrOut),
message = e.stderrOut
)
} finally {
tempApk?.delete()
}
return PackageManagerResult.Success(null)
}
override suspend fun installSplitApp(apks: Array<File>): PackageManagerResult<Nothing> {
val sessionId: Int
try {
val installCreate = Shell.su("pm", "install-create", "-r").awaitOutputOrThrow()
sessionId = installCreate.toInt()
} catch (e: SuException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_CREATE,
message = e.stderrOut
)
} catch (e: NumberFormatException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_INVALID_ID,
message = e.stackTraceToString()
)
}
for (apk in apks) {
var tempApk: File? = null
try {
tempApk = copyApkToTemp(apk)
Shell.su("pm", "install-write", sessionId.toString(), tempApk.name, tempApk.absolutePath).awaitOutputOrThrow()
} catch (e: SuException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_WRITE,
message = e.stderrOut
)
} catch (e: IOException) {
return PackageManagerResult.Error(
status = PackageManagerStatus.SESSION_FAILED_COPY,
message = e.stackTraceToString()
)
} finally {
tempApk?.delete()
}
}
try {
Shell.su("pm", "install-commit", sessionId.toString()).awaitOutputOrThrow()
} catch (e: SuException) {
return PackageManagerResult.Error(
status = getEnumForInstallFailed(e.stderrOut),
message = e.stderrOut
)
}
return PackageManagerResult.Success(null)
}
override suspend fun uninstallApp(packageName: String): PackageManagerResult<Nothing> {
return try {
Shell.su("pm", "uninstall", packageName).awaitOutputOrThrow()
PackageManagerResult.Success(null)
} catch (e: SuException) {
PackageManagerResult.Error(
status = PackageManagerStatus.UNINSTALL_FAILED,
message = e.stderrOut
)
}
}
@Throws(
IOException::class,
FileNotFoundException::class
)
private fun copyApkToTemp(apk: File): SuFile {
val tmpPath = "/data/local/tmp/${apk.name}"
val tmpApk = SuFile(tmpPath).apply {
createNewFile()
}
SuFileInputStream.open(tmpApk).use { inputStream ->
SuFileOutputStream.open(tmpApk).use { outputStream ->
inputStream.copyTo(outputStream)
outputStream.flush()
}
}
return tmpApk
}
}
enum class PackageManagerStatus {
SET_FAILED_INSTALLER,
GET_FAILED_PACKAGE_DIR,
GET_FAILED_PACKAGE_VERSION_NAME,
GET_FAILED_PACKAGE_VERSION_CODE,
APP_FAILED_FORCE_STOP,
SESSION_FAILED_CREATE,
SESSION_FAILED_COMMIT,
SESSION_FAILED_WRITE,
SESSION_FAILED_COPY,
SESSION_FAILED_OPEN,
SESSION_INVALID_ID,
INSTALL_FAILED_ABORTED,
INSTALL_FAILED_ALREADY_EXISTS,
INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
INSTALL_FAILED_INSUFFICIENT_STORAGE,
INSTALL_FAILED_INVALID_APK,
INSTALL_FAILED_VERSION_DOWNGRADE,
INSTALL_FAILED_PARSE_NO_CERTIFICATES,
INSTALL_FAILED_UNKNOWN,
UNINSTALL_FAILED,
LINK_FAILED_UNMOUNT,
LINK_FAILED_MOUNT,
PATCH_FAILED_COPY,
PATCH_FAILED_CHMOD,
PATCH_FAILED_CHOWN,
PATCH_FAILED_CHCON,
PATCH_FAILED_DESTROY,
SCRIPT_FAILED_SETUP_POST_FS,
SCRIPT_FAILED_SETUP_SERVICE_D,
SCRIPT_FAILED_DESTROY_POST_FS,
SCRIPT_FAILED_DESTROY_SERVICE_D,
}
fun getEnumForInstallFailed(outString: String): PackageManagerStatus {
return when {
outString.contains("INSTALL_FAILED_ABORTED") -> PackageManagerStatus.INSTALL_FAILED_ABORTED
outString.contains("INSTALL_FAILED_ALREADY_EXISTS") -> PackageManagerStatus.INSTALL_FAILED_ALREADY_EXISTS
outString.contains("INSTALL_FAILED_CPU_ABI_INCOMPATIBLE") -> PackageManagerStatus.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE
outString.contains("INSTALL_FAILED_INSUFFICIENT_STORAGE") -> PackageManagerStatus.INSTALL_FAILED_INSUFFICIENT_STORAGE
outString.contains("INSTALL_FAILED_INVALID_APK") -> PackageManagerStatus.INSTALL_FAILED_INVALID_APK
outString.contains("INSTALL_FAILED_VERSION_DOWNGRADE") -> PackageManagerStatus.INSTALL_FAILED_VERSION_DOWNGRADE
outString.contains("INSTALL_PARSE_FAILED_NO_CERTIFICATES") -> PackageManagerStatus.INSTALL_FAILED_PARSE_NO_CERTIFICATES
else -> PackageManagerStatus.INSTALL_FAILED_UNKNOWN
}
}
sealed class PackageManagerResult<out V> {
data class Success<out V>(val value: V?) : PackageManagerResult<V>()
data class Error(val status: PackageManagerStatus, val message: String) : PackageManagerResult<Nothing>()
fun getValueOrNull(): V? = getOrElse { null }
val isError
get() = this is Error
val isSuccess
get() = this is Success
}
inline fun <R, T : R> PackageManagerResult<T>.getOrElse(
onError: (PackageManagerResult.Error) -> R?
): R? {
return when (this) {
is PackageManagerResult.Success -> this.value
is PackageManagerResult.Error -> onError(this)
}
}

View File

@ -1,50 +0,0 @@
package com.vanced.manager.repository.source
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Build
interface PkgInfoDatasource {
fun getVersionCode(packageName: String): Int?
fun getVersionName(packageName: String): String?
}
class PkgInfoDatasourceImpl(
private val packageManager: PackageManager
) : PkgInfoDatasource {
private companion object {
const val FLAG_NOTHING = 0
const val VERSION_IGNORE_MAJOR = 0xFFFFFFFF
}
@SuppressLint("WrongConstant")
override fun getVersionCode(packageName: String): Int? {
return try {
val packageInfo = packageManager.getPackageInfo(packageName, FLAG_NOTHING)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode.and(VERSION_IGNORE_MAJOR).toInt()
} else {
@Suppress("DEPRECATION")
packageInfo.versionCode
}
} catch (e: PackageManager.NameNotFoundException) {
null
}
}
@Suppress("DEPRECATION")
@SuppressLint("WrongConstant")
override fun getVersionName(packageName: String): String? {
return try {
packageManager
.getPackageInfo(packageName, FLAG_NOTHING)
.versionName
} catch (e: PackageManager.NameNotFoundException) {
null
}
}
}

View File

@ -14,11 +14,11 @@ import com.vanced.manager.downloader.impl.VancedDownloader
import com.vanced.manager.installer.impl.MicrogInstaller
import com.vanced.manager.installer.impl.MusicInstaller
import com.vanced.manager.installer.impl.VancedInstaller
import com.vanced.manager.installer.util.PMRootResult
import com.vanced.manager.network.util.MICROG_NAME
import com.vanced.manager.network.util.MUSIC_NAME
import com.vanced.manager.network.util.VANCED_NAME
import com.vanced.manager.preferences.holder.managerVariantPref
import com.vanced.manager.repository.manager.PackageManagerResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -117,7 +117,7 @@ class InstallViewModel(
}
}
private fun installApp(
private suspend fun installApp(
appName: String,
appVersions: List<String>?,
) {
@ -127,11 +127,11 @@ class InstallViewModel(
if (isRoot) {
when (val installStatus = installer.installRoot(appVersions)) {
is PMRootResult.Success -> {
is PackageManagerResult.Success -> {
status = Status.Installed
log(Log.Success("Successfully installed"))
}
is PMRootResult.Error -> {
is PackageManagerResult.Error -> {
status = Status.Failure
log(Log.Error("Failed to install app", installStatus.message))
}

View File

@ -2,6 +2,7 @@ package com.vanced.manager.util
import com.topjohnwu.superuser.Shell
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine
val Shell.Result.outString
@ -13,8 +14,26 @@ val Shell.Result.errString
val isMagiskInstalled
get() = Shell.rootAccess() && Shell.su("magisk", "-c").exec().isSuccess
suspend fun Shell.Job.await() =
suspendCoroutine<Shell.Result> { continuation ->
submit(/*continuation.context.asExecutor(),*/ continuation::resume)
suspend fun Shell.Job.await(): Shell.Result {
return suspendCoroutine { continuation ->
submit {
continuation.resume(it)
}
}
}
class SuException(val stderrOut: String) : Exception(stderrOut)
@Throws(SuException::class)
suspend fun Shell.Job.awaitOutputOrThrow(): String {
return suspendCoroutine { continuation ->
submit {
if (it.isSuccess) {
continuation.resume(it.outString)
} else {
continuation.resumeWithException(SuException(it.errString))
}
}
}
}