Merge "Handle compressed APK enable/disable" into oc-mr1-dev
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4ff4885..529fcc4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -85,6 +85,8 @@
import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
+import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
@@ -19757,6 +19759,10 @@
// writer
synchronized (mPackages) {
+ // NOTE: The system package always needs to be enabled; even if it's for
+ // a compressed stub. If we don't, installing the system package fails
+ // during scan [scanning checks the disabled packages]. We will reverse
+ // this later, after we've "installed" the stub.
// Reinstate the old system package
enableSystemPackageLPw(disabledPs.pkg);
// Remove any native libraries from the upgraded package.
@@ -19765,23 +19771,38 @@
// Install the system package
if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
- int parseFlags = mDefParseFlags
- | PackageParser.PARSE_MUST_BE_APK
- | PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR;
- if (locationIsPrivileged(disabledPs.codePath)) {
- parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
- }
-
- final PackageParser.Package newPkg;
try {
- newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, 0 /* scanFlags */,
- 0 /* currentTime */, null);
+ installPackageFromSystemLIF(disabledPs.codePath, false /*isPrivileged*/, allUserHandles,
+ outInfo.origUsers, deletedPs.getPermissionsState(), writeSettings);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + deletedPkg.packageName + ": "
+ e.getMessage());
return false;
+ } finally {
+ if (disabledPs.pkg.isStub) {
+ mSettings.disableSystemPackageLPw(disabledPs.name, true /*replaced*/);
+ }
}
+ return true;
+ }
+
+ /**
+ * Installs a package that's already on the system partition.
+ */
+ private PackageParser.Package installPackageFromSystemLIF(@NonNull File codePath,
+ boolean isPrivileged, @Nullable int[] allUserHandles, @Nullable int[] origUserHandles,
+ @Nullable PermissionsState origPermissionState, boolean writeSettings)
+ throws PackageManagerException {
+ int parseFlags = mDefParseFlags
+ | PackageParser.PARSE_MUST_BE_APK
+ | PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR;
+ if (isPrivileged || locationIsPrivileged(codePath)) {
+ parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
+ }
+
+ final PackageParser.Package newPkg =
+ scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/, 0 /*currentTime*/, null);
try {
// update shared libraries for the newly re-installed system package
@@ -19799,17 +19820,21 @@
// Propagate the permissions state as we do not want to drop on the floor
// runtime permissions. The update permissions method below will take
// care of removing obsolete permissions and grant install permissions.
- ps.getPermissionsState().copyFrom(deletedPs.getPermissionsState());
+ if (origPermissionState != null) {
+ ps.getPermissionsState().copyFrom(origPermissionState);
+ }
updatePermissionsLPw(newPkg.packageName, newPkg,
UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+ final boolean applyUserRestrictions
+ = (allUserHandles != null) && (origUserHandles != null);
if (applyUserRestrictions) {
boolean installedStateChanged = false;
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across reinstall");
}
for (int userId : allUserHandles) {
- final boolean installed = ArrayUtils.contains(outInfo.origUsers, userId);
+ final boolean installed = ArrayUtils.contains(origUserHandles, userId);
if (DEBUG_REMOVE) {
Slog.d(TAG, " user " + userId + " => " + installed);
}
@@ -19832,7 +19857,7 @@
mSettings.writeLPr();
}
}
- return true;
+ return newPkg;
}
private boolean deleteInstalledPackageLIF(PackageSetting ps,
@@ -21765,77 +21790,183 @@
}
}
- synchronized (mPackages) {
- if (callingUid == Process.SHELL_UID
- && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
- // Shell can only change whole packages between ENABLED and DISABLED_USER states
- // unless it is a test package.
- int oldState = pkgSetting.getEnabled(userId);
- if (className == null
- &&
- (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
- || oldState == COMPONENT_ENABLED_STATE_DEFAULT
- || oldState == COMPONENT_ENABLED_STATE_ENABLED)
- &&
- (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
- || newState == COMPONENT_ENABLED_STATE_DEFAULT
- || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
- // ok
- } else {
- throw new SecurityException(
- "Shell cannot change component state for " + packageName + "/"
- + className + " to " + newState);
- }
- }
- if (className == null) {
- // We're dealing with an application/package level state change
- if (pkgSetting.getEnabled(userId) == newState) {
- // Nothing to do
- return;
- }
- if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
- || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
- // Don't care about who enables an app.
- callingPackage = null;
- }
- pkgSetting.setEnabled(newState, userId, callingPackage);
- // pkgSetting.pkg.mSetEnabled = newState;
+ if (callingUid == Process.SHELL_UID
+ && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
+ // Shell can only change whole packages between ENABLED and DISABLED_USER states
+ // unless it is a test package.
+ int oldState = pkgSetting.getEnabled(userId);
+ if (className == null
+ &&
+ (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || oldState == COMPONENT_ENABLED_STATE_DEFAULT
+ || oldState == COMPONENT_ENABLED_STATE_ENABLED)
+ &&
+ (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
+ || newState == COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
+ // ok
} else {
- // We're dealing with a component level state change
- // First, verify that this is a valid class name.
- PackageParser.Package pkg = pkgSetting.pkg;
- if (pkg == null || !pkg.hasComponentClassName(className)) {
- if (pkg != null &&
- pkg.applicationInfo.targetSdkVersion >=
- Build.VERSION_CODES.JELLY_BEAN) {
- throw new IllegalArgumentException("Component class " + className
- + " does not exist in " + packageName);
- } else {
- Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
- + className + " does not exist in " + packageName);
- }
- }
- switch (newState) {
- case COMPONENT_ENABLED_STATE_ENABLED:
- if (!pkgSetting.enableComponentLPw(className, userId)) {
- return;
- }
- break;
- case COMPONENT_ENABLED_STATE_DISABLED:
- if (!pkgSetting.disableComponentLPw(className, userId)) {
- return;
- }
- break;
- case COMPONENT_ENABLED_STATE_DEFAULT:
- if (!pkgSetting.restoreComponentLPw(className, userId)) {
- return;
- }
- break;
- default:
- Slog.e(TAG, "Invalid new component state: " + newState);
+ throw new SecurityException(
+ "Shell cannot change component state for " + packageName + "/"
+ + className + " to " + newState);
+ }
+ }
+ if (className == null) {
+ // We're dealing with an application/package level state change
+ if (pkgSetting.getEnabled(userId) == newState) {
+ // Nothing to do
+ return;
+ }
+ // If we're enabling a system stub, there's a little more work to do.
+ // Prior to enabling the package, we need to decompress the APK(s) to the
+ // data partition and then replace the version on the system partition.
+ final PackageParser.Package deletedPkg = pkgSetting.pkg;
+ final boolean isSystemStub = deletedPkg.isStub
+ && deletedPkg.isSystemApp();
+ if (isSystemStub
+ && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
+ final File codePath = decompressPackage(deletedPkg);
+ if (codePath == null) {
+ Slog.e(TAG, "couldn't decompress pkg: " + pkgSetting.name);
return;
}
+ // TODO remove direct parsing of the package object during internal cleanup
+ // of scan package
+ // We need to call parse directly here for no other reason than we need
+ // the new package in order to disable the old one [we use the information
+ // for some internal optimization to optionally create a new package setting
+ // object on replace]. However, we can't get the package from the scan
+ // because the scan modifies live structures and we need to remove the
+ // old [system] package from the system before a scan can be attempted.
+ // Once scan is indempotent we can remove this parse and use the package
+ // object we scanned, prior to adding it to package settings.
+ final PackageParser pp = new PackageParser();
+ pp.setSeparateProcesses(mSeparateProcesses);
+ pp.setDisplayMetrics(mMetrics);
+ pp.setCallback(mPackageParserCallback);
+ final PackageParser.Package tmpPkg;
+ try {
+ final int parseFlags = mDefParseFlags
+ | PackageParser.PARSE_MUST_BE_APK
+ | PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR;
+ tmpPkg = pp.parsePackage(codePath, parseFlags);
+ } catch (PackageParserException e) {
+ Slog.w(TAG, "Failed to parse compressed system package:" + pkgSetting.name, e);
+ return;
+ }
+ synchronized (mInstallLock) {
+ // Disable the stub and remove any package entries
+ removePackageLI(deletedPkg, true);
+ synchronized (mPackages) {
+ disableSystemPackageLPw(deletedPkg, tmpPkg);
+ }
+ final PackageParser.Package newPkg;
+ try (PackageFreezer freezer =
+ freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
+ final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ | PackageParser.PARSE_ENFORCE_CODE;
+ newPkg = scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/,
+ 0 /*currentTime*/, null /*user*/);
+ prepareAppDataAfterInstallLIF(newPkg);
+ synchronized (mPackages) {
+ try {
+ updateSharedLibrariesLPr(newPkg, null);
+ } catch (PackageManagerException e) {
+ Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+ }
+ updatePermissionsLPw(newPkg.packageName, newPkg,
+ UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG);
+ mSettings.writeLPr();
+ }
+ } catch (PackageManagerException e) {
+ // Whoops! Something went wrong; try to roll back to the stub
+ Slog.w(TAG, "Failed to install compressed system package:"
+ + pkgSetting.name, e);
+ // Remove the failed install
+ removeCodePathLI(codePath);
+
+ // Install the system package
+ try (PackageFreezer freezer =
+ freezePackage(deletedPkg.packageName, "setEnabledSetting")) {
+ synchronized (mPackages) {
+ // NOTE: The system package always needs to be enabled; even
+ // if it's for a compressed stub. If we don't, installing the
+ // system package fails during scan [scanning checks the disabled
+ // packages]. We will reverse this later, after we've "installed"
+ // the stub.
+ // This leaves us in a fragile state; the stub should never be
+ // enabled, so, cross your fingers and hope nothing goes wrong
+ // until we can disable the package later.
+ enableSystemPackageLPw(deletedPkg);
+ }
+ installPackageFromSystemLIF(new File(deletedPkg.codePath),
+ false /*isPrivileged*/, null /*allUserHandles*/,
+ null /*origUserHandles*/, null /*origPermissionsState*/,
+ true /*writeSettings*/);
+ } catch (PackageManagerException pme) {
+ Slog.w(TAG, "Failed to restore system package:"
+ + deletedPkg.packageName, pme);
+ } finally {
+ synchronized (mPackages) {
+ mSettings.disableSystemPackageLPw(
+ deletedPkg.packageName, true /*replaced*/);
+ mSettings.writeLPr();
+ }
+ }
+ return;
+ }
+ clearAppDataLIF(newPkg, UserHandle.USER_ALL, FLAG_STORAGE_DE
+ | FLAG_STORAGE_CE | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
+ clearAppProfilesLIF(newPkg, UserHandle.USER_ALL);
+ mDexManager.notifyPackageUpdated(newPkg.packageName,
+ newPkg.baseCodePath, newPkg.splitCodePaths);
+ }
}
+ if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+ || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+ // Don't care about who enables an app.
+ callingPackage = null;
+ }
+ pkgSetting.setEnabled(newState, userId, callingPackage);
+ } else {
+ // We're dealing with a component level state change
+ // First, verify that this is a valid class name.
+ PackageParser.Package pkg = pkgSetting.pkg;
+ if (pkg == null || !pkg.hasComponentClassName(className)) {
+ if (pkg != null &&
+ pkg.applicationInfo.targetSdkVersion >=
+ Build.VERSION_CODES.JELLY_BEAN) {
+ throw new IllegalArgumentException("Component class " + className
+ + " does not exist in " + packageName);
+ } else {
+ Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
+ + className + " does not exist in " + packageName);
+ }
+ }
+ switch (newState) {
+ case COMPONENT_ENABLED_STATE_ENABLED:
+ if (!pkgSetting.enableComponentLPw(className, userId)) {
+ return;
+ }
+ break;
+ case COMPONENT_ENABLED_STATE_DISABLED:
+ if (!pkgSetting.disableComponentLPw(className, userId)) {
+ return;
+ }
+ break;
+ case COMPONENT_ENABLED_STATE_DEFAULT:
+ if (!pkgSetting.restoreComponentLPw(className, userId)) {
+ return;
+ }
+ break;
+ default:
+ Slog.e(TAG, "Invalid new component state: " + newState);
+ return;
+ }
+ }
+ synchronized (mPackages) {
scheduleWritePackageRestrictionsLocked(userId);
updateSequenceNumberLP(pkgSetting, new int[] { userId });
final long callingId = Binder.clearCallingIdentity();