OSDN Git Service

PackageManagerService: Implement packageParser cache in ParallelPackageParser.
authorNarayan Kamath <narayan@google.com>
Thu, 24 Nov 2016 13:22:40 +0000 (13:22 +0000)
committerNarayan Kamath <narayan@google.com>
Thu, 5 Jan 2017 19:15:31 +0000 (19:15 +0000)
We save about 2800ms of cold startup time over baseline on a marlin,
and ~1200 ms over the parallel parsing case.

                   warm     cold
   ---------------
Baseline         : 1700ms   4300ms
Parallel         : 1400ms   2700ms
Cache            : 1000ms   1600ms
Cache & parallel : 900ms    1500ms

Note that further changes will improve the speed of cache processing.

This change also includes support for :
- a flag that been flipped in code (currently set to false).
- disabling the cache via a system property.
- wiping the cache on system upgrades.
- cache versioning.

Bug: 30792387

Test: FrameworksServicesTests
Test: manual timing

Change-Id: I281710c110af5307901dd62ce93b515287c91918

core/java/android/os/FileUtils.java
services/core/java/com/android/server/pm/PackageManagerService.java
services/core/java/com/android/server/pm/ParallelPackageParser.java
services/tests/servicestests/src/com/android/server/pm/ParallelPackageParserTest.java

index 760df45..73c9462 100644 (file)
@@ -762,4 +762,19 @@ public class FileUtils {
     public static @Nullable File newFileOrNull(@Nullable String path) {
         return (path != null) ? new File(path) : null;
     }
+
+    /**
+     * Creates a directory with name {@code name} under an existing directory {@code baseDir}.
+     * Returns a {@code File} object representing the directory on success, {@code null} on
+     * failure.
+     */
+    public static @Nullable File createDir(File baseDir, String name) {
+        final File dir = new File(baseDir, name);
+
+        if (dir.exists()) {
+            return dir.isDirectory() ? dir : null;
+        }
+
+        return dir.mkdir() ? dir : null;
+    }
 }
index 2dd9503..1031588 100644 (file)
@@ -102,7 +102,6 @@ import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCES
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
@@ -566,6 +565,18 @@ public class PackageManagerService extends IPackageManager.Stub {
             Manifest.permission.WRITE_EXTERNAL_STORAGE,
             Manifest.permission.READ_PHONE_NUMBER);
 
+
+    /**
+     * Version number for the package parser cache. Increment this whenever the format or
+     * extent of cached data changes. See {@code PackageParser#setCacheDir}.
+     */
+    private static final String PACKAGE_PARSER_CACHE_VERSION = "1";
+
+    /**
+     * Whether the package parser cache is enabled.
+     */
+    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = false;
+
     final ServiceThread mHandlerThread;
 
     final PackageHandler mHandler;
@@ -804,6 +815,8 @@ public class PackageManagerService extends IPackageManager.Stub {
 
     private UserManagerInternal mUserManagerInternal;
 
+    private File mCacheDir;
+
     private static class IFVerificationParams {
         PackageParser.Package pkg;
         boolean replacing;
@@ -2353,6 +2366,8 @@ public class PackageManagerService extends IPackageManager.Stub {
                 }
             }
 
+            mCacheDir = preparePackageParserCache(mIsUpgrade);
+
             // Set flag to monitor and not change apk file paths when
             // scanning install directories.
             int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
@@ -2815,6 +2830,35 @@ public class PackageManagerService extends IPackageManager.Stub {
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
     }
 
+    private static File preparePackageParserCache(boolean isUpgrade) {
+        if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) {
+            return null;
+        }
+
+        if (SystemProperties.getBoolean("ro.boot.disable_package_cache", false)) {
+            Slog.i(TAG, "Disabling package parser cache due to system property.");
+            return null;
+        }
+
+        // The base directory for the package parser cache lives under /data/system/.
+        final File cacheBaseDir = FileUtils.createDir(Environment.getDataSystemDirectory(),
+                "package_cache");
+        if (cacheBaseDir == null) {
+            return null;
+        }
+
+        // If this is a system upgrade scenario, delete the contents of the package cache dir.
+        // This also serves to "GC" unused entries when the package cache version changes (which
+        // can only happen during upgrades).
+        if (isUpgrade) {
+            FileUtils.deleteContents(cacheBaseDir);
+        }
+
+        // Return the versioned package cache directory. This is something like
+        // "/data/system/package_cache/1"
+        return FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+    }
+
     @Override
     public boolean isFirstBoot() {
         return mFirstBoot;
@@ -6938,7 +6982,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                     + " flags=0x" + Integer.toHexString(parseFlags));
         }
         ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
-                mSeparateProcesses, mOnlyCore, mMetrics);
+                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir);
 
         // Submit files for parsing in parallel
         int fileCount = 0;
index 158cfc9..c9c91be 100644 (file)
@@ -47,6 +47,7 @@ class ParallelPackageParser implements AutoCloseable {
     private final String[] mSeparateProcesses;
     private final boolean mOnlyCore;
     private final DisplayMetrics mMetrics;
+    private final File mCacheDir;
     private volatile String mInterruptedInThread;
 
     private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
@@ -68,10 +69,11 @@ class ParallelPackageParser implements AutoCloseable {
             });
 
     ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
-            DisplayMetrics metrics) {
+            DisplayMetrics metrics, File cacheDir) {
         mSeparateProcesses = separateProcesses;
         mOnlyCore = onlyCoreApps;
         mMetrics = metrics;
+        mCacheDir = cacheDir;
     }
 
     static class ParseResult {
@@ -122,6 +124,7 @@ class ParallelPackageParser implements AutoCloseable {
                 pp.setSeparateProcesses(mSeparateProcesses);
                 pp.setOnlyCoreApps(mOnlyCore);
                 pp.setDisplayMetrics(mMetrics);
+                pp.setCacheDir(mCacheDir);
                 pr.scanFile = scanFile;
                 pr.pkg = parsePackage(pp, scanFile, parseFlags);
             } catch (Throwable e) {
@@ -144,7 +147,7 @@ class ParallelPackageParser implements AutoCloseable {
     @VisibleForTesting
     protected PackageParser.Package parsePackage(PackageParser packageParser, File scanFile,
             int parseFlags) throws PackageParser.PackageParserException {
-        return packageParser.parsePackage(scanFile, parseFlags);
+        return packageParser.parsePackage(scanFile, parseFlags, true /* useCaches */);
     }
 
     @Override
index d165b8b..6c6eb7e 100644 (file)
@@ -69,7 +69,7 @@ public class ParallelPackageParserTest {
     class TestParallelPackageParser extends ParallelPackageParser {
 
         TestParallelPackageParser() {
-            super(null, false, null);
+            super(null, false, null, null);
         }
 
         @Override