From dbfbb17512fe6a5b3c7198d60b6a149969174a71 Mon Sep 17 00:00:00 2001 From: Jeff Hao Date: Tue, 30 Sep 2014 14:41:31 -0700 Subject: [PATCH] Backport of ordering apps for boot dexopt. This is a squashed commit of the following changes: 1. Order apps by priority when performing boot dexopt. (cherry picked from commit 65cde7d42d741c7d9aa2714a397b7333f688ab55) 2. Improve priority ordering of apps when performing boot dexopt. Added core apps and updated system apps. (cherry picked from commit 272bf3a274daff62995caf05da338c1f2a73dae3) 3. Stop boot dexopt when low on memory. (cherry picked from commit 1d892dcb6b0ff3a50cc63e387667dc29baf1014f) Bug: 17641843 Change-Id: Ie32f1c21047d3462aaf728f7633fecf647ba2b47 --- core/java/android/content/pm/PackageInfo.java | 5 + .../android/content/pm/PackageParser.java | 5 + .../server/pm/PackageManagerService.java | 196 ++++++++++++++---- 3 files changed, 161 insertions(+), 45 deletions(-) diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 765b2a9216058..b66bd01727580 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -234,6 +234,9 @@ public class PackageInfo implements Parcelable { */ public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY; + /** @hide */ + public boolean coreApp; + /** @hide */ public boolean requiredForAllUsers; @@ -293,6 +296,7 @@ public class PackageInfo implements Parcelable { dest.writeTypedArray(reqFeatures, parcelableFlags); dest.writeTypedArray(featureGroups, parcelableFlags); dest.writeInt(installLocation); + dest.writeInt(coreApp ? 1 : 0); dest.writeInt(requiredForAllUsers ? 1 : 0); dest.writeString(restrictedAccountType); dest.writeString(requiredAccountType); @@ -337,6 +341,7 @@ public class PackageInfo implements Parcelable { reqFeatures = source.createTypedArray(FeatureInfo.CREATOR); featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR); installLocation = source.readInt(); + coreApp = source.readInt() != 0; requiredForAllUsers = source.readInt() != 0; restrictedAccountType = source.readString(); requiredAccountType = source.readString(); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 3364741e52c47..ca4ff6a49c6c5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -415,6 +415,7 @@ public class PackageParser { pi.sharedUserLabel = p.mSharedUserLabel; pi.applicationInfo = generateApplicationInfo(p, flags, state, userId); pi.installLocation = p.installLocation; + pi.coreApp = p.coreApp; if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0 || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { pi.requiredForAllUsers = p.mRequiredForAllUsers; @@ -1384,6 +1385,8 @@ public class PackageParser { PARSE_DEFAULT_INSTALL_LOCATION); pkg.applicationInfo.installLocation = pkg.installLocation; + pkg.coreApp = attrs.getAttributeBooleanValue(null, "coreApp", false); + sa.recycle(); /* Set the global "forward lock" flag */ @@ -4267,6 +4270,8 @@ public class PackageParser { public int installLocation; + public boolean coreApp; + /* An app that's required for all users and cannot be uninstalled for a user */ public boolean mRequiredForAllUsers; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a8a946adc8b89..b79e15756a112 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -80,6 +80,7 @@ import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.AppGlobals; import android.app.IActivityManager; import android.app.admin.IDevicePolicyManager; import android.app.backup.IBackupManager; @@ -4471,62 +4472,164 @@ public class PackageManagerService extends IPackageManager.Stub { } if (pkgs != null) { - // Filter out packages that aren't recently used. - // - // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which - // should do a full dexopt. - if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) { - // TODO: add a property to control this? - long dexOptLRUThresholdInMinutes; - if (mLazyDexOpt) { - dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. - } else { - dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. - } - long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; - - int total = pkgs.size(); - int skipped = 0; - long now = System.currentTimeMillis(); - for (Iterator i = pkgs.iterator(); i.hasNext();) { - PackageParser.Package pkg = i.next(); - long then = pkg.mLastPackageUsageTimeInMills; - if (then + dexOptLRUThresholdInMills < now) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " + - ((then == 0) ? "never" : new Date(then))); - } - i.remove(); - skipped++; + // Sort apps by importance for dexopt ordering. Important apps are given more priority + // in case the device runs out of space. + ArrayList sortedPkgs = new ArrayList(); + // Give priority to core apps. + for (Iterator it = pkgs.iterator(); it.hasNext();) { + PackageParser.Package pkg = it.next(); + if (pkg.coreApp) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Adding core app " + sortedPkgs.size() + ": " + pkg.packageName); } + sortedPkgs.add(pkg); + it.remove(); } + } + // Give priority to system apps that listen for pre boot complete. + Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); + HashSet pkgNames = getPackageNamesForIntent(intent); + for (Iterator it = pkgs.iterator(); it.hasNext();) { + PackageParser.Package pkg = it.next(); + if (pkgNames.contains(pkg.packageName)) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Adding pre boot system app " + sortedPkgs.size() + ": " + pkg.packageName); + } + sortedPkgs.add(pkg); + it.remove(); + } + } + // Give priority to system apps. + for (Iterator it = pkgs.iterator(); it.hasNext();) { + PackageParser.Package pkg = it.next(); + if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName); + } + sortedPkgs.add(pkg); + it.remove(); + } + } + // Give priority to updated system apps. + for (Iterator it = pkgs.iterator(); it.hasNext();) { + PackageParser.Package pkg = it.next(); + if (isUpdatedSystemApp(pkg)) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName); + } + sortedPkgs.add(pkg); + it.remove(); + } + } + // Give priority to apps that listen for boot complete. + intent = new Intent(Intent.ACTION_BOOT_COMPLETED); + pkgNames = getPackageNamesForIntent(intent); + for (Iterator it = pkgs.iterator(); it.hasNext();) { + PackageParser.Package pkg = it.next(); + if (pkgNames.contains(pkg.packageName)) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Adding boot app " + sortedPkgs.size() + ": " + pkg.packageName); + } + sortedPkgs.add(pkg); + it.remove(); + } + } + // Filter out packages that aren't recently used. + filterRecentlyUsedApps(pkgs); + // Add all remaining apps. + for (PackageParser.Package pkg : pkgs) { if (DEBUG_DEXOPT) { - Log.i(TAG, "Skipped optimizing " + skipped + " of " + total); + Log.i(TAG, "Adding app " + sortedPkgs.size() + ": " + pkg.packageName); } + sortedPkgs.add(pkg); } int i = 0; - for (PackageParser.Package pkg : pkgs) { - i++; - if (DEBUG_DEXOPT) { - Log.i(TAG, "Optimizing app " + i + " of " + pkgs.size() - + ": " + pkg.packageName); + int total = sortedPkgs.size(); + File dataDir = Environment.getDataDirectory(); + long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir); + if (lowThreshold == 0) { + throw new IllegalStateException("Invalid low memory threshold"); + } + for (PackageParser.Package pkg : sortedPkgs) { + long usableSpace = dataDir.getUsableSpace(); + if (usableSpace < lowThreshold) { + Log.w(TAG, "Not running dexopt on remaining apps due to low memory: " + usableSpace); + break; } - if (!isFirstBoot()) { - try { - ActivityManagerNative.getDefault().showBootMessage( - mContext.getResources().getString( - R.string.android_upgrading_apk, - i, pkgs.size()), true); - } catch (RemoteException e) { + performBootDexOpt(pkg, ++i, total); + } + } + } + + private void filterRecentlyUsedApps(HashSet pkgs) { + // Filter out packages that aren't recently used. + // + // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which + // should do a full dexopt. + if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) { + // TODO: add a property to control this? + long dexOptLRUThresholdInMinutes; + if (mLazyDexOpt) { + dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. + } else { + dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. + } + long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; + + int total = pkgs.size(); + int skipped = 0; + long now = System.currentTimeMillis(); + for (Iterator i = pkgs.iterator(); i.hasNext();) { + PackageParser.Package pkg = i.next(); + long then = pkg.mLastPackageUsageTimeInMills; + if (then + dexOptLRUThresholdInMills < now) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " + + ((then == 0) ? "never" : new Date(then))); } - } - PackageParser.Package p = pkg; - synchronized (mInstallLock) { - performDexOptLI(p, null /* instruction sets */, false /* force dex */, false /* defer */, - true /* include dependencies */); + i.remove(); + skipped++; } } + if (DEBUG_DEXOPT) { + Log.i(TAG, "Skipped optimizing " + skipped + " of " + total); + } + } + } + + private HashSet getPackageNamesForIntent(Intent intent) { + List ris = null; + try { + ris = AppGlobals.getPackageManager().queryIntentReceivers( + intent, null, 0, UserHandle.USER_OWNER); + } catch (RemoteException e) { + } + HashSet pkgNames = new HashSet(); + if (ris != null) { + for (ResolveInfo ri : ris) { + pkgNames.add(ri.activityInfo.packageName); + } + } + return pkgNames; + } + + private void performBootDexOpt(PackageParser.Package pkg, int curr, int total) { + if (DEBUG_DEXOPT) { + Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName); + } + if (!isFirstBoot()) { + try { + ActivityManagerNative.getDefault().showBootMessage( + mContext.getResources().getString(R.string.android_upgrading_apk, + curr, total), true); + } catch (RemoteException e) { + } + } + PackageParser.Package p = pkg; + synchronized (mInstallLock) { + performDexOptLI(p, null /* instruction sets */, false /* force dex */, + false /* defer */, true /* include dependencies */); } } @@ -5077,6 +5180,9 @@ public class PackageManagerService extends IPackageManager.Stub { if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; + } else { + // Only allow system apps to be flagged as core apps. + pkg.coreApp = false; } if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {