Merge "Populate active apexes cache in a background thread" into qt-dev
am: 05cec74a48
Change-Id: I9360bcda2247d2d11b7ed01ed47d2e8ef9eebc61
This commit is contained in:
@@ -22,22 +22,21 @@ import android.apex.ApexInfo;
|
||||
import android.apex.ApexInfoList;
|
||||
import android.apex.ApexSessionInfo;
|
||||
import android.apex.IApexService;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.PackageParser.PackageParserException;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.ServiceManager.ServiceNotFoundException;
|
||||
import android.os.SystemClock;
|
||||
import android.sysprop.ApexProperties;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
@@ -46,75 +45,108 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* ApexManager class handles communications with the apex service to perform operation and queries,
|
||||
* as well as providing caching to avoid unnecessary calls to the service.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class ApexManager {
|
||||
static final String TAG = "ApexManager";
|
||||
private final IApexService mApexService;
|
||||
private final Context mContext;
|
||||
private final Object mLock = new Object();
|
||||
@GuardedBy("mLock")
|
||||
public final class ApexManager extends SystemService {
|
||||
private static final String TAG = "ApexManager";
|
||||
private IApexService mApexService;
|
||||
|
||||
private final CountDownLatch mActivePackagesCacheLatch = new CountDownLatch(1);
|
||||
private Map<String, PackageInfo> mActivePackagesCache;
|
||||
|
||||
ApexManager(Context context) {
|
||||
private final CountDownLatch mApexFilesCacheLatch = new CountDownLatch(1);
|
||||
private ApexInfo[] mApexFiles;
|
||||
|
||||
public ApexManager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
try {
|
||||
mApexService = IApexService.Stub.asInterface(
|
||||
ServiceManager.getServiceOrThrow("apexservice"));
|
||||
ServiceManager.getServiceOrThrow("apexservice"));
|
||||
} catch (ServiceNotFoundException e) {
|
||||
throw new IllegalStateException("Required service apexservice not available");
|
||||
}
|
||||
mContext = context;
|
||||
publishLocalService(ApexManager.class, this);
|
||||
HandlerThread oneShotThread = new HandlerThread("ApexManagerOneShotHandler");
|
||||
oneShotThread.start();
|
||||
oneShotThread.getThreadHandler().post(this::initSequence);
|
||||
oneShotThread.quitSafely();
|
||||
}
|
||||
|
||||
void systemReady() {
|
||||
mContext.registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
onBootCompleted();
|
||||
mContext.unregisterReceiver(this);
|
||||
}
|
||||
}, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
|
||||
private void initSequence() {
|
||||
populateApexFilesCache();
|
||||
parseApexFiles();
|
||||
}
|
||||
|
||||
private void populateActivePackagesCacheIfNeeded() {
|
||||
synchronized (mLock) {
|
||||
if (mActivePackagesCache != null) {
|
||||
return;
|
||||
}
|
||||
private void populateApexFilesCache() {
|
||||
if (mApexFiles != null) {
|
||||
return;
|
||||
}
|
||||
long startTimeMicros = SystemClock.currentTimeMicro();
|
||||
Slog.i(TAG, "Starting to populate apex files cache");
|
||||
try {
|
||||
mApexFiles = mApexService.getActivePackages();
|
||||
Slog.i(TAG, "IPC to apexd finished in " + (SystemClock.currentTimeMicro()
|
||||
- startTimeMicros) + " μs");
|
||||
} catch (RemoteException re) {
|
||||
// TODO: make sure this error is propagated to system server.
|
||||
Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
|
||||
re.rethrowAsRuntimeException();
|
||||
}
|
||||
mApexFilesCacheLatch.countDown();
|
||||
Slog.i(TAG, "Finished populating apex files cache in " + (SystemClock.currentTimeMicro()
|
||||
- startTimeMicros) + " μs");
|
||||
}
|
||||
|
||||
private void parseApexFiles() {
|
||||
waitForLatch(mApexFilesCacheLatch);
|
||||
if (mApexFiles == null) {
|
||||
throw new IllegalStateException("mApexFiles must be populated");
|
||||
}
|
||||
long startTimeMicros = SystemClock.currentTimeMicro();
|
||||
Slog.i(TAG, "Starting to parse apex files");
|
||||
List<PackageInfo> list = new ArrayList<>();
|
||||
// TODO: this can be parallelized.
|
||||
for (ApexInfo ai : mApexFiles) {
|
||||
try {
|
||||
List<PackageInfo> list = new ArrayList<>();
|
||||
final ApexInfo[] activePkgs = mApexService.getActivePackages();
|
||||
for (ApexInfo ai : activePkgs) {
|
||||
// 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.packagePath)).isDirectory()) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
list.add(PackageParser.generatePackageInfoFromApex(
|
||||
new File(ai.packagePath), PackageManager.GET_META_DATA
|
||||
| PackageManager.GET_SIGNING_CERTIFICATES));
|
||||
} catch (PackageParserException pe) {
|
||||
throw new IllegalStateException("Unable to parse: " + ai, pe);
|
||||
}
|
||||
// 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.packagePath)).isDirectory()) {
|
||||
break;
|
||||
}
|
||||
mActivePackagesCache = list.stream().collect(
|
||||
Collectors.toMap(p -> p.packageName, Function.identity()));
|
||||
} catch (RemoteException re) {
|
||||
Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
|
||||
throw new RuntimeException(re);
|
||||
list.add(PackageParser.generatePackageInfoFromApex(
|
||||
new File(ai.packagePath), PackageManager.GET_META_DATA
|
||||
| PackageManager.GET_SIGNING_CERTIFICATES));
|
||||
} catch (PackageParserException pe) {
|
||||
// TODO: make sure this error is propagated to system server.
|
||||
throw new IllegalStateException("Unable to parse: " + ai, pe);
|
||||
}
|
||||
}
|
||||
mActivePackagesCache = list.stream().collect(
|
||||
Collectors.toMap(p -> p.packageName, Function.identity()));
|
||||
mActivePackagesCacheLatch.countDown();
|
||||
Slog.i(TAG, "Finished parsing apex files in " + (SystemClock.currentTimeMicro()
|
||||
- startTimeMicros) + " μs");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves information about an active APEX package.
|
||||
*
|
||||
* <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in
|
||||
* case {@link #parseApexFiles()}} throws an exception this method will never finish
|
||||
* essentially putting device into a boot loop.
|
||||
*
|
||||
* @param packageName the package name to look for. Note that this is the package name reported
|
||||
* in the APK container manifest (i.e. AndroidManifest.xml), which might
|
||||
* differ from the one reported in the APEX manifest (i.e.
|
||||
@@ -123,30 +155,43 @@ class ApexManager {
|
||||
* is not found.
|
||||
*/
|
||||
@Nullable PackageInfo getActivePackage(String packageName) {
|
||||
populateActivePackagesCacheIfNeeded();
|
||||
waitForLatch(mActivePackagesCacheLatch);
|
||||
return mActivePackagesCache.get(packageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves information about all active APEX packages.
|
||||
*
|
||||
* <p>This method blocks caller thread until {@link #parseApexFiles()} succeeds. Note that in
|
||||
* case {@link #parseApexFiles()}} throws an exception this method will never finish
|
||||
* essentially putting device into a boot loop.
|
||||
*
|
||||
* @return a Collection of PackageInfo object, each one containing information about a different
|
||||
* active package.
|
||||
*/
|
||||
Collection<PackageInfo> getActivePackages() {
|
||||
populateActivePackagesCacheIfNeeded();
|
||||
waitForLatch(mActivePackagesCacheLatch);
|
||||
return mActivePackagesCache.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code packageName} is an apex package.
|
||||
*
|
||||
* <p>This method blocks caller thread until {@link #populateApexFilesCache()} succeeds. Note
|
||||
* that in case {@link #populateApexFilesCache()} throws an exception this method will never
|
||||
* finish essentially putting device into a boot loop.
|
||||
*
|
||||
* @param packageName package to check.
|
||||
* @return {@code true} if {@code packageName} is an apex package.
|
||||
*/
|
||||
boolean isApexPackage(String packageName) {
|
||||
populateActivePackagesCacheIfNeeded();
|
||||
return mActivePackagesCache.containsKey(packageName);
|
||||
waitForLatch(mApexFilesCacheLatch);
|
||||
for (ApexInfo ai : mApexFiles) {
|
||||
if (ai.packageName.equals(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +318,19 @@ class ApexManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks current thread until {@code latch} has counted down to zero.
|
||||
*
|
||||
* @throws RuntimeException if thread was interrupted while waiting.
|
||||
*/
|
||||
private void waitForLatch(CountDownLatch latch) {
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException("Interrupted waiting for cache to be populated", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps various state information to the provided {@link PrintWriter} object.
|
||||
*
|
||||
@@ -286,7 +344,7 @@ class ApexManager {
|
||||
ipw.println("Active APEX packages:");
|
||||
ipw.increaseIndent();
|
||||
try {
|
||||
populateActivePackagesCacheIfNeeded();
|
||||
waitForLatch(mActivePackagesCacheLatch);
|
||||
for (PackageInfo pi : mActivePackagesCache.values()) {
|
||||
if (packageName != null && !packageName.equals(pi.packageName)) {
|
||||
continue;
|
||||
@@ -331,8 +389,4 @@ class ApexManager {
|
||||
ipw.println("Couldn't communicate with apexd.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onBootCompleted() {
|
||||
populateActivePackagesCacheIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2374,6 +2374,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
public PackageManagerService(Context context, Installer installer,
|
||||
boolean factoryTest, boolean onlyCore) {
|
||||
mApexManager = LocalServices.getService(ApexManager.class);
|
||||
|
||||
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
|
||||
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
|
||||
@@ -2470,7 +2472,6 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
mProtectedPackages = new ProtectedPackages(mContext);
|
||||
|
||||
mApexManager = new ApexManager(context);
|
||||
synchronized (mInstallLock) {
|
||||
// writer
|
||||
synchronized (mPackages) {
|
||||
@@ -21462,7 +21463,6 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
storage.registerListener(mStorageListener);
|
||||
|
||||
mInstallerService.systemReady();
|
||||
mApexManager.systemReady();
|
||||
mPackageDexOptimizer.systemReady();
|
||||
|
||||
getStorageManagerInternal().addExternalStoragePolicy(
|
||||
|
||||
@@ -115,6 +115,7 @@ import com.android.server.om.OverlayManagerService;
|
||||
import com.android.server.os.BugreportManagerService;
|
||||
import com.android.server.os.DeviceIdentifiersPolicyService;
|
||||
import com.android.server.os.SchedulingPolicyService;
|
||||
import com.android.server.pm.ApexManager;
|
||||
import com.android.server.pm.BackgroundDexOptService;
|
||||
import com.android.server.pm.CrossProfileAppsService;
|
||||
import com.android.server.pm.DynamicCodeLoggingService;
|
||||
@@ -627,6 +628,12 @@ public final class SystemServer {
|
||||
watchdog.start();
|
||||
traceEnd();
|
||||
|
||||
// Start ApexManager as early as we can to give it enough time to call apexd and populate
|
||||
// cache of known apex packages. Note that calling apexd will happen asynchronously.
|
||||
traceBeginAndSlog("StartApexManager");
|
||||
mSystemServiceManager.startService(ApexManager.class);
|
||||
traceEnd();
|
||||
|
||||
Slog.i(TAG, "Reading configuration...");
|
||||
final String TAG_SYSTEM_CONFIG = "ReadingSystemConfig";
|
||||
traceBeginAndSlog(TAG_SYSTEM_CONFIG);
|
||||
|
||||
Reference in New Issue
Block a user