summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Carlstrom <bdc@google.com>2014-05-07 03:29:25 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2014-05-07 03:29:25 +0000
commitedb88bcd232176e575f2dbee2cfdb1bfbe33faef (patch)
tree2c89c758b994e749f380db7c3156fc04c9e226ca
parent92ddac0457de85704b1f9b6d6fbfcaada56fe182 (diff)
parentff1ec4d9e7b7eb1b6303d147c796f8767ee6715b (diff)
downloadbase-edb88bcd232176e575f2dbee2cfdb1bfbe33faef.tar.gz
Merge "Use package usage information to decide what dex files to optimize in PackageManagerService"
-rw-r--r--core/java/android/content/pm/PackageParser.java11
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java2
-rwxr-xr-xservices/java/com/android/server/pm/PackageManagerService.java338
-rw-r--r--services/java/com/android/server/power/ShutdownThread.java9
4 files changed, 284 insertions, 76 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 52564eb401ab..c0963f546637 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3533,10 +3533,13 @@ public class PackageParser {
// For use by the package manager to keep track of the path to the
// file an app came from.
public String mScanPath;
-
- // For use by package manager to keep track of where it has done dexopt.
- public boolean mDidDexOpt;
-
+
+ // For use by package manager to keep track of where it needs to do dexopt.
+ public boolean mDexOptNeeded = true;
+
+ // For use by package manager to keep track of when a package was last used.
+ public long mLastPackageUsageTimeInMills;
+
// // User set enabled state.
// public int mSetEnabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
//
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 16b9963e6cf2..ff035532f05c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -924,7 +924,7 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* This is set if we had to do a delayed dexopt of an app before launching
- * it, to increasing the ANR timeouts in that case.
+ * it, to increase the ANR timeouts in that case.
*/
boolean mDidDexOpt;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index b77c94cc0edd..9d9e3838be9b 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -28,10 +28,12 @@ import static android.system.OsConstants.S_IRGRP;
import static android.system.OsConstants.S_IXGRP;
import static android.system.OsConstants.S_IROTH;
import static android.system.OsConstants.S_IXOTH;
+import static android.os.Process.PACKAGE_INFO_GID;
+import static android.os.Process.SYSTEM_UID;
import static com.android.internal.util.ArrayUtils.appendInt;
import static com.android.internal.util.ArrayUtils.removeInt;
-import android.content.pm.PackageParser.*;
+import com.android.internal.R;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
@@ -42,8 +44,8 @@ import com.android.internal.util.XmlUtils;
import com.android.server.DeviceStorageMonitorService;
import com.android.server.EventLogTags;
import com.android.server.IntentResolver;
-
import com.android.server.Watchdog;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -78,6 +80,7 @@ import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
+import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageParser;
import android.content.pm.PackageStats;
import android.content.pm.PackageUserState;
@@ -121,6 +124,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import android.text.TextUtils;
+import android.util.AtomicFile;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -132,6 +136,7 @@ import android.util.Xml;
import android.view.Display;
import android.view.WindowManager;
+import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
@@ -141,7 +146,9 @@ import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateException;
@@ -158,12 +165,14 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import dalvik.system.DexFile;
+import dalvik.system.StaleDexCacheError;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
-import com.android.internal.R;
-
/**
* Keep track of all those .apks everywhere.
*
@@ -190,6 +199,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_APP_DIR_OBSERVER = false;
private static final boolean DEBUG_VERIFY = false;
+ private static final boolean DEBUG_DEXOPT = false;
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
@@ -280,7 +290,6 @@ public class PackageManagerService extends IPackageManager.Stub {
final Context mContext;
final boolean mFactoryTest;
final boolean mOnlyCore;
- final boolean mNoDexOpt;
final DisplayMetrics mMetrics;
final int mDefParseFlags;
final String[] mSeparateProcesses;
@@ -584,6 +593,138 @@ public class PackageManagerService extends IPackageManager.Stub {
private final String mRequiredVerifierPackage;
+ private final PackageUsage mPackageUsage = new PackageUsage();
+
+ private class PackageUsage {
+ private static final int WRITE_INTERVAL
+ = (DEBUG_DEXOPT) ? 0 : 30*60*1000; // 30m in ms
+
+ private final Object mFileLock = new Object();
+ private final AtomicLong mLastWritten = new AtomicLong(0);
+ private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
+
+ void write(boolean force) {
+ if (force) {
+ write();
+ return;
+ }
+ if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
+ && !DEBUG_DEXOPT) {
+ return;
+ }
+ if (mBackgroundWriteRunning.compareAndSet(false, true)) {
+ new Thread("PackageUsage_DiskWriter") {
+ public void run() {
+ try {
+ write(true);
+ } finally {
+ mBackgroundWriteRunning.set(false);
+ }
+ }
+ }.start();
+ }
+ }
+
+ private void write() {
+ synchronized (mPackages) {
+ synchronized (mFileLock) {
+ AtomicFile file = getFile();
+ FileOutputStream f = null;
+ try {
+ f = file.startWrite();
+ BufferedOutputStream out = new BufferedOutputStream(f);
+ FileUtils.setPermissions(file.getBaseFile().getPath(), 0660, SYSTEM_UID, PACKAGE_INFO_GID);
+ StringBuilder sb = new StringBuilder();
+ for (PackageParser.Package pkg : mPackages.values()) {
+ if (pkg.mLastPackageUsageTimeInMills == 0) {
+ continue;
+ }
+ sb.setLength(0);
+ sb.append(pkg.packageName);
+ sb.append(' ');
+ sb.append((long)pkg.mLastPackageUsageTimeInMills);
+ sb.append('\n');
+ out.write(sb.toString().getBytes(StandardCharsets.US_ASCII));
+ }
+ out.flush();
+ file.finishWrite(f);
+ } catch (IOException e) {
+ if (f != null) {
+ file.failWrite(f);
+ }
+ Log.e(TAG, "Failed to write package usage times", e);
+ }
+ }
+ }
+ mLastWritten.set(SystemClock.elapsedRealtime());
+ }
+
+ void readLP() {
+ synchronized (mFileLock) {
+ AtomicFile file = getFile();
+ BufferedInputStream in = null;
+ try {
+ in = new BufferedInputStream(file.openRead());
+ StringBuffer sb = new StringBuffer();
+ while (true) {
+ String packageName = readToken(in, sb, ' ');
+ if (packageName == null) {
+ break;
+ }
+ String timeInMillisString = readToken(in, sb, '\n');
+ if (timeInMillisString == null) {
+ throw new IOException("Failed to find last usage time for package "
+ + packageName);
+ }
+ PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ continue;
+ }
+ long timeInMillis;
+ try {
+ timeInMillis = Long.parseLong(timeInMillisString.toString());
+ } catch (NumberFormatException e) {
+ throw new IOException("Failed to parse " + timeInMillisString
+ + " as a long.", e);
+ }
+ pkg.mLastPackageUsageTimeInMills = timeInMillis;
+ }
+ } catch (FileNotFoundException expected) {
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to read package usage times", e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+ mLastWritten.set(SystemClock.elapsedRealtime());
+ }
+
+ private String readToken(InputStream in, StringBuffer sb, char endOfToken)
+ throws IOException {
+ sb.setLength(0);
+ while (true) {
+ int ch = in.read();
+ if (ch == -1) {
+ if (sb.length() == 0) {
+ return null;
+ }
+ throw new IOException("Unexpected EOF");
+ }
+ if (ch == endOfToken) {
+ return sb.toString();
+ }
+ sb.append((char)ch);
+ }
+ }
+
+ private AtomicFile getFile() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ File fname = new File(systemDir, "package-usage.list");
+ return new AtomicFile(fname);
+ }
+ }
+
class PackageHandler extends Handler {
private boolean mBound = false;
final ArrayList<HandlerParams> mPendingInstalls =
@@ -1099,7 +1240,6 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
- mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
mMetrics = new DisplayMetrics();
mSettings = new Settings(context);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
@@ -1181,10 +1321,6 @@ public class PackageManagerService extends IPackageManager.Stub {
// Set flag to monitor and not change apk file paths when
// scanning install directories.
int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
- if (mNoDexOpt) {
- Slog.w(TAG, "Running ENG build: no pre-dexopt!");
- scanMode |= SCAN_NO_DEX;
- }
final HashSet<String> alreadyDexOpted = new HashSet<String>();
@@ -1203,7 +1339,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
- boolean didDexOpt = false;
+ boolean didDexOptLibraryOrTool = false;
final List<String> instructionSets = getAllInstructionSets();
@@ -1223,13 +1359,12 @@ public class PackageManagerService extends IPackageManager.Stub {
}
try {
- if (dalvik.system.DexFile.isDexOptNeededInternal(
- lib, null, instructionSet, false)) {
+ if (DexFile.isDexOptNeededInternal(lib, null, instructionSet, false)) {
alreadyDexOpted.add(lib);
// The list of "shared libraries" we have at this point is
mInstaller.dexopt(lib, Process.SYSTEM_UID, true, instructionSet);
- didDexOpt = true;
+ didDexOptLibraryOrTool = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
@@ -1275,9 +1410,9 @@ public class PackageManagerService extends IPackageManager.Stub {
continue;
}
try {
- if (dalvik.system.DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) {
+ if (DexFile.isDexOptNeededInternal(path, null, instructionSet, false)) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, instructionSet);
- didDexOpt = true;
+ didDexOptLibraryOrTool = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
@@ -1288,7 +1423,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
- if (didDexOpt) {
+ if (didDexOptLibraryOrTool) {
pruneDexFiles(new File(dataDir, "dalvik-cache"));
}
@@ -1459,12 +1594,15 @@ public class PackageManagerService extends IPackageManager.Stub {
// the correct library paths.
updateAllSharedLibrariesLPw();
-
for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
adjustCpuAbisForSharedUserLPw(setting.packages, true /* do dexopt */,
false /* force dexopt */, false /* defer dexopt */);
}
+ // Now that we know all the packages we are keeping,
+ // read and update their last usage times.
+ mPackageUsage.readLP();
+
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
@@ -1520,6 +1658,14 @@ public class PackageManagerService extends IPackageManager.Stub {
//
// Additionally, delete all dex files from the root directory
// since there shouldn't be any there anyway.
+ //
+ // Note: This isn't as good an indicator as it used to be. It
+ // used to include the boot classpath but at some point
+ // DexFile.isDexOptNeeded started returning false for the boot
+ // class path files in all cases. It is very possible in a
+ // small maintenance release update that the library and tool
+ // jars may be unchanged but APK could be removed resulting in
+ // unused dalvik-cache files.
File[] files = cacheDir.listFiles();
if (files != null) {
for (File file : files) {
@@ -3955,21 +4101,60 @@ 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, which
+ // should do a full dexopt.
+ boolean eng = "eng".equals(SystemProperties.get("ro.build.type"));
+ if (eng || !isFirstBoot()) {
+ // TODO: add a property to control this?
+ long dexOptLRUThresholdInMinutes;
+ if (eng) {
+ 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++;
+ }
+ }
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Skipped optimizing " + skipped + " of " + total);
+ }
+ }
+
int i = 0;
for (PackageParser.Package pkg : pkgs) {
+ i++;
+ if (DEBUG_DEXOPT) {
+ Log.i(TAG, "Optimizing app " + i + " of " + pkgs.size()
+ + ": " + pkg.packageName);
+ }
if (!isFirstBoot()) {
- i++;
try {
ActivityManagerNative.getDefault().showBootMessage(
mContext.getResources().getString(
- com.android.internal.R.string.android_upgrading_apk,
+ R.string.android_upgrading_apk,
i, pkgs.size()), true);
} catch (RemoteException e) {
}
}
PackageParser.Package p = pkg;
synchronized (mInstallLock) {
- if (!p.mDidDexOpt) {
+ if (p.mDexOptNeeded) {
performDexOptLI(p, false /* force dex */, false /* defer */,
true /* include dependencies */);
}
@@ -3981,25 +4166,32 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public boolean performDexOpt(String packageName) {
enforceSystemOrRoot("Only the system can request dexopt be performed");
- if (!mNoDexOpt) {
- return false;
- }
PackageParser.Package p;
synchronized (mPackages) {
p = mPackages.get(packageName);
- if (p == null || p.mDidDexOpt) {
+ if (p == null) {
+ return false;
+ }
+ p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
+ mPackageUsage.write();
+ if (!p.mDexOptNeeded) {
return false;
}
}
+
synchronized (mInstallLock) {
return performDexOptLI(p, false /* force dex */, false /* defer */,
true /* include dependencies */) == DEX_OPT_PERFORMED;
}
}
- private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet, boolean forceDex,
- boolean defer, HashSet<String> done) {
+ public void shutdown() {
+ mPackageUsage.write(true);
+ }
+
+ private void performDexOptLibsLI(ArrayList<String> libs, String instructionSet,
+ boolean forceDex, boolean defer, HashSet<String> done) {
for (int i=0; i<libs.size(); i++) {
PackageParser.Package libPkg;
String libName;
@@ -4024,8 +4216,7 @@ public class PackageManagerService extends IPackageManager.Stub {
static final int DEX_OPT_FAILED = -1;
private int performDexOptLI(PackageParser.Package pkg, String instructionSetOverride,
- boolean forceDex,
- boolean defer, HashSet<String> done) {
+ boolean forceDex, boolean defer, HashSet<String> done) {
final String instructionSet = instructionSetOverride != null ?
instructionSetOverride : getAppInstructionSet(pkg.applicationInfo);
@@ -4042,47 +4233,52 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean performed = false;
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
String path = pkg.mScanPath;
- int ret = 0;
try {
- if (forceDex || dalvik.system.DexFile.isDexOptNeededInternal(path,
- pkg.packageName, instructionSet, defer)) {
- if (!forceDex && defer) {
- if (mDeferredDexOpt == null) {
- mDeferredDexOpt = new HashSet<PackageParser.Package>();
- }
- mDeferredDexOpt.add(pkg);
- return DEX_OPT_DEFERRED;
- } else {
- Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName +
- " (instructionSet=" + instructionSet + ")");
-
- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
+ boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
+ pkg.packageName,
+ instructionSet,
+ defer);
+ // There are three basic cases here:
+ // 1.) we need to dexopt, either because we are forced or it is needed
+ // 2.) we are defering a needed dexopt
+ // 3.) we are skipping an unneeded dexopt
+ if (forceDex || (!defer && isDexOptNeededInternal)) {
+ Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
pkg.packageName, instructionSet);
- pkg.mDidDexOpt = true;
- performed = true;
+ // Note that we ran dexopt, since rerunning will
+ // probably just result in an error again.
+ pkg.mDexOptNeeded = false;
+ if (ret < 0) {
+ return DEX_OPT_FAILED;
}
+ return DEX_OPT_PERFORMED;
}
+ if (defer && isDexOptNeededInternal) {
+ if (mDeferredDexOpt == null) {
+ mDeferredDexOpt = new HashSet<PackageParser.Package>();
+ }
+ mDeferredDexOpt.add(pkg);
+ return DEX_OPT_DEFERRED;
+ }
+ pkg.mDexOptNeeded = false;
+ return DEX_OPT_SKIPPED;
} catch (FileNotFoundException e) {
Slog.w(TAG, "Apk not found for dexopt: " + path);
- ret = -1;
+ return DEX_OPT_FAILED;
} catch (IOException e) {
Slog.w(TAG, "IOException reading apk: " + path, e);
- ret = -1;
- } catch (dalvik.system.StaleDexCacheError e) {
+ return DEX_OPT_FAILED;
+ } catch (StaleDexCacheError e) {
Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
- ret = -1;
+ return DEX_OPT_FAILED;
} catch (Exception e) {
Slog.w(TAG, "Exception when doing dexopt : ", e);
- ret = -1;
- }
- if (ret < 0) {
- //error from installer
return DEX_OPT_FAILED;
}
}
-
- return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
+ return DEX_OPT_SKIPPED;
}
private String getAppInstructionSet(ApplicationInfo info) {
@@ -4372,7 +4568,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
- mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert;
+ mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveInfo.activityInfo = mResolveActivity;
@@ -9280,22 +9476,22 @@ public class PackageManagerService extends IPackageManager.Stub {
// Utility method used to move dex files during install.
private int moveDexFilesLI(PackageParser.Package newPackage) {
- int retCode;
if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
- retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
- getAppInstructionSet(newPackage.applicationInfo));
+ final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
+ int retCode = mInstaller.movedex(newPackage.mScanPath, newPackage.mPath,
+ instructionSet);
if (retCode != 0) {
- if (mNoDexOpt) {
- /*
- * If we're in an engineering build, programs are lazily run
- * through dexopt. If the .dex file doesn't exist yet, it
- * will be created when the program is run next.
- */
- Slog.i(TAG, "dex file doesn't exist, skipping move: " + newPackage.mPath);
- } else {
- Slog.e(TAG, "Couldn't rename dex file: " + newPackage.mPath);
- return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
- }
+ /*
+ * Programs may be lazily run through dexopt, so the
+ * source may not exist. However, something seems to
+ * have gone wrong, so note that dexopt needs to be
+ * run again and remove the source file. In addition,
+ * remove the target to make sure there isn't a stale
+ * file from a previous version of the package.
+ */
+ newPackage.mDexOptNeeded = true;
+ mInstaller.rmdex(newPackage.mScanPath, instructionSet);
+ mInstaller.rmdex(newPackage.mPath, instructionSet);
}
}
return PackageManager.INSTALL_SUCCEEDED;
diff --git a/services/java/com/android/server/power/ShutdownThread.java b/services/java/com/android/server/power/ShutdownThread.java
index 88a27f5f74dd..126d4c0c31bf 100644
--- a/services/java/com/android/server/power/ShutdownThread.java
+++ b/services/java/com/android/server/power/ShutdownThread.java
@@ -44,6 +44,7 @@ import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
import com.android.internal.telephony.ITelephony;
+import com.android.server.pm.PackageManagerService;
import android.util.Log;
import android.view.WindowManager;
@@ -328,6 +329,14 @@ public final class ShutdownThread extends Thread {
}
}
+ Log.i(TAG, "Shutting down package manager...");
+
+ final PackageManagerService pm = (PackageManagerService)
+ ServiceManager.getService("package");
+ if (pm != null) {
+ pm.shutdown();
+ }
+
// Shutdown radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);