Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit afe0b0e7 authored by Nikita Ioffe's avatar Nikita Ioffe Committed by android-build-merger
Browse files

Merge "Populate active apexes cache in a background thread" into qt-dev am: 05cec74a

am: f7478d7e

Change-Id: Ia9cbae70699bdc25bca05bddf0656290019bdf7e
parents ee25ae23 f7478d7e
Loading
Loading
Loading
Loading
+109 −55
Original line number Diff line number Diff line
@@ -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"));
        } 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) {
    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<>();
                final ApexInfo[] activePkgs = mApexService.getActivePackages();
                for (ApexInfo ai : activePkgs) {
        // TODO: this can be parallelized.
        for (ApexInfo ai : mApexFiles) {
            try {
                // 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) {
                // 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()));
            } catch (RemoteException re) {
                Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
                throw new RuntimeException(re);
            }
        }
        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();
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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) {
@@ -21467,7 +21468,6 @@ public class PackageManagerService extends IPackageManager.Stub
        storage.registerListener(mStorageListener);
        mInstallerService.systemReady();
        mApexManager.systemReady();
        mPackageDexOptimizer.systemReady();
        getStorageManagerInternal().addExternalStoragePolicy(
+7 −0
Original line number Diff line number Diff line
@@ -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;
@@ -629,6 +630,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);