From b8dbdccc099650af994b29938b070c64c29f518b Mon Sep 17 00:00:00 2001 From: Ankita Vyas Date: Mon, 16 Jan 2023 14:19:16 +0000 Subject: [PATCH] Add new UserProperty deleteAppWithParent. Indicates whether an app in any profile should be deleted alongwith deletion of same app in parent user. Bug: 265622990 Test: atest android.multiuser.cts.UserManagerTest Test: atest com.android.server.pm.UserManagerTest Test: atest UserManagerServiceUserTypeTest Test: atest UserManagerServiceUserPropertiesTest Change-Id: I827f150f446efe4d9f83be784132fe25acfaff0f --- .../android/content/pm/PackageManager.java | 8 +++ .../android/content/pm/UserProperties.java | 50 +++++++++++++++++-- .../server/pm/DeletePackageHelper.java | 21 +++++--- .../android/server/pm/UserTypeFactory.java | 2 +- .../res/xml/usertypes_test_profile.xml | 3 ++ .../UserManagerServiceUserPropertiesTest.java | 7 +++ .../pm/UserManagerServiceUserTypeTest.java | 25 +++++++++- .../android/server/pm/UserManagerTest.java | 3 ++ 8 files changed, 104 insertions(+), 15 deletions(-) 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