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 commit65cde7d42d
) 2. Improve priority ordering of apps when performing boot dexopt. Added core apps and updated system apps. (cherry picked from commit272bf3a274
) 3. Stop boot dexopt when low on memory. (cherry picked from commit1d892dcb6b
) Bug: 17641843 Change-Id: Ie32f1c21047d3462aaf728f7633fecf647ba2b47
This commit is contained in:
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<PackageParser.Package> 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<PackageParser.Package> sortedPkgs = new ArrayList<PackageParser.Package>();
|
||||
// Give priority to core apps.
|
||||
for (Iterator<PackageParser.Package> 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<String> pkgNames = getPackageNamesForIntent(intent);
|
||||
for (Iterator<PackageParser.Package> 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<PackageParser.Package> 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<PackageParser.Package> 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<PackageParser.Package> 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<PackageParser.Package> 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<PackageParser.Package> 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<String> getPackageNamesForIntent(Intent intent) {
|
||||
List<ResolveInfo> ris = null;
|
||||
try {
|
||||
ris = AppGlobals.getPackageManager().queryIntentReceivers(
|
||||
intent, null, 0, UserHandle.USER_OWNER);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
HashSet<String> pkgNames = new HashSet<String>();
|
||||
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) {
|
||||
|
Reference in New Issue
Block a user