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;
+ }
}
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;
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;
private UserManagerInternal mUserManagerInternal;
+ private File mCacheDir;
+
private static class IFVerificationParams {
PackageParser.Package pkg;
boolean replacing;
}
}
+ mCacheDir = preparePackageParserCache(mIsUpgrade);
+
// Set flag to monitor and not change apk file paths when
// scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
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;
+ " 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;
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);
});
ParallelPackageParser(String[] separateProcesses, boolean onlyCoreApps,
- DisplayMetrics metrics) {
+ DisplayMetrics metrics, File cacheDir) {
mSeparateProcesses = separateProcesses;
mOnlyCore = onlyCoreApps;
mMetrics = metrics;
+ mCacheDir = cacheDir;
}
static class ParseResult {
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
+ pp.setCacheDir(mCacheDir);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
@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