From 7141df8af8997ded0260299ae64d7c321735a637 Mon Sep 17 00:00:00 2001 From: X1nto Date: Tue, 23 Nov 2021 18:28:04 +0400 Subject: [PATCH] implement nonroot uninstallation --- app/src/main/AndroidManifest.xml | 3 +- .../installer/service/AppInstallService.kt | 15 +++--- .../installer/service/AppUninstallService.kt | 39 ++++++++++++++ .../vanced/manager/core/installer/util/PM.kt | 54 +++++++++++-------- .../com/vanced/manager/ui/MainActivity.kt | 22 +++++--- .../vanced/manager/ui/screens/HomeLayout.kt | 6 ++- .../manager/ui/viewmodel/MainViewModel.kt | 8 +++ 7 files changed, 107 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/vanced/manager/core/installer/service/AppUninstallService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d23988b9..ae12ca2a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,9 +41,8 @@ android:theme="@style/Theme.MaterialComponents.NoActionBar" android:label="@string/app_name"/> - - + diff --git a/app/src/main/java/com/vanced/manager/core/installer/service/AppInstallService.kt b/app/src/main/java/com/vanced/manager/core/installer/service/AppInstallService.kt index bd2cf420..ed3d3183 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/service/AppInstallService.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/service/AppInstallService.kt @@ -12,7 +12,9 @@ class AppInstallService : Service() { flags: Int, startId: Int ): Int { - when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) { + val extraStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999) + val extraStatusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + when (extraStatus) { PackageInstaller.STATUS_PENDING_USER_ACTION -> { startActivity( intent.getParcelableExtra(Intent.EXTRA_INTENT).apply { @@ -22,12 +24,9 @@ class AppInstallService : Service() { } else -> { sendBroadcast(Intent().apply { - action = APP_INSTALL_STATUS - putExtra(EXTRA_INSTALL_STATUS, status) - putExtra( - EXTRA_INSTALL_EXTRA, - intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) - ) + action = APP_INSTALL_ACTION + putExtra(EXTRA_INSTALL_STATUS, extraStatus) + putExtra(EXTRA_INSTALL_EXTRA, extraStatusMessage) }) } } @@ -38,7 +37,7 @@ class AppInstallService : Service() { override fun onBind(intent: Intent?): IBinder? = null companion object { - const val APP_INSTALL_STATUS = "APP_INSTALL_STATUS" + const val APP_INSTALL_ACTION = "APP_INSTALL_ACTION" const val EXTRA_INSTALL_STATUS = "EXTRA_INSTALL_STATUS" const val EXTRA_INSTALL_EXTRA = "EXTRA_INSTALL_EXTRA" diff --git a/app/src/main/java/com/vanced/manager/core/installer/service/AppUninstallService.kt b/app/src/main/java/com/vanced/manager/core/installer/service/AppUninstallService.kt new file mode 100644 index 00000000..a06af653 --- /dev/null +++ b/app/src/main/java/com/vanced/manager/core/installer/service/AppUninstallService.kt @@ -0,0 +1,39 @@ +package com.vanced.manager.core.installer.service + +import android.app.Service +import android.content.Intent +import android.content.pm.PackageInstaller +import android.os.IBinder + +class AppUninstallService : Service() { + + override fun onStartCommand( + intent: Intent, + flags: Int, + startId: Int + ): Int { + when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) { + PackageInstaller.STATUS_PENDING_USER_ACTION -> { + startActivity( + intent.getParcelableExtra(Intent.EXTRA_INTENT).apply { + this?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + ) + } + else -> { + sendBroadcast(Intent().apply { + action = APP_UNINSTALL_ACTION + }) + } + } + stopSelf() + return START_NOT_STICKY + } + + override fun onBind(intent: Intent?): IBinder? = null + + companion object { + const val APP_UNINSTALL_ACTION = "APP_UNINSTALL_ACTION" + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt b/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt index 8bb1f08a..c419e33f 100644 --- a/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt +++ b/app/src/main/java/com/vanced/manager/core/installer/util/PM.kt @@ -7,6 +7,7 @@ import android.content.pm.PackageInstaller import android.content.pm.PackageManager import android.os.Build import com.vanced.manager.core.installer.service.AppInstallService +import com.vanced.manager.core.installer.service.AppUninstallService import java.io.File import java.io.FileInputStream @@ -17,7 +18,7 @@ fun installApp(apk: File, context: Context) { val session = packageInstaller.openSession(packageInstaller.createSession(sessionParams)) writeApkToSession(apk, session) - session.commit(getIntentSender(context)) + session.commit(context.installIntentSender) session.close() } @@ -28,10 +29,28 @@ fun installSplitApp(apks: Array, context: Context) { for (apk in apks) { writeApkToSession(apk, session) } - session.commit(getIntentSender(context)) + session.commit(context.installIntentSender) session.close() } +fun uninstallPackage(pkg: String, context: Context) { + val packageInstaller = context.packageManager.packageInstaller + packageInstaller.uninstall(pkg, context.uninstallIntentSender) +} + +private fun writeApkToSession( + apk: File, + session: PackageInstaller.Session +) { + val inputStream = FileInputStream(apk) + val outputStream = session.openWrite(apk.name, 0, apk.length()) + inputStream.copyTo(outputStream, byteArraySize) + session.fsync(outputStream) + inputStream.close() + outputStream.flush() + outputStream.close() +} + private val intentFlags get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE @@ -47,27 +66,18 @@ private val sessionParams } } -private fun getIntentSender(context: Context) = - PendingIntent.getService( - context, +private val Context.installIntentSender + get() = PendingIntent.getService( + this, 0, - Intent(context, AppInstallService::class.java), + Intent(this, AppInstallService::class.java), intentFlags ).intentSender -private fun writeApkToSession( - apk: File, - session: PackageInstaller.Session -) { - val inputStream = FileInputStream(apk) - val outputStream = session.openWrite(apk.name, 0, apk.length()) - val buffer = ByteArray(byteArraySize) - var length: Int - while (inputStream.read(buffer).also { length = it } > 0) { - outputStream.write(buffer, 0, length) - } - session.fsync(outputStream) - inputStream.close() - outputStream.flush() - outputStream.close() -} \ No newline at end of file +private val Context.uninstallIntentSender + get() = PendingIntent.getService( + this, + 0, + Intent(this, AppUninstallService::class.java), + intentFlags + ).intentSender \ No newline at end of file diff --git a/app/src/main/java/com/vanced/manager/ui/MainActivity.kt b/app/src/main/java/com/vanced/manager/ui/MainActivity.kt index 6e31136a..006d0c7f 100644 --- a/app/src/main/java/com/vanced/manager/ui/MainActivity.kt +++ b/app/src/main/java/com/vanced/manager/ui/MainActivity.kt @@ -17,6 +17,7 @@ import com.github.zsoltk.compose.backpress.LocalBackPressHandler import com.github.zsoltk.compose.router.Router import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.vanced.manager.core.installer.service.AppInstallService +import com.vanced.manager.core.installer.service.AppUninstallService import com.vanced.manager.ui.component.color.managerSurfaceColor import com.vanced.manager.ui.screens.* import com.vanced.manager.ui.theme.ManagerTheme @@ -36,12 +37,18 @@ class MainActivity : ComponentActivity() { private val installBroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action != AppInstallService.APP_INSTALL_STATUS) return - - installViewModel.postInstallStatus( - pmStatus = intent.getIntExtra(AppInstallService.EXTRA_INSTALL_STATUS, -999), - extra = intent.getStringExtra(AppInstallService.EXTRA_INSTALL_EXTRA)!!, - ) + when (intent?.action) { + AppInstallService.APP_INSTALL_ACTION -> { + installViewModel.postInstallStatus( + pmStatus = intent.getIntExtra(AppInstallService.EXTRA_INSTALL_STATUS, -999), + extra = intent.getStringExtra(AppInstallService.EXTRA_INSTALL_EXTRA)!!, + ) + mainViewModel.fetch() + } + AppUninstallService.APP_UNINSTALL_ACTION -> { + mainViewModel.fetch() + } + } } } @@ -151,7 +158,8 @@ class MainActivity : ComponentActivity() { registerReceiver( installBroadcastReceiver, IntentFilter().apply { - addAction(AppInstallService.APP_INSTALL_STATUS) + addAction(AppInstallService.APP_INSTALL_ACTION) + addAction(AppUninstallService.APP_UNINSTALL_ACTION) } ) } diff --git a/app/src/main/java/com/vanced/manager/ui/screens/HomeLayout.kt b/app/src/main/java/com/vanced/manager/ui/screens/HomeLayout.kt index 254553bb..1777fa38 100644 --- a/app/src/main/java/com/vanced/manager/ui/screens/HomeLayout.kt +++ b/app/src/main/java/com/vanced/manager/ui/screens/HomeLayout.kt @@ -136,7 +136,11 @@ fun HomeLayout( app.installationOptions ) }, - onAppUninstallClick = { /*TODO*/ }, + onAppUninstallClick = { + viewModel.uninstallApp( + appPackage = app.packageName + ) + }, onAppLaunchClick = { viewModel.launchApp( appName = app.name, diff --git a/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt b/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt index b458a8ce..8b82a498 100644 --- a/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/com/vanced/manager/ui/viewmodel/MainViewModel.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.vanced.manager.core.installer.util.uninstallPackage import com.vanced.manager.core.preferences.holder.managerVariantPref import com.vanced.manager.core.preferences.holder.musicEnabled import com.vanced.manager.core.preferences.holder.vancedEnabled @@ -96,6 +97,13 @@ class MainViewModel( } } + //TODO implement root uninstallation + fun uninstallApp( + appPackage: String, + ) { + uninstallPackage(appPackage, app) + } + init { fetch() }