1 /* <lambda>null2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.spa.app.appinfo 18 19 import android.app.settings.SettingsEnums 20 import android.content.Context 21 import android.content.Intent 22 import android.content.IntentFilter 23 import android.content.pm.PackageInfo 24 import android.content.pm.PackageManager 25 import android.os.UserHandle 26 import android.util.Log 27 import androidx.compose.runtime.Composable 28 import com.android.settings.overlay.FeatureFactory 29 import com.android.settings.spa.app.startUninstallActivity 30 import com.android.settingslib.spa.framework.compose.LocalNavController 31 import com.android.settingslib.spaprivileged.framework.common.activityManager 32 import com.android.settingslib.spaprivileged.framework.common.asUser 33 import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser 34 import com.android.settingslib.spaprivileged.model.app.IPackageManagers 35 import com.android.settingslib.spaprivileged.model.app.PackageManagers 36 import kotlinx.coroutines.CoroutineScope 37 import kotlinx.coroutines.Dispatchers 38 import kotlinx.coroutines.flow.MutableStateFlow 39 import kotlinx.coroutines.flow.StateFlow 40 import kotlinx.coroutines.launch 41 42 private const val TAG = "PackageInfoPresenter" 43 44 /** 45 * Presenter which helps to present the status change of [PackageInfo]. 46 */ 47 class PackageInfoPresenter( 48 val context: Context, 49 val packageName: String, 50 val userId: Int, 51 private val coroutineScope: CoroutineScope, 52 private val packageManagers: IPackageManagers = PackageManagers, 53 ) { 54 private val metricsFeatureProvider = FeatureFactory.getFactory(context).metricsFeatureProvider 55 private val userHandle = UserHandle.of(userId) 56 val userContext by lazy { context.asUser(userHandle) } 57 val userPackageManager: PackageManager by lazy { userContext.packageManager } 58 private val _flow: MutableStateFlow<PackageInfo?> = MutableStateFlow(null) 59 60 val flow: StateFlow<PackageInfo?> = _flow 61 62 fun reloadPackageInfo() { 63 coroutineScope.launch(Dispatchers.IO) { 64 _flow.value = getPackageInfo() 65 } 66 } 67 68 /** 69 * Detects the package removed event. 70 */ 71 @Composable 72 fun PackageRemoveDetector() { 73 val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_REMOVED).apply { 74 addDataScheme("package") 75 } 76 val navController = LocalNavController.current 77 DisposableBroadcastReceiverAsUser(intentFilter, userHandle) { intent -> 78 if (packageName == intent.data?.schemeSpecificPart) { 79 val packageInfo = flow.value 80 if (packageInfo != null && packageInfo.applicationInfo.isSystemApp) { 81 // System app still exists after uninstalling the updates, refresh the page. 82 reloadPackageInfo() 83 } else { 84 navController.navigateBack() 85 } 86 } 87 } 88 } 89 90 /** Enables this package. */ 91 fun enable() { 92 logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP) 93 coroutineScope.launch(Dispatchers.IO) { 94 userPackageManager.setApplicationEnabledSetting( 95 packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0 96 ) 97 reloadPackageInfo() 98 } 99 } 100 101 /** Disables this package. */ 102 fun disable() { 103 logAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP) 104 coroutineScope.launch(Dispatchers.IO) { 105 userPackageManager.setApplicationEnabledSetting( 106 packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0 107 ) 108 reloadPackageInfo() 109 } 110 } 111 112 /** Starts the uninstallation activity. */ 113 fun startUninstallActivity(forAllUsers: Boolean = false) { 114 logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP) 115 context.startUninstallActivity(packageName, userHandle, forAllUsers) 116 } 117 118 /** Clears this instant app. */ 119 fun clearInstantApp() { 120 logAction(SettingsEnums.ACTION_SETTINGS_CLEAR_INSTANT_APP) 121 coroutineScope.launch(Dispatchers.IO) { 122 userPackageManager.deletePackageAsUser(packageName, null, 0, userId) 123 reloadPackageInfo() 124 } 125 } 126 127 /** Force stops this package. */ 128 fun forceStop() { 129 logAction(SettingsEnums.ACTION_APP_FORCE_STOP) 130 coroutineScope.launch(Dispatchers.Default) { 131 Log.d(TAG, "Stopping package $packageName") 132 context.activityManager.forceStopPackageAsUser(packageName, userId) 133 reloadPackageInfo() 134 } 135 } 136 137 fun logAction(category: Int) { 138 metricsFeatureProvider.action(context, category, packageName) 139 } 140 141 private fun getPackageInfo() = 142 packageManagers.getPackageInfoAsUser( 143 packageName = packageName, 144 flags = PackageManager.MATCH_ANY_USER or 145 PackageManager.MATCH_DISABLED_COMPONENTS or 146 PackageManager.GET_PERMISSIONS, 147 userId = userId, 148 ) 149 } 150