diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e8a355f102c3c..5209c14447c73 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2399,6 +2399,14 @@ public abstract class PackageManager {
*/
public static final int DELETE_FAILED_APP_PINNED = -7;
+ /**
+ * Deletion failed return code: this is passed to the
+ * {@link IPackageDeleteObserver} if the system failed to delete the package
+ * for any child profile with {@link UserProperties#getDeleteAppWithParent()} as true.
+ * @hide
+ */
+ public static final int DELETE_FAILED_FOR_CHILD_PROFILE = -8;
+
/**
* Return code that is passed to the {@link IPackageMoveObserver} when the
* package has been successfully moved by the system.
diff --git a/core/java/android/content/pm/UserProperties.java b/core/java/android/content/pm/UserProperties.java
index 824d15c656b1a..77b1954c0f1ba 100644
--- a/core/java/android/content/pm/UserProperties.java
+++ b/core/java/android/content/pm/UserProperties.java
@@ -61,6 +61,7 @@ public final class UserProperties implements Parcelable {
"mediaSharedWithParent";
private static final String ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT =
"credentialShareableWithParent";
+ private static final String ATTR_DELETE_APP_WITH_PARENT = "deleteAppWithParent";
/** Index values of each property (to indicate whether they are present in this object). */
@IntDef(prefix = "INDEX_", value = {
@@ -73,7 +74,8 @@ public final class UserProperties implements Parcelable {
INDEX_CROSS_PROFILE_INTENT_FILTER_ACCESS_CONTROL,
INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY,
INDEX_MEDIA_SHARED_WITH_PARENT,
- INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT
+ INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT,
+ INDEX_DELETE_APP_WITH_PARENT,
})
@Retention(RetentionPolicy.SOURCE)
private @interface PropertyIndex {
@@ -88,6 +90,7 @@ public final class UserProperties implements Parcelable {
private static final int INDEX_CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY = 7;
private static final int INDEX_MEDIA_SHARED_WITH_PARENT = 8;
private static final int INDEX_CREDENTIAL_SHAREABLE_WITH_PARENT = 9;
+ private static final int INDEX_DELETE_APP_WITH_PARENT = 10;
/** A bit set, mapping each PropertyIndex to whether it is present (1) or absent (0). */
private long mPropertiesPresent = 0;
@@ -312,6 +315,7 @@ public final class UserProperties implements Parcelable {
setCrossProfileIntentFilterAccessControl(
orig.getCrossProfileIntentFilterAccessControl());
setCrossProfileIntentResolutionStrategy(orig.getCrossProfileIntentResolutionStrategy());
+ setDeleteAppWithParent(orig.getDeleteAppWithParent());
}
if (hasManagePermission) {
// Add items that require MANAGE_USERS or stronger.
@@ -417,6 +421,24 @@ public final class UserProperties implements Parcelable {
}
private boolean mStartWithParent;
+ /**
+ * Returns whether an app in the profile should be deleted when the same package in
+ * the parent user is being deleted.
+ * This only applies for users that have parents (i.e. for profiles).
+ * @hide
+ */
+ public boolean getDeleteAppWithParent() {
+ if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) return mDeleteAppWithParent;
+ if (mDefaultProperties != null) return mDefaultProperties.mDeleteAppWithParent;
+ throw new SecurityException("You don't have permission to query deleteAppWithParent");
+ }
+ /** @hide */
+ public void setDeleteAppWithParent(boolean val) {
+ this.mDeleteAppWithParent = val;
+ setPresent(INDEX_DELETE_APP_WITH_PARENT);
+ }
+ private boolean mDeleteAppWithParent;
+
/**
* Return whether, and how, select user restrictions or device policies should be inherited
* from other user.
@@ -609,6 +631,7 @@ public final class UserProperties implements Parcelable {
+ getCrossProfileIntentResolutionStrategy()
+ ", mMediaSharedWithParent=" + isMediaSharedWithParent()
+ ", mCredentialShareableWithParent=" + isCredentialShareableWithParent()
+ + ", mDeleteAppWithParent=" + getDeleteAppWithParent()
+ "}";
}
@@ -634,6 +657,7 @@ public final class UserProperties implements Parcelable {
pw.println(prefix + " mMediaSharedWithParent=" + isMediaSharedWithParent());
pw.println(prefix + " mCredentialShareableWithParent="
+ isCredentialShareableWithParent());
+ pw.println(prefix + " mDeleteAppWithParent=" + getDeleteAppWithParent());
}
/**
@@ -697,6 +721,9 @@ public final class UserProperties implements Parcelable {
case ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT:
setCredentialShareableWithParent(parser.getAttributeBoolean(i));
break;
+ case ATTR_DELETE_APP_WITH_PARENT:
+ setDeleteAppWithParent(parser.getAttributeBoolean(i));
+ break;
default:
Slog.w(LOG_TAG, "Skipping unknown property " + attributeName);
}
@@ -752,6 +779,10 @@ public final class UserProperties implements Parcelable {
serializer.attributeBoolean(null, ATTR_CREDENTIAL_SHAREABLE_WITH_PARENT,
mCredentialShareableWithParent);
}
+ if (isPresent(INDEX_DELETE_APP_WITH_PARENT)) {
+ serializer.attributeBoolean(null, ATTR_DELETE_APP_WITH_PARENT,
+ mDeleteAppWithParent);
+ }
}
// For use only with an object that has already had any permission-lacking fields stripped out.
@@ -768,6 +799,7 @@ public final class UserProperties implements Parcelable {
dest.writeInt(mCrossProfileIntentResolutionStrategy);
dest.writeBoolean(mMediaSharedWithParent);
dest.writeBoolean(mCredentialShareableWithParent);
+ dest.writeBoolean(mDeleteAppWithParent);
}
/**
@@ -788,6 +820,7 @@ public final class UserProperties implements Parcelable {
mCrossProfileIntentResolutionStrategy = source.readInt();
mMediaSharedWithParent = source.readBoolean();
mCredentialShareableWithParent = source.readBoolean();
+ mDeleteAppWithParent = source.readBoolean();
}
@Override
@@ -825,6 +858,7 @@ public final class UserProperties implements Parcelable {
CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_DEFAULT;
private boolean mMediaSharedWithParent = false;
private boolean mCredentialShareableWithParent = false;
+ private boolean mDeleteAppWithParent = false;
public Builder setShowInLauncher(@ShowInLauncher int showInLauncher) {
mShowInLauncher = showInLauncher;
@@ -886,6 +920,12 @@ public final class UserProperties implements Parcelable {
return this;
}
+ /** Sets the value for {@link #mDeleteAppWithParent}*/
+ public Builder setDeleteAppWithParent(boolean deleteAppWithParent) {
+ mDeleteAppWithParent = deleteAppWithParent;
+ return this;
+ }
+
/** Builds a UserProperties object with *all* values populated. */
public UserProperties build() {
return new UserProperties(
@@ -898,7 +938,8 @@ public final class UserProperties implements Parcelable {
mCrossProfileIntentFilterAccessControl,
mCrossProfileIntentResolutionStrategy,
mMediaSharedWithParent,
- mCredentialShareableWithParent);
+ mCredentialShareableWithParent,
+ mDeleteAppWithParent);
}
} // end Builder
@@ -912,8 +953,8 @@ public final class UserProperties implements Parcelable {
@CrossProfileIntentFilterAccessControlLevel int crossProfileIntentFilterAccessControl,
@CrossProfileIntentResolutionStrategy int crossProfileIntentResolutionStrategy,
boolean mediaSharedWithParent,
- boolean credentialShareableWithParent) {
-
+ boolean credentialShareableWithParent,
+ boolean deleteAppWithParent) {
mDefaultProperties = null;
setShowInLauncher(showInLauncher);
setStartWithParent(startWithParent);
@@ -925,5 +966,6 @@ public final class UserProperties implements Parcelable {
setCrossProfileIntentResolutionStrategy(crossProfileIntentResolutionStrategy);
setMediaSharedWithParent(mediaSharedWithParent);
setCredentialShareableWithParent(credentialShareableWithParent);
+ setDeleteAppWithParent(deleteAppWithParent);
}
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index a7e0af371d511..e0de2949ec150 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -43,6 +43,7 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -776,19 +777,23 @@ final class DeletePackageHelper {
userId, deleteFlags, false /*removedBySystem*/);
// Get a list of child user profiles and delete if package is
- // present in clone profile.
+ // present in that profile.
int[] childUserIds = mUserManagerInternal.getProfileIds(userId, true);
+ int returnCodeOfChild;
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;
+ if (childId == userId) continue;
+ UserProperties userProperties = mUserManagerInternal
+ .getUserProperties(childId);
+ if (userProperties != null && userProperties.getDeleteAppWithParent()) {
+ returnCodeOfChild = deletePackageX(internalPackageName, versionCode,
+ childId, deleteFlags, false /*removedBySystem*/);
+ if (returnCodeOfChild != PackageManager.DELETE_SUCCEEDED) {
+ Slog.w(TAG, "Package delete failed for user " + childId
+ + ", returnCode " + returnCodeOfChild);
+ returnCode = PackageManager.DELETE_FAILED_FOR_CHILD_PROFILE;
}
}
}
-
} else {
int[] blockUninstallUserIds = getBlockUninstallForUsers(innerSnapshot,
internalPackageName, users);
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index b8c57b86cd85d..77c32aefaee61 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -136,7 +136,7 @@ public final class UserTypeFactory {
.CROSS_PROFILE_INTENT_RESOLUTION_STRATEGY_NO_FILTERING)
.setMediaSharedWithParent(true)
.setCredentialShareableWithParent(true)
- );
+ .setDeleteAppWithParent(true));
}
/**
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index 908e717019fc1..26d681bb27ef3 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -38,6 +38,9 @@
crossProfileIntentResolutionStrategy='0'
mediaSharedWithParent='true'
credentialShareableWithParent='false'
+ showInSettings='23'
+ inheritDevicePolicy='450'
+ deleteAppWithParent='false'
/>
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
index ade1bd4145712..2675f05ed8fe7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserPropertiesTest.java
@@ -66,6 +66,7 @@ public class UserManagerServiceUserPropertiesTest {
.setCrossProfileIntentResolutionStrategy(0)
.setMediaSharedWithParent(false)
.setCredentialShareableWithParent(true)
+ .setDeleteAppWithParent(false)
.build();
final UserProperties actualProps = new UserProperties(defaultProps);
actualProps.setShowInLauncher(14);
@@ -76,6 +77,7 @@ public class UserManagerServiceUserPropertiesTest {
actualProps.setCrossProfileIntentResolutionStrategy(1);
actualProps.setMediaSharedWithParent(true);
actualProps.setCredentialShareableWithParent(false);
+ actualProps.setDeleteAppWithParent(true);
// Write the properties to xml.
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -116,12 +118,14 @@ public class UserManagerServiceUserPropertiesTest {
.setShowInSettings(3452)
.setInheritDevicePolicy(1732)
.setMediaSharedWithParent(true)
+ .setDeleteAppWithParent(true)
.build();
final UserProperties orig = new UserProperties(defaultProps);
orig.setShowInLauncher(2841);
orig.setStartWithParent(false);
orig.setShowInSettings(1437);
orig.setInheritDevicePolicy(9456);
+ orig.setDeleteAppWithParent(false);
// Test every permission level. (Currently, it's linear so it's easy.)
for (int permLevel = 0; permLevel < 4; permLevel++) {
@@ -163,6 +167,8 @@ public class UserManagerServiceUserPropertiesTest {
copy::getCrossProfileIntentFilterAccessControl, exposeAll);
assertEqualGetterOrThrows(orig::getCrossProfileIntentResolutionStrategy,
copy::getCrossProfileIntentResolutionStrategy, exposeAll);
+ assertEqualGetterOrThrows(orig::getDeleteAppWithParent,
+ copy::getDeleteAppWithParent, exposeAll);
// Items requiring hasManagePermission - put them here using hasManagePermission.
assertEqualGetterOrThrows(orig::getShowInSettings, copy::getShowInSettings,
@@ -227,5 +233,6 @@ public class UserManagerServiceUserPropertiesTest {
.isEqualTo(actual.isMediaSharedWithParent());
assertThat(expected.isCredentialShareableWithParent())
.isEqualTo(actual.isCredentialShareableWithParent());
+ assertThat(expected.getDeleteAppWithParent()).isEqualTo(actual.getDeleteAppWithParent());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 702059db83fb6..ff9a79e61fe0e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -88,7 +88,11 @@ public class UserManagerServiceUserTypeTest {
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(1)
.setMediaSharedWithParent(true)
- .setCredentialShareableWithParent(false);
+ .setCredentialShareableWithParent(false)
+ .setShowInSettings(900)
+ .setInheritDevicePolicy(340)
+ .setDeleteAppWithParent(true);
+
final UserTypeDetails type = new UserTypeDetails.Builder()
.setName("a.name")
.setEnabled(1)
@@ -152,6 +156,10 @@ public class UserManagerServiceUserTypeTest {
.getCrossProfileIntentResolutionStrategy());
assertTrue(type.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(type.getDefaultUserPropertiesReference().isCredentialShareableWithParent());
+ assertEquals(900, type.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(340, type.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertTrue(type.getDefaultUserPropertiesReference().getDeleteAppWithParent());
assertEquals(23, type.getBadgeLabel(0));
assertEquals(24, type.getBadgeLabel(1));
@@ -287,7 +295,11 @@ public class UserManagerServiceUserTypeTest {
.setCrossProfileIntentFilterAccessControl(10)
.setCrossProfileIntentResolutionStrategy(1)
.setMediaSharedWithParent(false)
- .setCredentialShareableWithParent(true);
+ .setCredentialShareableWithParent(true)
+ .setShowInSettings(20)
+ .setInheritDevicePolicy(21)
+ .setDeleteAppWithParent(true);
+
final ArrayMap builders = new ArrayMap<>();
builders.put(userTypeAosp1, new UserTypeDetails.Builder()
.setName(userTypeAosp1)
@@ -323,6 +335,10 @@ public class UserManagerServiceUserTypeTest {
assertFalse(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertTrue(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
+ assertEquals(20, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(21, aospType.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertTrue(aospType.getDefaultUserPropertiesReference().getDeleteAppWithParent());
// userTypeAosp2 should be modified.
aospType = builders.get(userTypeAosp2).createUserTypeDetails();
@@ -362,6 +378,11 @@ public class UserManagerServiceUserTypeTest {
assertTrue(aospType.getDefaultUserPropertiesReference().isMediaSharedWithParent());
assertFalse(aospType.getDefaultUserPropertiesReference()
.isCredentialShareableWithParent());
+ assertEquals(23, aospType.getDefaultUserPropertiesReference().getShowInSettings());
+ assertEquals(450, aospType.getDefaultUserPropertiesReference()
+ .getInheritDevicePolicy());
+ assertFalse(aospType.getDefaultUserPropertiesReference()
+ .getDeleteAppWithParent());
// userTypeOem1 should be created.
UserTypeDetails.Builder customType = builders.get(userTypeOem1);
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index ac5bcfffe7326..9b9cb4d8cfa09 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -192,6 +192,7 @@ public final class UserManagerTest {
.isEqualTo(cloneUserProperties.isMediaSharedWithParent());
assertThat(typeProps.isCredentialShareableWithParent())
.isEqualTo(cloneUserProperties.isCredentialShareableWithParent());
+ assertThrows(SecurityException.class, cloneUserProperties::getDeleteAppWithParent);
// Verify clone user parent
assertThat(mUserManager.getProfileParent(mainUserId)).isNull();
@@ -844,8 +845,10 @@ public final class UserManagerTest {
assertThrows(SecurityException.class, userProps::getInheritDevicePolicy);
assertThat(userProps.isMediaSharedWithParent()).isFalse();
assertThat(userProps.isCredentialShareableWithParent()).isTrue();
+ assertThrows(SecurityException.class, userProps::getDeleteAppWithParent);
}
+
// Make sure only max managed profiles can be created
@MediumTest
@Test