From 30dc2a08667d56fdd3eecdcb70abec1b28d821f4 Mon Sep 17 00:00:00 2001 From: Jaekyun Seok Date: Tue, 11 Apr 2017 09:41:11 +0900 Subject: [PATCH] Apply static RRO when parsing Android manifest of a target package This CL creates idmaps for static RROs right before parsing a target package. Those idmaps will be used to parse correctly the Android manifest of the target package. Later, OverlayManagerService will update each idmap with the correct gid from the target package app id. Note that the idmaps generated here will be used by OverlayManagerService as well until they are outdated (http://ag/2127839). Additionally a target package should be parsed again if its overlays are updated to apply the changes. Test: building succeeded and tested with sailfish-userdebug. Bug: 37179531 Change-Id: Ie0d5e92ae6e06b2d377ebd4cfdb0a8519b49a944 --- .../android/content/pm/PackageParser.java | 38 +++++- .../server/pm/PackageManagerService.java | 120 +++++++++++++++++- 2 files changed, 152 insertions(+), 6 deletions(-) diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e4db0f07b8018..a05f11b1953c9 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -547,6 +547,8 @@ public class PackageParser { */ public interface Callback { boolean hasFeature(String feature); + String[] getOverlayPaths(String targetPackageName, String targetPath); + String[] getOverlayApks(String targetPackageName); } /** @@ -563,6 +565,14 @@ public class PackageParser { @Override public boolean hasFeature(String feature) { return mPm.hasSystemFeature(feature); } + + @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) { + return null; + } + + @Override public String[] getOverlayApks(String targetPackageName) { + return null; + } } /** @@ -1054,7 +1064,19 @@ public class PackageParser { try { final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath()); - return fromCacheEntry(bytes); + Package p = fromCacheEntry(bytes); + if (mCallback != null) { + String[] overlayApks = mCallback.getOverlayApks(p.packageName); + if (overlayApks != null && overlayApks.length > 0) { + for (String overlayApk : overlayApks) { + // If a static RRO is updated, return null. + if (!isCacheUpToDate(new File(overlayApk), cacheFile)) { + return null; + } + } + } + } + return p; } catch (Exception e) { Slog.w(TAG, "Error reading package cache: ", e); @@ -1238,7 +1260,7 @@ public class PackageParser { parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); final String[] outError = new String[1]; - final Package pkg = parseBaseApk(res, parser, flags, outError); + final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); if (pkg == null) { throw new PackageParserException(mParseError, apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]); @@ -1938,6 +1960,7 @@ public class PackageParser { * need to consider whether they should be supported by split APKs and child * packages. * + * @param apkPath The package apk file path * @param res The resources from which to resolve values * @param parser The manifest parser * @param flags Flags how to parse @@ -1947,7 +1970,7 @@ public class PackageParser { * @throws XmlPullParserException * @throws IOException */ - private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, + private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final String splitName; final String pkgName; @@ -1967,6 +1990,15 @@ public class PackageParser { return null; } + if (mCallback != null) { + String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath); + if (overlayPaths != null && overlayPaths.length > 0) { + for (String overlayPath : overlayPaths) { + res.getAssets().addOverlayPath(overlayPath); + } + } + } + final Package pkg = new Package(pkgName); TypedArray sa = res.obtainAttributes(parser, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f8e40142cdf95..6da5db0b77ac4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -739,12 +739,123 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") final SparseArray> mChangedPackagesSequenceNumbers = new SparseArray<>(); - final PackageParser.Callback mPackageParserCallback = new PackageParser.Callback() { - @Override public boolean hasFeature(String feature) { + class PackageParserCallback implements PackageParser.Callback { + @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); } + + final List getStaticOverlayPackagesLocked( + Collection allPackages, String targetPackageName) { + List overlayPackages = null; + for (PackageParser.Package p : allPackages) { + if (targetPackageName.equals(p.mOverlayTarget) && p.mIsStaticOverlay) { + if (overlayPackages == null) { + overlayPackages = new ArrayList(); + } + overlayPackages.add(p); + } + } + if (overlayPackages != null) { + Comparator cmp = new Comparator() { + public int compare(PackageParser.Package p1, PackageParser.Package p2) { + return p1.mOverlayPriority - p2.mOverlayPriority; + } + }; + Collections.sort(overlayPackages, cmp); + } + return overlayPackages; + } + + final String[] getStaticOverlayPathsLocked(Collection allPackages, + String targetPackageName, String targetPath) { + if ("android".equals(targetPackageName)) { + // Static RROs targeting to "android", ie framework-res.apk, are already applied by + // native AssetManager. + return null; + } + List overlayPackages = + getStaticOverlayPackagesLocked(allPackages, targetPackageName); + if (overlayPackages == null || overlayPackages.isEmpty()) { + return null; + } + List overlayPathList = null; + for (PackageParser.Package overlayPackage : overlayPackages) { + if (targetPath == null) { + if (overlayPathList == null) { + overlayPathList = new ArrayList(); + } + overlayPathList.add(overlayPackage.baseCodePath); + continue; + } + + try { + // Creates idmaps for system to parse correctly the Android manifest of the + // target package. + // + // OverlayManagerService will update each of them with a correct gid from its + // target package app id. + mInstaller.idmap(targetPath, overlayPackage.baseCodePath, + UserHandle.getSharedAppGid( + UserHandle.getUserGid(UserHandle.USER_SYSTEM))); + if (overlayPathList == null) { + overlayPathList = new ArrayList(); + } + overlayPathList.add(overlayPackage.baseCodePath); + } catch (InstallerException e) { + Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " + + overlayPackage.baseCodePath); + } + } + return overlayPathList == null ? null : overlayPathList.toArray(new String[0]); + } + + String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { + synchronized (mPackages) { + return getStaticOverlayPathsLocked( + mPackages.values(), targetPackageName, targetPath); + } + } + + @Override public final String[] getOverlayApks(String targetPackageName) { + return getStaticOverlayPaths(targetPackageName, null); + } + + @Override public final String[] getOverlayPaths(String targetPackageName, + String targetPath) { + return getStaticOverlayPaths(targetPackageName, targetPath); + } }; + class ParallelPackageParserCallback extends PackageParserCallback { + List mOverlayPackages = null; + + void findStaticOverlayPackages() { + synchronized (mPackages) { + for (PackageParser.Package p : mPackages.values()) { + if (p.mIsStaticOverlay) { + if (mOverlayPackages == null) { + mOverlayPackages = new ArrayList(); + } + mOverlayPackages.add(p); + } + } + } + } + + @Override + synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { + // We can trust mOverlayPackages without holding mPackages because package uninstall + // can't happen while running parallel parsing. + // Moreover holding mPackages on each parsing thread causes dead-lock. + return mOverlayPackages == null ? null : + getStaticOverlayPathsLocked(mOverlayPackages, targetPackageName, targetPath); + } + } + + final PackageParser.Callback mPackageParserCallback = new PackageParserCallback(); + final ParallelPackageParserCallback mParallelPackageParserCallback = + new ParallelPackageParserCallback(); + public static final class SharedLibraryEntry { public final String path; public final String apk; @@ -2453,6 +2564,8 @@ public class PackageManagerService extends IPackageManager.Stub | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0); + mParallelPackageParserCallback.findStaticOverlayPackages(); + // Find base frameworks (resource packages without code). scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM @@ -7856,7 +7969,8 @@ public class PackageManagerService extends IPackageManager.Stub + " flags=0x" + Integer.toHexString(parseFlags)); } ParallelPackageParser parallelPackageParser = new ParallelPackageParser( - mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mPackageParserCallback); + mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, + mParallelPackageParserCallback); // Submit files for parsing in parallel int fileCount = 0;