diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index c37ea8ba68bda..c97f33b5103f1 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; @@ -61,7 +62,9 @@ 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; /** @@ -72,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 = @@ -139,7 +142,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. @@ -154,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. @@ -188,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. @@ -342,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; @@ -357,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; @@ -373,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", @@ -411,104 +432,94 @@ 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(); + @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; boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0; for (PackageInfo packageInfo: mAllPackagesCache) { @@ -525,7 +536,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 +546,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 +556,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 +567,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; @@ -562,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 { @@ -684,8 +729,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 +743,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 +772,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 +846,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 +875,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,12 +931,13 @@ public abstract class ApexManager { } @Override - void systemReady(Context context) { + void scanApexPackagesTraced(@NonNull PackageParser2 packageParser, + @NonNull ExecutorService executorService) { // No-op } @Override - PackageInfo getPackageInfo(String packageName, int flags) { + public PackageInfo getPackageInfo(String packageName, int flags) { return null; } @@ -908,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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 58a9d9c5f6bf0..34363c8df8f6c 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 @@ -20696,7 +20699,6 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); - mApexManager.systemReady(mContext); mPackageDexOptimizer.systemReady(); mInjector.getStorageManagerInternal().addExternalStoragePolicy( 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 04c965e695885..765ecb9710cb0 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -42,6 +42,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; @@ -130,6 +131,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; @@ -3317,8 +3319,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()) { 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(); }