Loading packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java +1 −7 Original line number Diff line number Diff line Loading @@ -91,8 +91,7 @@ public class UninstallerActivity extends Activity { // be stale, if e.g. the app was uninstalled while the activity was destroyed. super.onCreate(null); // TODO(b/318521110) Enable PIA v2 for archive dialog. if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) { if (usePiaV2() && !isTv()) { Log.i(TAG, "Using Pia V2"); boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); Loading Loading @@ -225,11 +224,6 @@ public class UninstallerActivity extends Activity { showConfirmationDialog(); } private boolean isArchiveDialog(Intent intent) { return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0) & PackageManager.DELETE_ARCHIVE) != 0; } /** * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent} * to archive an app if requested. Loading packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt +2 −1 Original line number Diff line number Diff line Loading @@ -736,7 +736,8 @@ class InstallRepository(private val context: Context) { val appInfo = packageManager.getApplicationInfo( pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES ) if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) { // If the package is archived, treat it as an update case. if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) { return false } } catch (e: PackageManager.NameNotFoundException) { Loading packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt +73 −15 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo import android.content.pm.PackageInstaller import android.content.pm.PackageManager import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.PackageManager.PackageInfoFlags import android.content.pm.VersionedPackage import android.graphics.drawable.Icon import android.os.Build Loading @@ -51,6 +53,9 @@ import com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionF import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame import com.android.packageinstaller.v2.model.UninstallAborted.Companion.ABORT_REASON_UNINSTALL_DONE import android.content.pm.Flags as PmFlags import android.multiuser.Flags as MultiuserFlags class UninstallRepository(private val context: Context) { Loading @@ -71,6 +76,7 @@ class UninstallRepository(private val context: Context) { private var uninstallFromAllUsers = false private var isClonedApp = false private var uninstallId = 0 private var deleteFlags = 0 fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage { this.intent = intent Loading Loading @@ -155,7 +161,9 @@ class UninstallRepository(private val context: Context) { try { targetAppInfo = packageManager.getApplicationInfo( targetPackageName!!, PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong()) ApplicationInfoFlags.of( PackageManager.MATCH_ANY_USER.toLong() or PackageManager.MATCH_ARCHIVED_PACKAGES ) ) } catch (e: PackageManager.NameNotFoundException) { Log.e(LOG_TAG, "Unable to get packageName") Loading @@ -180,9 +188,27 @@ class UninstallRepository(private val context: Context) { } } parseDeleteFlags(intent) return UninstallReady() } /** * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent} * to archive an app if requested. * * Do not parse other flags because developers might pass here any flags which might cause * unintended behaviour. * For more context {@link com.android.server.pm.PackageArchiver#requestArchive}. */ private fun parseDeleteFlags(intent: Intent) { val flags = intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0) val archive = flags and PackageManager.DELETE_ARCHIVE val keepData = flags and PackageManager.DELETE_KEEP_DATA deleteFlags = archive or keepData } fun generateUninstallDetails(): UninstallStage { val messageBuilder = StringBuilder() Loading @@ -201,6 +227,8 @@ class UninstallRepository(private val context: Context) { } val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 val isArchive = PmFlags.archiving() && ((deleteFlags and PackageManager.DELETE_ARCHIVE) != 0) val myUserHandle = Process.myUserHandle() val isSingleUser = isSingleUser() Loading @@ -215,36 +243,56 @@ class UninstallRepository(private val context: Context) { ) ) } else if (uninstallFromAllUsers && !isSingleUser) { messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users)) val messageString = if (isArchive) { context.getString(R.string.archive_application_text_all_users) } else { context.getString(R.string.uninstall_application_text_all_users) } messageBuilder.append(messageString) } else if (uninstalledUser != myUserHandle) { // Uninstalling user is issuing uninstall for another user val customUserManager = context.createContextAsUser(uninstalledUser!!, 0) .getSystemService(UserManager::class.java) val userName = customUserManager!!.userName var messageString = context.getString( R.string.uninstall_application_text_user, userName ) var messageString = if (isArchive) { context.getString(R.string.archive_application_text_user, userName) } else { context.getString(R.string.uninstall_application_text_user, userName) } if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) { if (customUserManager.isManagedProfile) { messageString = context.getString( messageString = if (isArchive) { context.getString( R.string.archive_application_text_current_user_work_profile, userName ) } else { context.getString( R.string.uninstall_application_text_current_user_work_profile, userName ) } } else if (customUserManager.isCloneProfile){ isClonedApp = true messageString = context.getString( R.string.uninstall_application_text_current_user_clone_profile ) } else if (Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures() && MultiuserFlags.enablePrivateSpaceFeatures() && customUserManager.isPrivateProfile ) { // TODO(b/324244123): Get these Strings from a User Property API. messageString = context.getString( messageString = if (isArchive) { context.getString( R.string.archive_application_text_current_user_private_profile, userName ) } else { context.getString( R.string.uninstall_application_text_current_user_private_profile ) } } } messageBuilder.append(messageString) } else if (isCloneProfile(uninstalledUser!!)) { isClonedApp = true Loading @@ -262,6 +310,8 @@ class UninstallRepository(private val context: Context) { targetAppLabel ) ) } else if (isArchive) { messageBuilder.append(context.getString(R.string.archive_application_text)) } else { messageBuilder.append(context.getString(R.string.uninstall_application_text)) } Loading @@ -270,15 +320,21 @@ class UninstallRepository(private val context: Context) { val title = if (isClonedApp) { context.getString(R.string.cloned_app_label, targetAppLabel) } else if (isArchive) { context.getString(R.string.archiving_app_label, targetAppLabel) } else { targetAppLabel.toString() } var suggestToKeepAppData = false try { val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0) val pkgInfo = packageManager.getPackageInfo( targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES) ) suggestToKeepAppData = pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData() pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData() && !isArchive } catch (e: PackageManager.NameNotFoundException) { Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e) } Loading @@ -291,7 +347,7 @@ class UninstallRepository(private val context: Context) { ) } return UninstallUserActionRequired(title, message, appDataSize) return UninstallUserActionRequired(title, message, appDataSize, isArchive) } /** Loading Loading @@ -444,10 +500,11 @@ class UninstallRepository(private val context: Context) { callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message) // Since the caller already received the results, just finish the app at this point uninstallResult.value = null uninstallResult.value = UninstallAborted(ABORT_REASON_UNINSTALL_DONE) return } val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false) if (returnResult || callingActivity != null) { val intent = Intent() intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus) Loading Loading @@ -717,6 +774,7 @@ class UninstallRepository(private val context: Context) { ): Boolean { var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0 flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0 flags = flags or deleteFlags return try { context.createContextAsUser(targetUser, 0) Loading packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt +8 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,8 @@ class UninstallReady : UninstallStage(STAGE_READY) data class UninstallUserActionRequired( val title: String? = null, val message: String? = null, val appDataSize: Long = 0 val appDataSize: Long = 0, val isArchive: Boolean = false ) : UninstallStage(STAGE_USER_ACTION_REQUIRED) data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) : Loading Loading @@ -96,6 +97,11 @@ data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED dialogTextResource = R.string.user_is_not_allowed_dlg_text } ABORT_REASON_UNINSTALL_DONE -> { dialogTitleResource = 0 dialogTextResource = 0 } else -> { dialogTitleResource = 0 dialogTextResource = R.string.generic_error_dlg_text Loading @@ -107,6 +113,7 @@ data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED const val ABORT_REASON_GENERIC_ERROR = 0 const val ABORT_REASON_APP_UNAVAILABLE = 1 const val ABORT_REASON_USER_NOT_ALLOWED = 2 const val ABORT_REASON_UNINSTALL_DONE = 3 } } packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ public class UninstallConfirmationFragment extends DialogFragment { Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData); AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()) .setTitle(mDialogData.getTitle()) .setPositiveButton(R.string.ok, .setPositiveButton(mDialogData.isArchive() ? R.string.archive : R.string.ok, (dialogInt, which) -> mUninstallActionListener.onPositiveResponse( mKeepData != null && mKeepData.isChecked())) .setNegativeButton(R.string.cancel, Loading Loading
packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java +1 −7 Original line number Diff line number Diff line Loading @@ -91,8 +91,7 @@ public class UninstallerActivity extends Activity { // be stale, if e.g. the app was uninstalled while the activity was destroyed. super.onCreate(null); // TODO(b/318521110) Enable PIA v2 for archive dialog. if (usePiaV2() && !isTv() && !isArchiveDialog(getIntent())) { if (usePiaV2() && !isTv()) { Log.i(TAG, "Using Pia V2"); boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); Loading Loading @@ -225,11 +224,6 @@ public class UninstallerActivity extends Activity { showConfirmationDialog(); } private boolean isArchiveDialog(Intent intent) { return (intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0) & PackageManager.DELETE_ARCHIVE) != 0; } /** * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent} * to archive an app if requested. Loading
packages/PackageInstaller/src/com/android/packageinstaller/v2/model/InstallRepository.kt +2 −1 Original line number Diff line number Diff line Loading @@ -736,7 +736,8 @@ class InstallRepository(private val context: Context) { val appInfo = packageManager.getApplicationInfo( pkgName, PackageManager.MATCH_UNINSTALLED_PACKAGES ) if (appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) { // If the package is archived, treat it as an update case. if (!appInfo.isArchived && appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0) { return false } } catch (e: PackageManager.NameNotFoundException) { Loading
packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt +73 −15 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.content.pm.ActivityInfo import android.content.pm.ApplicationInfo import android.content.pm.PackageInstaller import android.content.pm.PackageManager import android.content.pm.PackageManager.ApplicationInfoFlags import android.content.pm.PackageManager.PackageInfoFlags import android.content.pm.VersionedPackage import android.graphics.drawable.Icon import android.os.Build Loading @@ -51,6 +53,9 @@ import com.android.packageinstaller.v2.model.PackageUtil.getMaxTargetSdkVersionF import com.android.packageinstaller.v2.model.PackageUtil.getPackageNameForUid import com.android.packageinstaller.v2.model.PackageUtil.isPermissionGranted import com.android.packageinstaller.v2.model.PackageUtil.isProfileOfOrSame import com.android.packageinstaller.v2.model.UninstallAborted.Companion.ABORT_REASON_UNINSTALL_DONE import android.content.pm.Flags as PmFlags import android.multiuser.Flags as MultiuserFlags class UninstallRepository(private val context: Context) { Loading @@ -71,6 +76,7 @@ class UninstallRepository(private val context: Context) { private var uninstallFromAllUsers = false private var isClonedApp = false private var uninstallId = 0 private var deleteFlags = 0 fun performPreUninstallChecks(intent: Intent, callerInfo: CallerInfo): UninstallStage { this.intent = intent Loading Loading @@ -155,7 +161,9 @@ class UninstallRepository(private val context: Context) { try { targetAppInfo = packageManager.getApplicationInfo( targetPackageName!!, PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_ANY_USER.toLong()) ApplicationInfoFlags.of( PackageManager.MATCH_ANY_USER.toLong() or PackageManager.MATCH_ARCHIVED_PACKAGES ) ) } catch (e: PackageManager.NameNotFoundException) { Log.e(LOG_TAG, "Unable to get packageName") Loading @@ -180,9 +188,27 @@ class UninstallRepository(private val context: Context) { } } parseDeleteFlags(intent) return UninstallReady() } /** * Parses specific {@link android.content.pm.PackageManager.DeleteFlags} from {@link Intent} * to archive an app if requested. * * Do not parse other flags because developers might pass here any flags which might cause * unintended behaviour. * For more context {@link com.android.server.pm.PackageArchiver#requestArchive}. */ private fun parseDeleteFlags(intent: Intent) { val flags = intent.getIntExtra(PackageInstaller.EXTRA_DELETE_FLAGS, 0) val archive = flags and PackageManager.DELETE_ARCHIVE val keepData = flags and PackageManager.DELETE_KEEP_DATA deleteFlags = archive or keepData } fun generateUninstallDetails(): UninstallStage { val messageBuilder = StringBuilder() Loading @@ -201,6 +227,8 @@ class UninstallRepository(private val context: Context) { } val isUpdate = (targetAppInfo!!.flags and ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0 val isArchive = PmFlags.archiving() && ((deleteFlags and PackageManager.DELETE_ARCHIVE) != 0) val myUserHandle = Process.myUserHandle() val isSingleUser = isSingleUser() Loading @@ -215,36 +243,56 @@ class UninstallRepository(private val context: Context) { ) ) } else if (uninstallFromAllUsers && !isSingleUser) { messageBuilder.append(context.getString(R.string.uninstall_application_text_all_users)) val messageString = if (isArchive) { context.getString(R.string.archive_application_text_all_users) } else { context.getString(R.string.uninstall_application_text_all_users) } messageBuilder.append(messageString) } else if (uninstalledUser != myUserHandle) { // Uninstalling user is issuing uninstall for another user val customUserManager = context.createContextAsUser(uninstalledUser!!, 0) .getSystemService(UserManager::class.java) val userName = customUserManager!!.userName var messageString = context.getString( R.string.uninstall_application_text_user, userName ) var messageString = if (isArchive) { context.getString(R.string.archive_application_text_user, userName) } else { context.getString(R.string.uninstall_application_text_user, userName) } if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) { if (customUserManager.isManagedProfile) { messageString = context.getString( messageString = if (isArchive) { context.getString( R.string.archive_application_text_current_user_work_profile, userName ) } else { context.getString( R.string.uninstall_application_text_current_user_work_profile, userName ) } } else if (customUserManager.isCloneProfile){ isClonedApp = true messageString = context.getString( R.string.uninstall_application_text_current_user_clone_profile ) } else if (Flags.allowPrivateProfile() && android.multiuser.Flags.enablePrivateSpaceFeatures() && MultiuserFlags.enablePrivateSpaceFeatures() && customUserManager.isPrivateProfile ) { // TODO(b/324244123): Get these Strings from a User Property API. messageString = context.getString( messageString = if (isArchive) { context.getString( R.string.archive_application_text_current_user_private_profile, userName ) } else { context.getString( R.string.uninstall_application_text_current_user_private_profile ) } } } messageBuilder.append(messageString) } else if (isCloneProfile(uninstalledUser!!)) { isClonedApp = true Loading @@ -262,6 +310,8 @@ class UninstallRepository(private val context: Context) { targetAppLabel ) ) } else if (isArchive) { messageBuilder.append(context.getString(R.string.archive_application_text)) } else { messageBuilder.append(context.getString(R.string.uninstall_application_text)) } Loading @@ -270,15 +320,21 @@ class UninstallRepository(private val context: Context) { val title = if (isClonedApp) { context.getString(R.string.cloned_app_label, targetAppLabel) } else if (isArchive) { context.getString(R.string.archiving_app_label, targetAppLabel) } else { targetAppLabel.toString() } var suggestToKeepAppData = false try { val pkgInfo = packageManager.getPackageInfo(targetPackageName!!, 0) val pkgInfo = packageManager.getPackageInfo( targetPackageName!!, PackageInfoFlags.of(PackageManager.MATCH_ARCHIVED_PACKAGES) ) suggestToKeepAppData = pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData() pkgInfo.applicationInfo != null && pkgInfo.applicationInfo!!.hasFragileUserData() && !isArchive } catch (e: PackageManager.NameNotFoundException) { Log.e(LOG_TAG, "Cannot check hasFragileUserData for $targetPackageName", e) } Loading @@ -291,7 +347,7 @@ class UninstallRepository(private val context: Context) { ) } return UninstallUserActionRequired(title, message, appDataSize) return UninstallUserActionRequired(title, message, appDataSize, isArchive) } /** Loading Loading @@ -444,10 +500,11 @@ class UninstallRepository(private val context: Context) { callback!!.onUninstallComplete(targetPackageName!!, legacyStatus, message) // Since the caller already received the results, just finish the app at this point uninstallResult.value = null uninstallResult.value = UninstallAborted(ABORT_REASON_UNINSTALL_DONE) return } val returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false) if (returnResult || callingActivity != null) { val intent = Intent() intent.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus) Loading Loading @@ -717,6 +774,7 @@ class UninstallRepository(private val context: Context) { ): Boolean { var flags = if (uninstallFromAllUsers) PackageManager.DELETE_ALL_USERS else 0 flags = flags or if (keepData) PackageManager.DELETE_KEEP_DATA else 0 flags = flags or deleteFlags return try { context.createContextAsUser(targetUser, 0) Loading
packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallStages.kt +8 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,8 @@ class UninstallReady : UninstallStage(STAGE_READY) data class UninstallUserActionRequired( val title: String? = null, val message: String? = null, val appDataSize: Long = 0 val appDataSize: Long = 0, val isArchive: Boolean = false ) : UninstallStage(STAGE_USER_ACTION_REQUIRED) data class UninstallUninstalling(val appLabel: CharSequence, val isCloneUser: Boolean) : Loading Loading @@ -96,6 +97,11 @@ data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED dialogTextResource = R.string.user_is_not_allowed_dlg_text } ABORT_REASON_UNINSTALL_DONE -> { dialogTitleResource = 0 dialogTextResource = 0 } else -> { dialogTitleResource = 0 dialogTextResource = R.string.generic_error_dlg_text Loading @@ -107,6 +113,7 @@ data class UninstallAborted(val abortReason: Int) : UninstallStage(STAGE_ABORTED const val ABORT_REASON_GENERIC_ERROR = 0 const val ABORT_REASON_APP_UNAVAILABLE = 1 const val ABORT_REASON_USER_NOT_ALLOWED = 2 const val ABORT_REASON_UNINSTALL_DONE = 3 } }
packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/UninstallConfirmationFragment.java +1 −1 Original line number Diff line number Diff line Loading @@ -60,7 +60,7 @@ public class UninstallConfirmationFragment extends DialogFragment { Log.i(LOG_TAG, "Creating " + LOG_TAG + "\n" + mDialogData); AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()) .setTitle(mDialogData.getTitle()) .setPositiveButton(R.string.ok, .setPositiveButton(mDialogData.isArchive() ? R.string.archive : R.string.ok, (dialogInt, which) -> mUninstallActionListener.onPositiveResponse( mKeepData != null && mKeepData.isChecked())) .setNegativeButton(R.string.cancel, Loading