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

Commit e198b8b9 authored by Android Build Merger (Role)'s avatar Android Build Merger (Role) Committed by Android (Google) Code Review
Browse files

Merge "Merge "Populate active apexes cache in a background thread" into qt-dev...

Merge "Merge "Populate active apexes cache in a background thread" into qt-dev am: 05cec74a am: f7478d7e"
parents 96c3a7b6 afe0b0e7
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);