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, datasourceModule,
downloaderModule, downloaderModule,
installerModule, installerModule,
managerModule,
networkModule, networkModule,
repositoryModule, repositoryModule,
serviceModule, serviceModule,

View File

@ -1,8 +1,6 @@
package com.vanced.manager.di package com.vanced.manager.di
import android.content.Context 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.PreferenceDatasource
import com.vanced.manager.repository.source.PreferenceDatasourceImpl import com.vanced.manager.repository.source.PreferenceDatasourceImpl
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
@ -10,14 +8,6 @@ import org.koin.dsl.module
val datasourceModule = module { val datasourceModule = module {
fun providePkgInfoDatasource(
context: Context
): PkgInfoDatasource {
return PkgInfoDatasourceImpl(
packageManager = context.packageManager
)
}
fun providePreferenceDatasource( fun providePreferenceDatasource(
context: Context context: Context
): PreferenceDatasource { ): PreferenceDatasource {
@ -29,6 +19,5 @@ val datasourceModule = module {
) )
} }
single { providePkgInfoDatasource(androidContext()) }
single { providePreferenceDatasource(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.MicrogInstaller
import com.vanced.manager.installer.impl.MusicInstaller import com.vanced.manager.installer.impl.MusicInstaller
import com.vanced.manager.installer.impl.VancedInstaller 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.android.ext.koin.androidContext
import org.koin.dsl.module import org.koin.dsl.module
val installerModule = module { val installerModule = module {
fun provideVancedInstaller( fun provideVancedInstaller(
context: Context context: Context,
nonrootPackageManager: NonrootPackageManager,
rootPackageManager: RootPackageManager
): VancedInstaller { ): VancedInstaller {
return VancedInstaller( return VancedInstaller(
context = context context = context,
nonrootPackageManager = nonrootPackageManager,
rootPackageManager = rootPackageManager
) )
} }
fun provideMusicInstaller( fun provideMusicInstaller(
context: Context context: Context,
nonrootPackageManager: NonrootPackageManager,
rootPackageManager: RootPackageManager
): MusicInstaller { ): MusicInstaller {
return MusicInstaller( return MusicInstaller(
context = context context = context,
nonrootPackageManager = nonrootPackageManager,
rootPackageManager = rootPackageManager
) )
} }
fun provideMicrogInstaller( fun provideMicrogInstaller(
context: Context context: Context,
nonrootPackageManager: NonrootPackageManager,
): MicrogInstaller { ): MicrogInstaller {
return MicrogInstaller( return MicrogInstaller(
context = context context = context,
nonrootPackageManager = nonrootPackageManager
) )
} }
single { provideVancedInstaller(androidContext()) } single { provideVancedInstaller(androidContext(), get(), get()) }
single { provideMusicInstaller(androidContext()) } single { provideMusicInstaller(androidContext(), get(), get()) }
single { provideMicrogInstaller(androidContext()) } 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.AppRepositoryImpl
import com.vanced.manager.repository.PreferenceRepository import com.vanced.manager.repository.PreferenceRepository
import com.vanced.manager.repository.PreferenceRepositoryImpl 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 com.vanced.manager.repository.source.PreferenceDatasource
import org.koin.dsl.module import org.koin.dsl.module
@ -13,11 +14,13 @@ val repositoryModule = module {
fun provideGithubRepository( fun provideGithubRepository(
githubService: GithubService, githubService: GithubService,
pkgInfoDatasource: PkgInfoDatasource nonrootPackageManager: NonrootPackageManager,
rootPackageManager: RootPackageManager,
): AppRepository { ): AppRepository {
return AppRepositoryImpl( return AppRepositoryImpl(
githubService = githubService, 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()) } single { providePreferenceRepository(get()) }
} }

View File

@ -1,11 +1,11 @@
package com.vanced.manager.installer.base package com.vanced.manager.installer.base
import com.vanced.manager.installer.util.PMRootResult import com.vanced.manager.repository.manager.PackageManagerResult
abstract class AppInstaller { 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 android.content.Context
import com.vanced.manager.downloader.util.getMicrogPath import com.vanced.manager.downloader.util.getMicrogPath
import com.vanced.manager.installer.base.AppInstaller import com.vanced.manager.installer.base.AppInstaller
import com.vanced.manager.installer.util.PM import com.vanced.manager.repository.manager.NonrootPackageManager
import com.vanced.manager.installer.util.PMRootResult import com.vanced.manager.repository.manager.PackageManagerResult
import java.io.File import java.io.File
class MicrogInstaller( class MicrogInstaller(
private val context: Context private val context: Context,
private val nonrootPackageManager: NonrootPackageManager,
) : AppInstaller() { ) : AppInstaller() {
override fun install( override suspend fun install(appVersions: List<String>?) {
appVersions: List<String>? val musicApk = File(getMicrogPath(context), "microg.apk")
) {
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") 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.getStockYoutubeMusicPath
import com.vanced.manager.downloader.util.getVancedYoutubeMusicPath import com.vanced.manager.downloader.util.getVancedYoutubeMusicPath
import com.vanced.manager.installer.base.AppInstaller 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.installer.util.RootPatchHelper
import com.vanced.manager.preferences.holder.managerVariantPref import com.vanced.manager.preferences.holder.managerVariantPref
import com.vanced.manager.preferences.holder.musicVersionPref import com.vanced.manager.preferences.holder.musicVersionPref
import com.vanced.manager.preferences.holder.vancedVersionPref 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 com.vanced.manager.util.getLatestOrProvidedAppVersion
import java.io.File import java.io.File
class MusicInstaller( class MusicInstaller(
private val context: Context private val context: Context,
private val rootPackageManager: RootPackageManager,
private val nonrootPackageManager: NonrootPackageManager,
) : AppInstaller() { ) : AppInstaller() {
override fun install( override suspend fun install(appVersions: List<String>?) {
appVersions: List<String>?
) {
val absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions) val absoluteVersion = getLatestOrProvidedAppVersion(musicVersionPref, appVersions)
val musicApk = File( val musicApk = File(
@ -32,33 +32,33 @@ class MusicInstaller(
) + "/music.apk" ) + "/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 absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
val stockPath = getStockYoutubeMusicPath(absoluteVersion, context) + "/base.apk" val stock = File(getStockYoutubeMusicPath(absoluteVersion, context), "base.apk")
val vancedPath = getVancedYoutubeMusicPath(absoluteVersion, "root", context) + "/base.apk" val vanced = File(getVancedYoutubeMusicPath(absoluteVersion, "root", context), "base.apk")
val prepareStock = RootPatchHelper.prepareStock( val prepareStock = RootPatchHelper.prepareStock(
stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC, stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC,
stockVersion = absoluteVersion stockVersion = absoluteVersion
) { ) {
PMRoot.installApp(stockPath) rootPackageManager.installApp(stock)
} }
if (prepareStock.isError) if (prepareStock.isError)
return prepareStock return prepareStock
val patchStock = RootPatchHelper.patchStock( val patchStock = RootPatchHelper.patchStock(
patchPath = vancedPath, patchPath = vanced.absolutePath,
stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC, stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC,
app = APP_KEY app = APP_KEY
) )
if (patchStock.isError) if (patchStock.isError)
return patchStock return patchStock
return PMRootResult.Success() return PackageManagerResult.Success(null)
} }
companion object { 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.getStockYoutubePath
import com.vanced.manager.downloader.util.getVancedYoutubePath import com.vanced.manager.downloader.util.getVancedYoutubePath
import com.vanced.manager.installer.base.AppInstaller 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.installer.util.RootPatchHelper
import com.vanced.manager.preferences.holder.vancedVersionPref 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 com.vanced.manager.util.getLatestOrProvidedAppVersion
import java.io.File import java.io.File
class VancedInstaller( class VancedInstaller(
private val context: Context private val context: Context,
private val rootPackageManager: RootPackageManager,
private val nonrootPackageManager: NonrootPackageManager,
) : AppInstaller() { ) : AppInstaller() {
override fun install( override suspend fun install(appVersions: List<String>?) {
appVersions: List<String>?
) {
val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions) val absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
val apks = File(getVancedYoutubePath(absoluteVersion, "nonroot", context)) val apks = File(getVancedYoutubePath(absoluteVersion, "nonroot", context))
.listFiles() .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 absoluteVersion = getLatestOrProvidedAppVersion(vancedVersionPref, appVersions)
val stockApks = File(getStockYoutubePath(absoluteVersion, context)) val stockApks = File(getStockYoutubePath(absoluteVersion, context))
.listFiles()?.map { it.absolutePath } .listFiles()
val vancedBaseApk = getVancedYoutubePath(absoluteVersion, "root", context) + "/base.apk" val vancedBaseApk = getVancedYoutubePath(absoluteVersion, "root", context) + "/base.apk"
val prepareStock = RootPatchHelper.prepareStock( val prepareStock = RootPatchHelper.prepareStock(
stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE, stockPackage = AppData.PACKAGE_ROOT_VANCED_YOUTUBE,
stockVersion = absoluteVersion, stockVersion = absoluteVersion,
) { ) {
PMRoot.installSplitApp(stockApks!!) rootPackageManager.installSplitApp(stockApks!!)
} }
if (prepareStock.isError) if (prepareStock.isError)
return prepareStock return prepareStock
@ -52,7 +52,7 @@ class VancedInstaller(
if (patchStock.isError) if (patchStock.isError)
return patchStock return patchStock
return PMRootResult.Success() return PackageManagerResult.Success(null)
} }
companion object { companion object {

View File

@ -3,6 +3,9 @@ package com.vanced.manager.installer.util
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.io.SuFile import com.topjohnwu.superuser.io.SuFile
import com.topjohnwu.superuser.io.SuFileOutputStream 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.errString
import com.vanced.manager.util.outString import com.vanced.manager.util.outString
import java.io.File import java.io.File
@ -10,11 +13,11 @@ import java.io.IOException
object PMRoot { object PMRoot {
fun installApp(apkPath: String): PMRootResult<Nothing> { fun installApp(apkPath: String): PackageManagerResult<Nothing> {
val apk = File(apkPath) val apk = File(apkPath)
val tmpApk = copyApkToTemp(apk).getOrElse { exception -> val tmpApk = copyApkToTemp(apk).getOrElse { exception ->
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.SESSION_FAILED_COPY, PackageManagerStatus.SESSION_FAILED_COPY,
exception.stackTraceToString() exception.stackTraceToString()
) )
} }
@ -25,28 +28,28 @@ object PMRoot {
if (!install.isSuccess) { if (!install.isSuccess) {
val errString = install.errString 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() val installCreate = Shell.su("pm", "install-create", "-r").exec()
if (!installCreate.isSuccess) 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 val sessionId = installCreate.outString
if (sessionId.toIntOrNull() == null) 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) { for (apkPath in apkPaths) {
val apk = File(apkPath) val apk = File(apkPath)
val tmpApk = copyApkToTemp(apk).getOrElse { exception -> val tmpApk = copyApkToTemp(apk).getOrElse { exception ->
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.SESSION_FAILED_COPY, PackageManagerStatus.SESSION_FAILED_COPY,
exception.stackTraceToString() exception.stackTraceToString()
) )
} }
@ -58,87 +61,87 @@ object PMRoot {
tmpApk.delete() tmpApk.delete()
if (!installWrite.isSuccess) 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() val installCommit = Shell.su("pm", "install-commit", sessionId).exec()
if (!installCommit.isSuccess) { if (!installCommit.isSuccess) {
val errString = installCommit.errString 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() val uninstall = Shell.su("pm", "uninstall", pkg).exec()
if (!uninstall.isSuccess) 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) val setInstaller = Shell.su("pm", "set-installer", targetPkg, installerPkg)
.exec() .exec()
if (!setInstaller.isSuccess) if (!setInstaller.isSuccess)
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.ACTION_FAILED_SET_INSTALLER, PackageManagerStatus.SET_FAILED_INSTALLER,
setInstaller.errString 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() val stopApp = Shell.su("am", "force-stop", pkg).exec()
if (!stopApp.isSuccess) 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 keyword = "versionName="
val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec() val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec()
if (!dumpsys.isSuccess) if (!dumpsys.isSuccess)
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.ACTION_FAILED_GET_PACKAGE_VERSION_NAME, PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_NAME,
dumpsys.errString 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 keyword = "versionCode="
val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec() val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec()
if (!dumpsys.isSuccess) if (!dumpsys.isSuccess)
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.ACTION_FAILED_GET_PACKAGE_VERSION_CODE, PackageManagerStatus.GET_FAILED_PACKAGE_VERSION_CODE,
dumpsys.errString dumpsys.errString
) )
return PMRootResult.Success( return PackageManagerResult.Success(
dumpsys.outString.removePrefix(keyword).substringAfter("minSdk") dumpsys.outString.removePrefix(keyword).substringAfter("minSdk")
.toLong() .toLong()
) )
} }
fun getPackageDir(pkg: String): PMRootResult<String> { fun getPackageDir(pkg: String): PackageManagerResult<String> {
val keyword = "path: " val keyword = "path: "
val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec() val dumpsys = Shell.su("dumpsys", "package", pkg, "|", "grep", keyword).exec()
if (!dumpsys.isSuccess) 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) 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.topjohnwu.superuser.io.SuFileOutputStream
import com.vanced.manager.io.ManagerSuFile import com.vanced.manager.io.ManagerSuFile
import com.vanced.manager.io.SUIOException 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 com.vanced.manager.util.errString
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
@ -15,7 +17,7 @@ object Patcher {
app: String, app: String,
stockPackage: String, stockPackage: String,
stockPath: String, stockPath: String,
): PMRootResult<Nothing> { ): PackageManagerResult<Nothing> {
val postFsDataScriptPath = getAppPostFsScriptPath(app) val postFsDataScriptPath = getAppPostFsScriptPath(app)
val serviceDScriptPath = getAppServiceDScriptPath(app) val serviceDScriptPath = getAppServiceDScriptPath(app)
@ -24,22 +26,22 @@ object Patcher {
val copyServiceDScript = copyScriptToDestination(postFsDataScript, postFsDataScriptPath) val copyServiceDScript = copyScriptToDestination(postFsDataScript, postFsDataScriptPath)
if (copyServiceDScript.isFailure) if (copyServiceDScript.isFailure)
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.SCRIPT_FAILED_SETUP_POST_FS, PackageManagerStatus.SCRIPT_FAILED_SETUP_POST_FS,
copyServiceDScript.exceptionOrNull()!!.stackTraceToString() copyServiceDScript.exceptionOrNull()!!.stackTraceToString()
) )
val copyPostFsDataScript = copyScriptToDestination(serviceDScript, serviceDScriptPath) val copyPostFsDataScript = copyScriptToDestination(serviceDScript, serviceDScriptPath)
if (copyPostFsDataScript.isFailure) if (copyPostFsDataScript.isFailure)
return PMRootResult.Error( return PackageManagerResult.Error(
PMRootStatus.SCRIPT_FAILED_SETUP_SERVICE_D, PackageManagerStatus.SCRIPT_FAILED_SETUP_SERVICE_D,
copyPostFsDataScript.exceptionOrNull()!!.stackTraceToString() 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 newPatchPath = getAppPatchPath(app)
val patchApk = File(patchPath) val patchApk = File(patchPath)
@ -53,42 +55,42 @@ object Patcher {
try { try {
patchApk.copyTo(newPatchApk) patchApk.copyTo(newPatchApk)
} catch (e: IOException) { } 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() val chmod = Shell.su("chmod", "644", newPatchPath).exec()
if (!chmod.isSuccess) 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() val chown = Shell.su("chown", "system:system", newPatchPath).exec()
if (!chmod.isSuccess) 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() val chcon = Shell.su("chcon u:object_r:apk_data_file:s0 ${getAppPatchPath(app)}").exec()
if (!chcon.isSuccess) 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 = val umount =
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $stockPackage | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """) Shell.su("""for i in ${'$'}(ls /data/app/ | grep $stockPackage | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """)
.exec() .exec()
if (!umount.isSuccess) if (!umount.isSuccess)
return PMRootResult.Error(PMRootStatus.LINK_FAILED_UNMOUNT, umount.errString) return PackageManagerResult.Error(PackageManagerStatus.LINK_FAILED_UNMOUNT, umount.errString)
val mount = val mount =
Shell.su("su", "-mm", "-c", """"mount -o bind ${getAppPatchPath(app)} $stockPath"""") Shell.su("su", "-mm", "-c", """"mount -o bind ${getAppPatchPath(app)} $stockPath"""")
.exec() .exec()
if (!mount.isSuccess) 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) = fun destroyPatch(app: String) =
@ -132,11 +134,11 @@ private fun cleanPatchFiles(
postFsPath: String, postFsPath: String,
serviceDPath: String, serviceDPath: String,
patchPath: String, patchPath: String,
): PMRootResult<Nothing> { ): PackageManagerResult<Nothing> {
val files = mapOf( val files = mapOf(
postFsPath to PMRootStatus.SCRIPT_FAILED_DESTROY_POST_FS, postFsPath to PackageManagerStatus.SCRIPT_FAILED_DESTROY_POST_FS,
serviceDPath to PMRootStatus.SCRIPT_FAILED_DESTROY_SERVICE_D, serviceDPath to PackageManagerStatus.SCRIPT_FAILED_DESTROY_SERVICE_D,
patchPath to PMRootStatus.PATCH_FAILED_DESTROY, patchPath to PackageManagerStatus.PATCH_FAILED_DESTROY,
) )
for ((filePath, errorStatusType) in files) { for ((filePath, errorStatusType) in files) {
@ -145,11 +147,11 @@ private fun cleanPatchFiles(
if (exists()) delete() if (exists()) delete()
} }
} catch (e: SUIOException) { } 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( private fun copyScriptToDestination(

View File

@ -1,8 +1,11 @@
package com.vanced.manager.installer.util package com.vanced.manager.installer.util
import com.vanced.manager.repository.manager.PackageManagerResult
import com.vanced.manager.repository.manager.getOrElse
object RootPatchHelper { object RootPatchHelper {
fun cleanPatches(app: String): PMRootResult<Nothing> { fun cleanPatches(app: String): PackageManagerResult<Nothing> {
val cleanOldPatches = Patcher.destroyOldPatch(app) val cleanOldPatches = Patcher.destroyOldPatch(app)
if (cleanOldPatches.isError) if (cleanOldPatches.isError)
return cleanOldPatches return cleanOldPatches
@ -11,14 +14,14 @@ object RootPatchHelper {
if (cleanOldPatches.isError) if (cleanOldPatches.isError)
return cleanPatches return cleanPatches
return PMRootResult.Success() return PackageManagerResult.Success(null)
} }
inline fun prepareStock( inline fun prepareStock(
stockPackage: String, stockPackage: String,
stockVersion: String, stockVersion: String,
install: () -> PMRootResult<Nothing> install: () -> PackageManagerResult<Nothing>
): PMRootResult<Nothing> { ): PackageManagerResult<Nothing> {
val stockYoutubeVersion = PMRoot.getPackageVersionName(stockPackage) val stockYoutubeVersion = PMRoot.getPackageVersionName(stockPackage)
.getOrElse { null } .getOrElse { null }
if (stockYoutubeVersion != stockVersion) { if (stockYoutubeVersion != stockVersion) {
@ -31,14 +34,14 @@ object RootPatchHelper {
return installStock return installStock
} }
return PMRootResult.Success() return PackageManagerResult.Success(null)
} }
fun patchStock( fun patchStock(
patchPath: String, patchPath: String,
stockPackage: String, stockPackage: String,
app: String app: String
): PMRootResult<Nothing> { ): PackageManagerResult<Nothing> {
val movePatch = Patcher.movePatchToDataAdb(patchPath, app) val movePatch = Patcher.movePatchToDataAdb(patchPath, app)
if (movePatch.isError) if (movePatch.isError)
return movePatch return movePatch
@ -51,14 +54,14 @@ object RootPatchHelper {
.getOrElse { error -> return error }!! .getOrElse { error -> return error }!!
val setupScript = Patcher.setupScript(app, stockPackage, stockPackageDir) val setupScript = Patcher.setupScript(app, stockPackage, stockPackageDir)
if (setupScript is PMRootResult.Error) if (setupScript is PackageManagerResult.Error)
return setupScript return setupScript
val linkPatch = Patcher.linkPatch(app, stockPackage, stockPackageDir) val linkPatch = Patcher.linkPatch(app, stockPackage, stockPackageDir)
if (linkPatch is PMRootResult.Error) if (linkPatch is PackageManagerResult.Error)
return linkPatch 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.domain.model.AppType
import com.vanced.manager.network.GithubService import com.vanced.manager.network.GithubService
import com.vanced.manager.network.dto.GithubReleaseDto 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 { interface AppRepository {
@ -26,15 +28,18 @@ interface AppRepository {
class AppRepositoryImpl( class AppRepositoryImpl(
private val githubService: GithubService, private val githubService: GithubService,
private val pkgInfoDatasource: PkgInfoDatasource, private val nonrootPackageManager: NonrootPackageManager,
private val rootPackageManager: RootPackageManager,
) : AppRepository { ) : AppRepository {
override suspend fun getVancedYoutubeNonroot(): App { override suspend fun getVancedYoutubeNonroot(): App {
val githubRelease = githubService.getVancedYoutubeRelease() val githubRelease = githubService.getVancedYoutubeRelease()
val remoteVersionCode = githubRelease.getVersionCode() val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName() val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE) val installedVersionCode =
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE) nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE).getValueOrNull()
val installedVersionName =
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE).getValueOrNull()
return App( return App(
name = AppData.NAME_VANCED_YOUTUBE, name = AppData.NAME_VANCED_YOUTUBE,
iconResId = AppData.ICON_VANCED_YOUTUBE, iconResId = AppData.ICON_VANCED_YOUTUBE,
@ -55,9 +60,9 @@ class AppRepositoryImpl(
val remoteVersionCode = githubRelease.getVersionCode() val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName() val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = val installedVersionCode =
pkgInfoDatasource.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE) rootPackageManager.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE).getValueOrNull()
val installedVersionName = val installedVersionName =
pkgInfoDatasource.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE) rootPackageManager.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE).getValueOrNull()
return App( return App(
name = AppData.NAME_VANCED_YOUTUBE, name = AppData.NAME_VANCED_YOUTUBE,
iconResId = AppData.ICON_VANCED_YOUTUBE, iconResId = AppData.ICON_VANCED_YOUTUBE,
@ -78,9 +83,9 @@ class AppRepositoryImpl(
val remoteVersionCode = githubRelease.getVersionCode() val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName() val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = val installedVersionCode =
pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC) nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC).getValueOrNull()
val installedVersionName = val installedVersionName =
pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC) nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_YOUTUBE_MUSIC).getValueOrNull()
return App( return App(
name = AppData.NAME_VANCED_YOUTUBE_MUSIC, name = AppData.NAME_VANCED_YOUTUBE_MUSIC,
iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC, iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC,
@ -101,9 +106,9 @@ class AppRepositoryImpl(
val remoteVersionCode = githubRelease.getVersionCode() val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName() val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = val installedVersionCode =
pkgInfoDatasource.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC) rootPackageManager.getVersionCode(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC).getValueOrNull()
val installedVersionName = val installedVersionName =
pkgInfoDatasource.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC) rootPackageManager.getVersionName(AppData.PACKAGE_ROOT_VANCED_YOUTUBE_MUSIC).getValueOrNull()
return App( return App(
name = AppData.NAME_VANCED_YOUTUBE_MUSIC, name = AppData.NAME_VANCED_YOUTUBE_MUSIC,
iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC, iconResId = AppData.ICON_VANCED_YOUTUBE_MUSIC,
@ -123,8 +128,10 @@ class AppRepositoryImpl(
val githubRelease = githubService.getVancedMicrogRelease() val githubRelease = githubService.getVancedMicrogRelease()
val remoteVersionCode = githubRelease.getVersionCode() val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName() val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_MICROG) val installedVersionCode =
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_MICROG) nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_MICROG).getValueOrNull()
val installedVersionName =
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_MICROG).getValueOrNull()
return App( return App(
name = AppData.NAME_VANCED_MICROG, name = AppData.NAME_VANCED_MICROG,
iconResId = AppData.ICON_VANCED_MICROG, iconResId = AppData.ICON_VANCED_MICROG,
@ -144,8 +151,10 @@ class AppRepositoryImpl(
val githubRelease = githubService.getVancedManagerRelease() val githubRelease = githubService.getVancedManagerRelease()
val remoteVersionCode = githubRelease.getVersionCode() val remoteVersionCode = githubRelease.getVersionCode()
val remoteVersionName = githubRelease.getVersionName() val remoteVersionName = githubRelease.getVersionName()
val installedVersionCode = pkgInfoDatasource.getVersionCode(AppData.PACKAGE_VANCED_MANAGER) val installedVersionCode =
val installedVersionName = pkgInfoDatasource.getVersionName(AppData.PACKAGE_VANCED_MANAGER) nonrootPackageManager.getVersionCode(AppData.PACKAGE_VANCED_MANAGER).getValueOrNull()
val installedVersionName =
nonrootPackageManager.getVersionName(AppData.PACKAGE_VANCED_MANAGER).getValueOrNull()
return App( return App(
name = AppData.NAME_VANCED_MANAGER, name = AppData.NAME_VANCED_MANAGER,
iconResId = AppData.ICON_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.MicrogInstaller
import com.vanced.manager.installer.impl.MusicInstaller import com.vanced.manager.installer.impl.MusicInstaller
import com.vanced.manager.installer.impl.VancedInstaller 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.MICROG_NAME
import com.vanced.manager.network.util.MUSIC_NAME import com.vanced.manager.network.util.MUSIC_NAME
import com.vanced.manager.network.util.VANCED_NAME import com.vanced.manager.network.util.VANCED_NAME
import com.vanced.manager.preferences.holder.managerVariantPref import com.vanced.manager.preferences.holder.managerVariantPref
import com.vanced.manager.repository.manager.PackageManagerResult
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -117,7 +117,7 @@ class InstallViewModel(
} }
} }
private fun installApp( private suspend fun installApp(
appName: String, appName: String,
appVersions: List<String>?, appVersions: List<String>?,
) { ) {
@ -127,11 +127,11 @@ class InstallViewModel(
if (isRoot) { if (isRoot) {
when (val installStatus = installer.installRoot(appVersions)) { when (val installStatus = installer.installRoot(appVersions)) {
is PMRootResult.Success -> { is PackageManagerResult.Success -> {
status = Status.Installed status = Status.Installed
log(Log.Success("Successfully installed")) log(Log.Success("Successfully installed"))
} }
is PMRootResult.Error -> { is PackageManagerResult.Error -> {
status = Status.Failure status = Status.Failure
log(Log.Error("Failed to install app", installStatus.message)) 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 com.topjohnwu.superuser.Shell
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
val Shell.Result.outString val Shell.Result.outString
@ -13,8 +14,26 @@ val Shell.Result.errString
val isMagiskInstalled val isMagiskInstalled
get() = Shell.rootAccess() && Shell.su("magisk", "-c").exec().isSuccess get() = Shell.rootAccess() && Shell.su("magisk", "-c").exec().isSuccess
suspend fun Shell.Job.await() = suspend fun Shell.Job.await(): Shell.Result {
suspendCoroutine<Shell.Result> { continuation -> return suspendCoroutine { continuation ->
submit(/*continuation.context.asExecutor(),*/ continuation::resume) 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))
}
}
}
}