diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index cb2baa974b0c2..ae6f71c954ca7 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -143,6 +143,8 @@
Uninstalled %1$s
+ Deleted %1$s clone
+
Uninstall unsuccessful.
Uninstalling %1$s unsuccessful.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
index b9552fc4e1433..e089aef43197c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -49,6 +49,7 @@ public class UninstallFinish extends BroadcastReceiver {
static final String EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID";
static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+ static final String EXTRA_IS_CLONE_APP = "com.android.packageinstaller.extra.IS_CLONE_APP";
@Override
public void onReceive(Context context, Intent intent) {
@@ -84,7 +85,10 @@ public class UninstallFinish extends BroadcastReceiver {
case PackageInstaller.STATUS_SUCCESS:
notificationManager.cancel(uninstallId);
- Toast.makeText(context, context.getString(R.string.uninstall_done_app, appLabel),
+ boolean isCloneApp = intent.getBooleanExtra(EXTRA_IS_CLONE_APP, false);
+ Toast.makeText(context, isCloneApp
+ ? context.getString(R.string.uninstall_done_clone_app, appLabel)
+ : context.getString(R.string.uninstall_done_app, appLabel),
Toast.LENGTH_LONG).show();
return;
case PackageInstaller.STATUS_FAILURE_BLOCKED: {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index 04496b91c9781..7250bdd4bec61 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -67,6 +67,7 @@ public class UninstallerActivity extends Activity {
private static final String TAG = "UninstallerActivity";
private static final String UNINSTALLING_CHANNEL = "uninstalling";
+ private boolean mIsClonedApp;
public static class DialogInfo {
public ApplicationInfo appInfo;
@@ -277,6 +278,14 @@ public class UninstallerActivity extends Activity {
fragment.show(ft, "dialog");
}
+ /**
+ * Starts uninstall of app.
+ */
+ public void startUninstallProgress(boolean keepData, boolean isClonedApp) {
+ mIsClonedApp = isClonedApp;
+ startUninstallProgress(keepData);
+ }
+
public void startUninstallProgress(boolean keepData) {
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
@@ -329,6 +338,7 @@ public class UninstallerActivity extends Activity {
broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
+ broadcastIntent.putExtra(UninstallFinish.EXTRA_IS_CLONE_APP, mIsClonedApp);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
@@ -343,7 +353,10 @@ public class UninstallerActivity extends Activity {
Notification uninstallingNotification =
(new Notification.Builder(this, UNINSTALLING_CHANNEL))
.setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
- .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
+ .setContentTitle(mIsClonedApp
+ ? getString(R.string.uninstalling_cloned_app, label)
+ : getString(R.string.uninstalling_app, label))
+ .setOngoing(true)
.build();
notificationManager.notify(uninstallId, uninstallingNotification);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 1bbdad5ddfc8b..4a93bf80ae390 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -54,6 +54,7 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
private static final String LOG_TAG = UninstallAlertDialogFragment.class.getSimpleName();
private @Nullable CheckBox mKeepData;
+ private boolean mIsClonedApp;
/**
* Get number of bytes of the app data of the package.
@@ -125,7 +126,6 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
messageBuilder.append(" ").append(appLabel).append(".\n\n");
}
}
- boolean isClonedApp = false;
final boolean isUpdate =
((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
@@ -154,7 +154,7 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
userName));
} else if (customUserManager.isUserOfType(USER_TYPE_PROFILE_CLONE)
&& customUserManager.isSameProfileGroup(dialogInfo.user, myUserHandle)) {
- isClonedApp = true;
+ mIsClonedApp = true;
messageBuilder.append(getString(
R.string.uninstall_application_text_current_user_clone_profile));
} else {
@@ -162,7 +162,7 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
getString(R.string.uninstall_application_text_user, userName));
}
} else if (isCloneProfile(myUserHandle)) {
- isClonedApp = true;
+ mIsClonedApp = true;
messageBuilder.append(getString(
R.string.uninstall_application_text_current_user_clone_profile));
} else {
@@ -177,7 +177,7 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
}
}
- if (isClonedApp) {
+ if (mIsClonedApp) {
dialogBuilder.setTitle(getString(R.string.cloned_app_label, appLabel));
} else {
dialogBuilder.setTitle(appLabel);
@@ -236,7 +236,7 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
UserManager userManager = getContext().getSystemService(UserManager.class);
List profiles = userManager.getUserProfiles();
for (UserHandle userHandle : profiles) {
- if (!Process.myUserHandle().equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
+ if (!userHandle.equals(UserHandle.SYSTEM) && isCloneProfile(userHandle)) {
cloneUser = userHandle;
break;
}
@@ -260,7 +260,7 @@ public class UninstallAlertDialogFragment extends DialogFragment implements
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
((UninstallerActivity) getActivity()).startUninstallProgress(
- mKeepData != null && mKeepData.isChecked());
+ mKeepData != null && mKeepData.isChecked(), mIsClonedApp);
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 3df46a245eb16..a7e0af371d511 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -774,6 +774,21 @@ final class DeletePackageHelper {
if (!deleteAllUsers) {
returnCode = deletePackageX(internalPackageName, versionCode,
userId, deleteFlags, false /*removedBySystem*/);
+
+ // Get a list of child user profiles and delete if package is
+ // present in clone profile.
+ int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true);
+ for (int childId : childUserIds) {
+ if (childId != userId) {
+ UserInfo userInfo = mUserManagerInternal.getUserInfo(childId);
+ if (userInfo != null && userInfo.isCloneProfile()) {
+ returnCode = deletePackageX(internalPackageName, versionCode,
+ childId, deleteFlags, false /*removedBySystem*/);
+ break;
+ }
+ }
+ }
+
} else {
int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
internalPackageName, users);