From 9541b09160f1733504c7498998ee3e747197acf6 Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Tue, 17 Mar 2020 22:35:33 +0800 Subject: [PATCH 1/2] Caches parsed package info for APEX Leverage PackageParser2 and ParallelPackageParser in package manager service to parse APEX files when device boots up. It caches parsed results and enables parallel parsering to improve APEX parsing performance. Bug: 151296698 Test: atest ApexManagerTest Test: atest RollbackManagerHostTest Change-Id: Ie5f49b5b3747b4785bafc14b48a77232373488f0 --- .../com/android/server/pm/ApexManager.java | 227 +++++++++--------- .../server/pm/PackageManagerService.java | 4 +- .../android/server/pm/ApexManagerTest.java | 23 ++ 3 files changed, 144 insertions(+), 110 deletions(-) diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index c37ea8ba68bda..0c46bde4f9972 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -16,22 +16,22 @@ package com.android.server.pm; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.ApexSessionParams; import android.apex.IApexService; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.parsing.PackageInfoWithoutStateUtils; import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; @@ -44,8 +44,9 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; +import com.android.server.pm.parsing.PackageParser2; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.utils.TimingsTraceAndSlog; @@ -62,6 +63,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; /** @@ -139,7 +141,14 @@ public abstract class ApexManager { */ public abstract List getActiveApexInfos(); - abstract void systemReady(Context context); + /** + * Called by package manager service to scan apex package files when device boots up. + * + * @param packageParser The package parser which supports caches. + * @param executorService An executor to support parallel package parsing. + */ + abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService); /** * Retrieves information about an APEX package. @@ -411,104 +420,92 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - // Post populateAllPackagesCacheIfNeeded to a background thread, since it's - // expensive to run it in broadcast handler thread. - BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded()); - context.unregisterReceiver(this); - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); - } - - private void populatePackageNameToApexModuleNameIfNeeded() { - synchronized (mLock) { - if (mPackageNameToApexModuleName != null) { - return; - } - try { - mPackageNameToApexModuleName = new ArrayMap<>(); - final ApexInfo[] allPkgs = mApexService.getAllPackages(); - for (int i = 0; i < allPkgs.length; i++) { - ApexInfo ai = allPkgs[i]; - PackageParser.PackageLite pkgLite; - try { - File apexFile = new File(ai.modulePath); - pkgLite = PackageParser.parsePackageLite(apexFile, 0); - } catch (PackageParser.PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " - + ai.modulePath, pe); - } - mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName); - } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re); - throw new RuntimeException(re); + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackagesTraced"); + try { + synchronized (mLock) { + scanApexPackagesInternalLocked(packageParser, executorService); } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } - private void populateAllPackagesCacheIfNeeded() { - synchronized (mLock) { - if (mAllPackagesCache != null) { - return; - } - try { - mAllPackagesCache = new ArrayList<>(); - HashSet activePackagesSet = new HashSet<>(); - HashSet factoryPackagesSet = new HashSet<>(); - final ApexInfo[] allPkgs = mApexService.getAllPackages(); - for (ApexInfo ai : allPkgs) { - // If the device is using flattened APEX, don't report any APEX - // packages since they won't be managed or updated by PackageManager. - if ((new File(ai.modulePath)).isDirectory()) { - break; - } - int flags = PackageManager.GET_META_DATA - | PackageManager.GET_SIGNING_CERTIFICATES - | PackageManager.GET_SIGNATURES; - PackageParser.Package pkg; - try { - File apexFile = new File(ai.modulePath); - PackageParser pp = new PackageParser(); - pkg = pp.parsePackage(apexFile, flags, false); - PackageParser.collectCertificates(pkg, false); - } catch (PackageParser.PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); - } + @GuardedBy("mLock") + private void scanApexPackagesInternalLocked(PackageParser2 packageParser, + ExecutorService executorService) { + final ApexInfo[] allPkgs; + try { + mAllPackagesCache = new ArrayList<>(); + mPackageNameToApexModuleName = new ArrayMap<>(); + allPkgs = mApexService.getAllPackages(); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + throw new RuntimeException(re); + } + if (allPkgs.length == 0) { + return; + } + int flags = PackageManager.GET_META_DATA + | PackageManager.GET_SIGNING_CERTIFICATES + | PackageManager.GET_SIGNATURES; + ArrayMap parsingApexInfo = new ArrayMap<>(); + ParallelPackageParser parallelPackageParser = + new ParallelPackageParser(packageParser, executorService); - final PackageInfo packageInfo = - PackageParser.generatePackageInfo(pkg, ai, flags); - mAllPackagesCache.add(packageInfo); - if (ai.isActive) { - if (activePackagesSet.contains(packageInfo.packageName)) { - throw new IllegalStateException( - "Two active packages have the same name: " - + packageInfo.packageName); - } - activePackagesSet.add(packageInfo.packageName); - } - if (ai.isFactory) { - if (factoryPackagesSet.contains(packageInfo.packageName)) { - throw new IllegalStateException( - "Two factory packages have the same name: " - + packageInfo.packageName); - } - factoryPackagesSet.add(packageInfo.packageName); - } + for (ApexInfo ai : allPkgs) { + File apexFile = new File(ai.modulePath); + parallelPackageParser.submit(apexFile, flags); + parsingApexInfo.put(apexFile, ai); + } + + HashSet activePackagesSet = new HashSet<>(); + HashSet factoryPackagesSet = new HashSet<>(); + // Process results one by one + for (int i = 0; i < parsingApexInfo.size(); i++) { + ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take(); + Throwable throwable = parseResult.throwable; + ApexInfo ai = parsingApexInfo.get(parseResult.scanFile); + + if (throwable == null) { + final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate( + parseResult.parsedPackage, ai, flags); + if (packageInfo == null) { + throw new IllegalStateException("Unable to generate package info: " + + ai.modulePath); } - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); - throw new RuntimeException(re); + mAllPackagesCache.add(packageInfo); + mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName); + if (ai.isActive) { + if (activePackagesSet.contains(packageInfo.packageName)) { + throw new IllegalStateException( + "Two active packages have the same name: " + + packageInfo.packageName); + } + activePackagesSet.add(packageInfo.packageName); + } + if (ai.isFactory) { + if (factoryPackagesSet.contains(packageInfo.packageName)) { + throw new IllegalStateException( + "Two factory packages have the same name: " + + packageInfo.packageName); + } + factoryPackagesSet.add(packageInfo.packageName); + } + } else if (throwable instanceof PackageParser.PackageParserException) { + throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable); + } else { + throw new IllegalStateException("Unexpected exception occurred while parsing " + + ai.modulePath, throwable); } } } @Override @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; for (PackageInfo packageInfo: mAllPackagesCache) { @@ -525,7 +522,8 @@ public abstract class ApexManager { @Override List getActivePackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> isActive(item)) @@ -534,7 +532,8 @@ public abstract class ApexManager { @Override List getFactoryPackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> isFactory(item)) @@ -543,7 +542,8 @@ public abstract class ApexManager { @Override List getInactivePackages() { - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); return mAllPackagesCache .stream() .filter(item -> !isActive(item)) @@ -553,7 +553,8 @@ public abstract class ApexManager { @Override boolean isApexPackage(String packageName) { if (!isApexSupported()) return false; - populateAllPackagesCacheIfNeeded(); + Preconditions.checkState(mAllPackagesCache != null, + "APEX packages have not been scanned"); for (PackageInfo packageInfo : mAllPackagesCache) { if (packageInfo.packageName.equals(packageName)) { return true; @@ -684,8 +685,9 @@ public abstract class ApexManager { @Override List getApksInApex(String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); String moduleName = mPackageNameToApexModuleName.get(apexPackageName); if (moduleName == null) { return Collections.emptyList(); @@ -697,17 +699,19 @@ public abstract class ApexManager { @Override @Nullable public String getApexModuleNameForPackageName(String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); return mPackageNameToApexModuleName.get(apexPackageName); } } @Override public long snapshotCeData(int userId, int rollbackId, String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); String apexModuleName; synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); } if (apexModuleName == null) { @@ -724,9 +728,10 @@ public abstract class ApexManager { @Override public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { - populatePackageNameToApexModuleNameIfNeeded(); String apexModuleName; synchronized (mLock) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); apexModuleName = mPackageNameToApexModuleName.get(apexPackageName); } if (apexModuleName == null) { @@ -797,15 +802,7 @@ public abstract class ApexManager { void dump(PrintWriter pw, @Nullable String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120); try { - populateAllPackagesCacheIfNeeded(); ipw.println(); - ipw.println("Active APEX packages:"); - dumpFromPackagesCache(getActivePackages(), packageName, ipw); - ipw.println("Inactive APEX packages:"); - dumpFromPackagesCache(getInactivePackages(), packageName, ipw); - ipw.println("Factory APEX packages:"); - dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); - ipw.increaseIndent(); ipw.println("APEX session state:"); ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); @@ -834,6 +831,17 @@ public abstract class ApexManager { ipw.decreaseIndent(); } ipw.decreaseIndent(); + ipw.println(); + if (mAllPackagesCache == null) { + ipw.println("APEX packages have not been scanned"); + return; + } + ipw.println("Active APEX packages:"); + dumpFromPackagesCache(getActivePackages(), packageName, ipw); + ipw.println("Inactive APEX packages:"); + dumpFromPackagesCache(getInactivePackages(), packageName, ipw); + ipw.println("Factory APEX packages:"); + dumpFromPackagesCache(getFactoryPackages(), packageName, ipw); } catch (RemoteException e) { ipw.println("Couldn't communicate with apexd."); } @@ -879,7 +887,8 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { // No-op } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 4e5d6c4efaaba..a060cbf3155a3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2900,6 +2900,9 @@ public class PackageManagerService extends IPackageManager.Stub mMetrics, mCacheDir, mPackageParserCallback); ExecutorService executorService = ParallelPackageParser.makeExecutorService(); + // Prepare apex package info before scanning APKs, these information are needed when + // scanning apk in apex. + mApexManager.scanApexPackagesTraced(packageParser, executorService); // Collect vendor/product/system_ext overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they @@ -20685,7 +20688,6 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); - mApexManager.systemReady(mContext); mPackageDexOptimizer.systemReady(); mInjector.getStorageManagerInternal().addExternalStoragePolicy( diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 40ada2aedd592..db1bbab7ed943 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -43,6 +43,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; +import com.android.server.pm.parsing.PackageParser2; import org.junit.Before; import org.junit.Test; @@ -63,6 +64,7 @@ public class ApexManagerTest { private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777}; private ApexManager mApexManager; private Context mContext; + private PackageParser2 mPackageParser2; private IApexService mApexService = mock(IApexService.class); @@ -70,11 +72,14 @@ public class ApexManagerTest { public void setUp() throws RemoteException { mContext = InstrumentationRegistry.getInstrumentation().getContext(); mApexManager = new ApexManager.ApexManagerImpl(mApexService); + mPackageParser2 = new PackageParser2(null, false, null, null, null); } @Test public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_ACTIVE_PACKAGE); @@ -90,6 +95,8 @@ public class ApexManagerTest { @Test public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG, ApexManager.MATCH_FACTORY_PACKAGE); @@ -105,6 +112,8 @@ public class ApexManagerTest { @Test public void testGetPackageInfo_setFlagsNone() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull(); } @@ -112,6 +121,8 @@ public class ApexManagerTest { @Test public void testGetActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getActivePackages()).isNotEmpty(); } @@ -119,6 +130,8 @@ public class ApexManagerTest { @Test public void testGetActivePackages_noneActivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getActivePackages()).isEmpty(); } @@ -126,6 +139,8 @@ public class ApexManagerTest { @Test public void testGetFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getFactoryPackages()).isNotEmpty(); } @@ -133,6 +148,8 @@ public class ApexManagerTest { @Test public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getFactoryPackages()).isEmpty(); } @@ -140,6 +157,8 @@ public class ApexManagerTest { @Test public void testGetInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getInactivePackages()).isNotEmpty(); } @@ -147,6 +166,8 @@ public class ApexManagerTest { @Test public void testGetInactivePackages_noneInactivePackages() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.getInactivePackages()).isEmpty(); } @@ -154,6 +175,8 @@ public class ApexManagerTest { @Test public void testIsApexPackage() throws RemoteException { when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true)); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue(); } From bd2780151a81d8d0397d2f8ac04320914942d27a Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Tue, 10 Mar 2020 14:46:27 -0700 Subject: [PATCH 2/2] Do not verify APKs out of updated APEXs For APK-in-APEX the system is not aware of the pre-installed APKs. Hence the currently installed APK always shows up as "factory-installed APK". For factory installed APKs we enforce that all privilidged perissions are manually whitelisted. If an APK-in-APEX gets updated the app might request additional privilidged permissions. We now explicity allow this. This change leaves all apex specific logic inside of ApexMgr. E.g. - how to figure out if a pkg is a apk-in-apex - how apexPkgs are marked as factory/updated Fixes: 148853593 Test: Built a new APK-In-APEX that contained new priv permission, installed it and rebooted -> no crash, but permission was not granted Change-Id: I254e5189909efdafe3dc0ca8a5ab5a520457c585 --- .../com/android/server/pm/ApexManager.java | 85 +++++++++++++++---- .../permission/PermissionManagerService.java | 14 ++- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 0c46bde4f9972..c97f33b5103f1 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -62,6 +62,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; @@ -74,7 +75,7 @@ public abstract class ApexManager { private static final String TAG = "ApexManager"; - static final int MATCH_ACTIVE_PACKAGE = 1 << 0; + public static final int MATCH_ACTIVE_PACKAGE = 1 << 0; static final int MATCH_FACTORY_PACKAGE = 1 << 1; private static final Singleton sApexManagerSingleton = @@ -163,7 +164,7 @@ public abstract class ApexManager { * is not found. */ @Nullable - abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); + public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags); /** * Retrieves information about all active APEX packages. @@ -197,6 +198,27 @@ public abstract class ApexManager { */ abstract boolean isApexPackage(String packageName); + /** + * Whether the APEX package is pre-installed or not. + * + * @param packageInfo the package to check + * @return {@code true} if this package is pre-installed, {@code false} otherwise. + */ + public static boolean isFactory(@NonNull PackageInfo packageInfo) { + return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + /** + * Returns the active apex package's name that contains the (apk) package. + * + * @param containedPackage The (apk) package that might be in a apex + * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside + * any apex. + */ + @Nullable + public abstract String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage); + /** * Retrieves information about an apexd staged session i.e. the internal state used by apexd to * track the different states of a session. @@ -351,7 +373,7 @@ public abstract class ApexManager { * difference between {@code packageName} and {@code apexModuleName}. */ @GuardedBy("mLock") - private Map> mApksInApex = new ArrayMap<>(); + private ArrayMap> mApksInApex = new ArrayMap<>(); @GuardedBy("mLock") private List mAllPackagesCache; @@ -366,7 +388,7 @@ public abstract class ApexManager { * the apk container to {@code apexModuleName} of the apex-payload inside. */ @GuardedBy("mLock") - private Map mPackageNameToApexModuleName; + private ArrayMap mPackageNameToApexModuleName; ApexManagerImpl(IApexService apexService) { mApexService = apexService; @@ -382,16 +404,6 @@ public abstract class ApexManager { return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0; } - /** - * Whether the APEX package is pre-installed or not. - * - * @param packageInfo the package to check - * @return {@code true} if this package is pre-installed, {@code false} otherwise. - */ - private static boolean isFactory(PackageInfo packageInfo) { - return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } - @Override public List getActiveApexInfos() { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", @@ -503,7 +515,9 @@ public abstract class ApexManager { } @Override - @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) { + @Nullable + public PackageInfo getPackageInfo(String packageName, + @PackageInfoFlags int flags) { Preconditions.checkState(mAllPackagesCache != null, "APEX packages have not been scanned"); boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0; @@ -563,6 +577,36 @@ public abstract class ApexManager { return false; } + @Override + @Nullable + public String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage) { + Preconditions.checkState(mPackageNameToApexModuleName != null, + "APEX packages have not been scanned"); + + Objects.requireNonNull(containedPackage); + + synchronized (mLock) { + int numApksInApex = mApksInApex.size(); + for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) { + if (mApksInApex.valueAt(apkInApexNum).contains( + containedPackage.getPackageName())) { + String apexModuleName = mApksInApex.keyAt(apkInApexNum); + + int numApexPkgs = mPackageNameToApexModuleName.size(); + for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) { + if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals( + apexModuleName)) { + return mPackageNameToApexModuleName.keyAt(apexPkgNum); + } + } + } + } + } + + return null; + } + @Override @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) { try { @@ -893,7 +937,7 @@ public abstract class ApexManager { } @Override - PackageInfo getPackageInfo(String packageName, int flags) { + public PackageInfo getPackageInfo(String packageName, int flags) { return null; } @@ -917,6 +961,15 @@ public abstract class ApexManager { return false; } + @Override + @Nullable + public String getActiveApexPackageNameContainingPackage( + @NonNull AndroidPackage containedPackage) { + Objects.requireNonNull(containedPackage); + + return null; + } + @Override ApexSessionInfo getStagedSessionInfo(int sessionId) { throw new UnsupportedOperationException(); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 79d0c2db44484..9522cc5d7d403 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -40,6 +40,7 @@ import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED; +import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE; import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL; import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING; import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS; @@ -128,6 +129,7 @@ import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; +import com.android.server.pm.ApexManager; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; @@ -3321,8 +3323,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged() && !platformPackage && platformPermission) { if (!hasPrivappWhitelistEntry(perm, pkg)) { - // Only report violations for apps on system image - if (!mSystemReady && !pkgSetting.getPkgState().isUpdatedSystemApp()) { + ApexManager apexMgr = ApexManager.getInstance(); + String apexContainingPkg = apexMgr.getActiveApexPackageNameContainingPackage(pkg); + + // Only enforce whitelist this on boot + if (!mSystemReady + // Updated system apps do not need to be whitelisted + && !pkgSetting.getPkgState().isUpdatedSystemApp() + // Apps that are in updated apexs' do not need to be whitelisted + && (apexContainingPkg == null || apexMgr.isFactory( + apexMgr.getPackageInfo(apexContainingPkg, MATCH_ACTIVE_PACKAGE)))) { // it's only a reportable violation if the permission isn't explicitly denied ArraySet deniedPermissions = null; if (pkg.isVendor()) {