Adding custom text for Private Space app uninstall.
A similar change was merged as ag/25450443, this
is the equivalent of same in UninstallRepositary.
Bug: 319250810
Test: Manual
Change-Id: I733eec3f1f7bdc7edbcac885c853218a8b2d5f74
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4aae6e0..806b06a1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11053,6 +11053,7 @@
field public static final String USER_TYPE_FULL_SYSTEM = "android.os.usertype.full.SYSTEM";
field public static final String USER_TYPE_PROFILE_CLONE = "android.os.usertype.profile.CLONE";
field public static final String USER_TYPE_PROFILE_MANAGED = "android.os.usertype.profile.MANAGED";
+ field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
field public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fc095d4..d6b9a79 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2454,7 +2454,6 @@
method public boolean isVisibleBackgroundUsersOnDefaultDisplaySupported();
method public boolean isVisibleBackgroundUsersSupported();
method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
- field @FlaggedApi("android.os.allow_private_profile") public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
}
public final class VibrationAttributes implements android.os.Parcelable {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 0da19df..c1d163b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -180,11 +180,14 @@
/**
- * User type representing a private profile.
+ * User type representing a private profile. Private profile is a user profile that can be used
+ * as an alternative user-space to install and use sensitive apps.
+ * UI surfaces can adopt an alternative strategy to show apps belonging to this profile, in line
+ * with their sensitive nature.
* @hide
*/
@FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
- @TestApi
+ @SystemApi
public static final String USER_TYPE_PROFILE_PRIVATE = "android.os.usertype.profile.PRIVATE";
/**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
index 7cc95c5..0fc1845 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/UninstallRepository.kt
@@ -36,6 +36,7 @@
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
+import android.os.Flags
import android.os.Process
import android.os.UserHandle
import android.os.UserManager
@@ -97,16 +98,17 @@
}
}
- if (getMaxTargetSdkVersionForUid(context, callingUid) >= Build.VERSION_CODES.P
- && !isPermissionGranted(
+ if (getMaxTargetSdkVersionForUid(context, callingUid) >= Build.VERSION_CODES.P &&
+ !isPermissionGranted(
context, Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid
- )
- && !isPermissionGranted(context, Manifest.permission.DELETE_PACKAGES, callingUid)
+ ) &&
+ !isPermissionGranted(context, Manifest.permission.DELETE_PACKAGES, callingUid)
) {
Log.e(
- LOG_TAG, "Uid " + callingUid + " does not have "
- + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
- + Manifest.permission.DELETE_PACKAGES
+ LOG_TAG,
+ "Uid " + callingUid + " does not have " +
+ Manifest.permission.REQUEST_DELETE_PACKAGES + " or " +
+ Manifest.permission.DELETE_PACKAGES
)
return UninstallAborted(UninstallAborted.ABORT_REASON_GENERIC_ERROR)
}
@@ -138,8 +140,9 @@
val profiles = userManager!!.userProfiles
if (!profiles.contains(uninstalledUser)) {
Log.e(
- LOG_TAG, "User " + Process.myUserHandle() + " can't request uninstall "
- + "for user " + uninstalledUser
+ LOG_TAG,
+ "User " + Process.myUserHandle() + " can't request uninstall " +
+ "for user " + uninstalledUser
)
return UninstallAborted(UninstallAborted.ABORT_REASON_USER_NOT_ALLOWED)
}
@@ -202,9 +205,13 @@
val isSingleUser = isSingleUser()
if (isUpdate) {
- messageBuilder.append(context.getString(
- if (isSingleUser) R.string.uninstall_update_text
- else R.string.uninstall_update_text_multiuser
+ messageBuilder.append(
+ context.getString(
+ if (isSingleUser) {
+ R.string.uninstall_update_text
+ } else {
+ R.string.uninstall_update_text_multiuser
+ }
)
)
} else if (uninstallFromAllUsers && !isSingleUser) {
@@ -214,42 +221,42 @@
val customUserManager = context.createContextAsUser(uninstalledUser!!, 0)
.getSystemService(UserManager::class.java)
val userName = customUserManager!!.userName
-
- val uninstalledUserType = getUninstalledUserType(myUserHandle, uninstalledUser!!)
- val messageString: String
- when (uninstalledUserType) {
- UserManager.USER_TYPE_PROFILE_MANAGED -> {
+ var messageString = context.getString(
+ R.string.uninstall_application_text_user,
+ userName
+ )
+ if (userManager!!.isSameProfileGroup(myUserHandle, uninstalledUser!!)) {
+ if (customUserManager!!.isManagedProfile()) {
messageString = context.getString(
- R.string.uninstall_application_text_current_user_work_profile, userName
+ R.string.uninstall_application_text_current_user_work_profile, userName
)
- }
-
- UserManager.USER_TYPE_PROFILE_CLONE -> {
+ } else if (customUserManager!!.isCloneProfile()){
isClonedApp = true
messageString = context.getString(
- R.string.uninstall_application_text_current_user_clone_profile
+ R.string.uninstall_application_text_current_user_clone_profile
)
- }
-
- else -> {
+ } else if (Flags.allowPrivateProfile() && customUserManager!!.isPrivateProfile()) {
+ // TODO(b/324244123): Get these Strings from a User Property API.
messageString = context.getString(
- R.string.uninstall_application_text_user, userName
+ R.string.uninstall_application_text_current_user_private_profile
)
}
-
}
messageBuilder.append(messageString)
} else if (isCloneProfile(uninstalledUser!!)) {
isClonedApp = true
- messageBuilder.append(context.getString(
+ messageBuilder.append(
+ context.getString(
R.string.uninstall_application_text_current_user_clone_profile
)
)
- } else if (myUserHandle == UserHandle.SYSTEM
- && hasClonedInstance(targetAppInfo!!.packageName)
+ } else if (myUserHandle == UserHandle.SYSTEM &&
+ hasClonedInstance(targetAppInfo!!.packageName)
) {
- messageBuilder.append(context.getString(
- R.string.uninstall_application_text_with_clone_instance, targetAppLabel
+ messageBuilder.append(
+ context.getString(
+ R.string.uninstall_application_text_with_clone_instance,
+ targetAppLabel
)
)
} else {
@@ -296,31 +303,6 @@
return userCount == 1 || (UserManager.isHeadlessSystemUserMode() && userCount == 2)
}
- /**
- * Returns the type of the user from where an app is being uninstalled. We are concerned with
- * only USER_TYPE_PROFILE_MANAGED and USER_TYPE_PROFILE_CLONE and whether the user and profile
- * belong to the same profile group.
- */
- private fun getUninstalledUserType(
- myUserHandle: UserHandle,
- uninstalledUserHandle: UserHandle
- ): String? {
- if (!userManager!!.isSameProfileGroup(myUserHandle, uninstalledUserHandle)) {
- return null
- }
- val customUserManager = context.createContextAsUser(uninstalledUserHandle, 0)
- .getSystemService(UserManager::class.java)
- val userTypes =
- arrayOf(UserManager.USER_TYPE_PROFILE_MANAGED, UserManager.USER_TYPE_PROFILE_CLONE)
-
- for (userType in userTypes) {
- if (customUserManager!!.isUserOfType(userType)) {
- return userType
- }
- }
- return null
- }
-
private fun hasClonedInstance(packageName: String): Boolean {
// Check if clone user is present on the device.
var cloneUser: UserHandle? = null
@@ -334,8 +316,8 @@
}
// Check if another instance of given package exists in clone user profile.
return try {
- cloneUser != null
- && packageManager.getPackageUidAsUser(
+ cloneUser != null &&
+ packageManager.getPackageUidAsUser(
packageName, PackageManager.PackageInfoFlags.of(0), cloneUser.identifier
) > 0
} catch (e: PackageManager.NameNotFoundException) {
@@ -382,7 +364,9 @@
val storageStatsManager = context.getSystemService(StorageStatsManager::class.java)
try {
val stats = storageStatsManager!!.queryStatsForPackage(
- packageManager.getApplicationInfo(pkg, 0).storageUuid, pkg, user
+ packageManager.getApplicationInfo(pkg, 0).storageUuid,
+ pkg,
+ user
)
return stats.getDataBytes()
} catch (e: Exception) {
@@ -423,17 +407,24 @@
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, uninstallId)
broadcastIntent.setPackage(context.packageName)
val pendingIntent = PendingIntent.getBroadcast(
- context, uninstallId, broadcastIntent,
+ context,
+ uninstallId,
+ broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
if (!startUninstall(
- targetPackageName!!, uninstalledUser!!, pendingIntent, uninstallFromAllUsers,
+ targetPackageName!!,
+ uninstalledUser!!,
+ pendingIntent,
+ uninstallFromAllUsers,
keepData
)
) {
handleUninstallResult(
PackageInstaller.STATUS_FAILURE,
- PackageManager.DELETE_FAILED_INTERNAL_ERROR, null, 0
+ PackageManager.DELETE_FAILED_INTERNAL_ERROR,
+ null,
+ 0
)
}
}
@@ -474,9 +465,14 @@
// Caller did not want the result back. So, we either show a Toast, or a Notification.
if (status == PackageInstaller.STATUS_SUCCESS) {
- val statusMessage = if (isClonedApp) context.getString(
- R.string.uninstall_done_clone_app, targetAppLabel
- ) else context.getString(R.string.uninstall_done_app, targetAppLabel)
+ val statusMessage = if (isClonedApp) {
+ context.getString(
+ R.string.uninstall_done_clone_app,
+ targetAppLabel
+ )
+ } else {
+ context.getString(R.string.uninstall_done_app, targetAppLabel)
+ }
uninstallResult.setValue(
UninstallSuccess(activityResultCode = legacyStatus, message = statusMessage)
)
@@ -499,27 +495,32 @@
findUserOfDeviceAdmin(myUserHandle, targetPackageName!!)
if (otherBlockingUserHandle == null) {
Log.d(
- LOG_TAG, "Uninstall failed because $targetPackageName"
- + " is a device admin"
+ LOG_TAG,
+ "Uninstall failed because $targetPackageName" +
+ " is a device admin"
)
addDeviceManagerButton(context, uninstallFailedNotification)
setBigText(
- uninstallFailedNotification, context.getString(
+ uninstallFailedNotification,
+ context.getString(
R.string.uninstall_failed_device_policy_manager
)
)
} else {
Log.d(
- LOG_TAG, "Uninstall failed because $targetPackageName"
- + " is a device admin of user $otherBlockingUserHandle"
+ LOG_TAG,
+ "Uninstall failed because $targetPackageName" +
+ " is a device admin of user $otherBlockingUserHandle"
)
val userName = context.createContextAsUser(otherBlockingUserHandle, 0)
.getSystemService(UserManager::class.java)!!.userName
setBigText(
- uninstallFailedNotification, String.format(
+ uninstallFailedNotification,
+ String.format(
context.getString(
R.string.uninstall_failed_device_policy_manager_of_user
- ), userName
+ ),
+ userName
)
)
}
@@ -528,7 +529,9 @@
PackageManager.DELETE_FAILED_OWNER_BLOCKED -> {
val otherBlockingUserHandle = findBlockingUser(targetPackageName!!)
val isProfileOfOrSame = isProfileOfOrSame(
- userManager!!, myUserHandle, otherBlockingUserHandle
+ userManager!!,
+ myUserHandle,
+ otherBlockingUserHandle
)
if (isProfileOfOrSame) {
addDeviceManagerButton(context, uninstallFailedNotification)
@@ -538,15 +541,19 @@
var bigText: String? = null
if (otherBlockingUserHandle == null) {
Log.d(
- LOG_TAG, "Uninstall failed for $targetPackageName " +
+ LOG_TAG,
+ "Uninstall failed for $targetPackageName " +
"with code $status no blocking user"
)
} else if (otherBlockingUserHandle === UserHandle.SYSTEM) {
bigText = context.getString(R.string.uninstall_blocked_device_owner)
} else {
bigText = context.getString(
- if (uninstallFromAllUsers) R.string.uninstall_all_blocked_profile_owner
- else R.string.uninstall_blocked_profile_owner
+ if (uninstallFromAllUsers) {
+ R.string.uninstall_all_blocked_profile_owner
+ } else {
+ R.string.uninstall_blocked_profile_owner
+ }
)
}
bigText?.let { setBigText(uninstallFailedNotification, it) }
@@ -554,8 +561,9 @@
else -> {
Log.d(
- LOG_TAG, "Uninstall blocked for $targetPackageName"
- + " with legacy code $legacyStatus"
+ LOG_TAG,
+ "Uninstall blocked for $targetPackageName" +
+ " with legacy code $legacyStatus"
)
}
}
@@ -639,7 +647,9 @@
Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
context.getString(R.string.manage_users),
PendingIntent.getActivity(
- context, 0, getUserSettingsIntent(),
+ context,
+ 0,
+ getUserSettingsIntent(),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
@@ -668,7 +678,9 @@
Icon.createWithResource(context, R.drawable.ic_lock),
context.getString(R.string.manage_device_administrators),
PendingIntent.getActivity(
- context, 0, getDeviceManagerIntent(),
+ context,
+ 0,
+ getDeviceManagerIntent(),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
@@ -706,7 +718,8 @@
context.createContextAsUser(targetUser, 0)
.packageManager.packageInstaller.uninstall(
VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
- flags, pendingIntent.intentSender
+ flags,
+ pendingIntent.intentSender
)
true
} catch (e: IllegalArgumentException) {
@@ -719,7 +732,8 @@
if (callback != null) {
callback!!.onUninstallComplete(
targetPackageName!!,
- PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user"
+ PackageManager.DELETE_FAILED_ABORTED,
+ "Cancelled by user"
)
}
}