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
This commit is contained in:
committed by
Philip P. Moltmann
parent
93032f6f95
commit
9541b09160
@@ -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<ActiveApexInfo> 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<String> activePackagesSet = new HashSet<>();
|
||||
HashSet<String> 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<File, ApexInfo> 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<String> activePackagesSet = new HashSet<>();
|
||||
HashSet<String> 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<PackageInfo> 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<PackageInfo> 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<PackageInfo> 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<String> 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
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user