start migrating packagemanager to di
This commit is contained in:
parent
e010ed0e80
commit
a1662bab5a
|
@ -19,6 +19,7 @@ class ManagerApplication : Application() {
|
|||
datasourceModule,
|
||||
downloaderModule,
|
||||
installerModule,
|
||||
managerModule,
|
||||
networkModule,
|
||||
repositoryModule,
|
||||
serviceModule,
|
||||
|
|
|
@ -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()) }
|
||||
}
|
|
@ -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()) }
|
||||
}
|
|
@ -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() }
|
||||
}
|
|
@ -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()) }
|
||||
}
|
|
@ -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>
|
||||
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue