From 253984ed8c8f39fa0aa9c5f4addf2f44f334c749 Mon Sep 17 00:00:00 2001 From: Vladislav Kuzkokov Date: Mon, 24 Oct 2016 09:27:16 +0200 Subject: [PATCH] Store "block uninstall" flag separately from the rest of package state. This allows to set "block uninstall" prior to installation and avoid the inevitable race that happens when Device Policy app tries to force install and then immediately block uninstall. BUG=31043188 Test: Block com.chrome.beta in TestDPC. install, fail to uninstall through adb, unblock, uninstall Change-Id: I5ffa2abcb003982eccfb77585c43b59532dd501d (cherry picked from commit 1fff9dcb9d0a4d7224ff8aa0f39e82df0b30152c) --- .../android/content/pm/PackageUserState.java | 5 -- .../server/pm/PackageManagerService.java | 20 +---- .../android/server/pm/PackageSettingBase.java | 13 +-- .../java/com/android/server/pm/Settings.java | 86 +++++++++++++++++-- .../pm/PackageManagerSettingsTests.java | 1 - .../server/pm/PackageUserStateTest.java | 4 - 6 files changed, 84 insertions(+), 45 deletions(-) diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index ee56a18e71a5f..4e53914a3c681 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -44,7 +44,6 @@ public class PackageUserState { public boolean notLaunched; public boolean hidden; // Is the app restricted by owner / admin public boolean suspended; - public boolean blockUninstall; public boolean instantApp; public int enabled; public String lastDisableAppCaller; @@ -75,7 +74,6 @@ public class PackageUserState { notLaunched = o.notLaunched; hidden = o.hidden; suspended = o.suspended; - blockUninstall = o.blockUninstall; instantApp = o.instantApp; enabled = o.enabled; lastDisableAppCaller = o.lastDisableAppCaller; @@ -193,9 +191,6 @@ public class PackageUserState { if (suspended != oldState.suspended) { return false; } - if (blockUninstall != oldState.blockUninstall) { - return false; - } if (instantApp != oldState.instantApp) { return false; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index cfcd0a51a3283..121ae07edd821 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18514,11 +18514,6 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_PACKAGES, null); synchronized (mPackages) { - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null) { - Log.i(TAG, "Package doesn't exist in set block uninstall " + packageName); - return false; - } // Cannot block uninstall of static shared libs as they are // considered a part of the using app (emulating static linking). // Also static libs are installed always on internal storage. @@ -18528,12 +18523,7 @@ public class PackageManagerService extends IPackageManager.Stub + " providing static shared library: " + pkg.staticSharedLibName); return false; } - if (!ps.getInstalled(userId)) { - // Can't block uninstall for an app that is not installed or enabled. - Log.i(TAG, "Package not installed in set block uninstall " + packageName); - return false; - } - ps.setBlockUninstall(blockUninstall, userId); + mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall); mSettings.writePackageRestrictionsLPr(userId); } return true; @@ -18542,12 +18532,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean getBlockUninstallForUser(String packageName, int userId) { synchronized (mPackages) { - PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null) { - Log.i(TAG, "Package doesn't exist in get block uninstall " + packageName); - return false; - } - return ps.getBlockUninstall(userId); + return mSettings.getBlockUninstallLPr(userId, packageName); } } @@ -18754,7 +18739,6 @@ public class PackageManagerService extends IPackageManager.Stub null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - false /*blockUninstall*/, ps.readUserState(nextUserId).domainVerificationStatus, 0, PackageManager.INSTALL_REASON_UNKNOWN); } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index dfed72fa9b61f..14f65eb485f4b 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -402,14 +402,6 @@ abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).suspended = suspended; } - boolean getBlockUninstall(int userId) { - return readUserState(userId).blockUninstall; - } - - void setBlockUninstall(boolean blockUninstall, int userId) { - modifyUserState(userId).blockUninstall = blockUninstall; - } - boolean getInstantApp(int userId) { return readUserState(userId).instantApp; } @@ -421,8 +413,8 @@ abstract class PackageSettingBase extends SettingBase { void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped, boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp, String lastDisableAppCaller, ArraySet enabledComponents, - ArraySet disabledComponents, boolean blockUninstall, - int domainVerifState, int linkGeneration, int installReason) { + ArraySet disabledComponents, int domainVerifState, + int linkGeneration, int installReason) { PackageUserState state = modifyUserState(userId); state.ceDataInode = ceDataInode; state.enabled = enabled; @@ -434,7 +426,6 @@ abstract class PackageSettingBase extends SettingBase { state.lastDisableAppCaller = lastDisableAppCaller; state.enabledComponents = enabledComponents; state.disabledComponents = disabledComponents; - state.blockUninstall = blockUninstall; state.domainVerificationStatus = domainVerifState; state.appLinkGeneration = linkGeneration; state.installReason = installReason; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index cea031eec9f50..44bcff25b53c3 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -186,6 +186,8 @@ final class Settings { private static final String TAG_PERMISSIONS = "perms"; private static final String TAG_CHILD_PACKAGE = "child-package"; private static final String TAG_USES_STATIC_LIB = "uses-static-lib"; + private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages"; + private static final String TAG_BLOCK_UNINSTALL = "block-uninstall"; private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES = "persistent-preferred-activities"; @@ -215,6 +217,8 @@ final class Settings { // New name for the above attribute. private static final String ATTR_HIDDEN = "hidden"; private static final String ATTR_SUSPENDED = "suspended"; + // Legacy, uninstall blocks are stored separately. + @Deprecated private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall"; private static final String ATTR_ENABLED = "enabled"; private static final String ATTR_ENABLED_CALLER = "enabledCaller"; @@ -271,6 +275,9 @@ final class Settings { private final ArrayMap mDisabledSysPackages = new ArrayMap(); + /** List of packages that are blocked for uninstall for specific users */ + private final SparseArray> mBlockUninstallPackages = new SparseArray<>(); + // Set of restored intent-filter verification states private final ArrayMap mRestoredIntentFilterVerifications = new ArrayMap(); @@ -756,7 +763,6 @@ final class Settings { null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - false /*blockUninstall*/, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0, PackageManager.INSTALL_REASON_UNKNOWN); } @@ -1614,6 +1620,34 @@ final class Settings { } } + void readBlockUninstallPackagesLPw(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + ArraySet packages = new ArraySet<>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String tagName = parser.getName(); + if (tagName.equals(TAG_BLOCK_UNINSTALL)) { + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + packages.add(packageName); + } else { + String msg = "Unknown element under " + TAG_BLOCK_UNINSTALL_PACKAGES + ": " + + parser.getName(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + if (packages.isEmpty()) { + mBlockUninstallPackages.remove(userId); + } else { + mBlockUninstallPackages.put(userId, packages); + } + } + void readPackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Reading package restrictions for user=" + userId); @@ -1662,7 +1696,6 @@ final class Settings { null /*lastDisableAppCaller*/, null /*enabledComponents*/, null /*disabledComponents*/, - false /*blockUninstall*/, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0, PackageManager.INSTALL_REASON_UNKNOWN); } @@ -1768,9 +1801,12 @@ final class Settings { } } + if (blockUninstall) { + setBlockUninstallLPw(userId, name, true); + } ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, hidden, suspended, instantApp, enabledCaller, enabledComponents, - disabledComponents, blockUninstall, verifState, linkGeneration, + disabledComponents, verifState, linkGeneration, installReason); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLPw(parser, userId); @@ -1780,6 +1816,8 @@ final class Settings { readCrossProfileIntentFiltersLPw(parser, userId); } else if (tagName.equals(TAG_DEFAULT_APPS)) { readDefaultAppsLPw(parser, userId); + } else if (tagName.equals(TAG_BLOCK_UNINSTALL_PACKAGES)) { + readBlockUninstallPackagesLPw(parser, userId); } else { Slog.w(PackageManagerService.TAG, "Unknown element under : " + parser.getName()); @@ -1806,6 +1844,30 @@ final class Settings { } } + void setBlockUninstallLPw(int userId, String packageName, boolean blockUninstall) { + ArraySet packages = mBlockUninstallPackages.get(userId); + if (blockUninstall) { + if (packages == null) { + packages = new ArraySet(); + mBlockUninstallPackages.put(userId, packages); + } + packages.add(packageName); + } else if (packages != null) { + packages.remove(packageName); + if (packages.isEmpty()) { + mBlockUninstallPackages.remove(userId); + } + } + } + + boolean getBlockUninstallLPr(int userId, String packageName) { + ArraySet packages = mBlockUninstallPackages.get(userId); + if (packages == null) { + return false; + } + return packages.contains(packageName); + } + private ArraySet readComponentsLPr(XmlPullParser parser) throws IOException, XmlPullParserException { ArraySet components = null; @@ -1976,6 +2038,20 @@ final class Settings { serializer.endTag(null, TAG_DEFAULT_APPS); } + void writeBlockUninstallPackagesLPr(XmlSerializer serializer, int userId) + throws IOException { + ArraySet packages = mBlockUninstallPackages.get(userId); + if (packages != null) { + serializer.startTag(null, TAG_BLOCK_UNINSTALL_PACKAGES); + for (int i = 0; i < packages.size(); i++) { + serializer.startTag(null, TAG_BLOCK_UNINSTALL); + serializer.attribute(null, ATTR_PACKAGE_NAME, packages.valueAt(i)); + serializer.endTag(null, TAG_BLOCK_UNINSTALL); + } + serializer.endTag(null, TAG_BLOCK_UNINSTALL_PACKAGES); + } + } + void writePackageRestrictionsLPr(int userId) { if (DEBUG_MU) { Log.i(TAG, "Writing package restrictions for user=" + userId); @@ -2038,9 +2114,6 @@ final class Settings { if (ustate.suspended) { serializer.attribute(null, ATTR_SUSPENDED, "true"); } - if (ustate.blockUninstall) { - serializer.attribute(null, ATTR_BLOCK_UNINSTALL, "true"); - } if (ustate.instantApp) { serializer.attribute(null, ATTR_INSTANT_APP, "true"); } @@ -2091,6 +2164,7 @@ final class Settings { writePersistentPreferredActivitiesLPr(serializer, userId); writeCrossProfileIntentFiltersLPr(serializer, userId); writeDefaultAppsLPr(serializer, userId); + writeBlockUninstallPackagesLPr(serializer, userId); serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 325d99a90e67c..4a6595f599835 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -547,7 +547,6 @@ public class PackageManagerSettingsTests { private void verifyUserState(PackageUserState userState, PackageUserState oldUserState, boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) { - assertThat(userState.blockUninstall, is(false)); assertThat(userState.enabled, is(0)); assertThat(userState.hidden, is(false)); assertThat(userState.installed, is(installed)); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java index bf05f21f32d54..50be8dbd16a89 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java @@ -48,10 +48,6 @@ public class PackageUserStateTest { oldUserState.appLinkGeneration = 6; assertThat(testUserState.equals(oldUserState), is(false)); - oldUserState = new PackageUserState(); - oldUserState.blockUninstall = true; - assertThat(testUserState.equals(oldUserState), is(false)); - oldUserState = new PackageUserState(); oldUserState.ceDataInode = 4000L; assertThat(testUserState.equals(oldUserState), is(false));