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