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

Commit 3b702144 authored by Samiul Islam's avatar Samiul Islam
Browse files

Resolve SDK dependency asynchronously before install

If a single session has unresolved SDK or static library dependency, we
try to bind with Dependency Installer to install the missing dependencies.

This CL currently focuses on creating a asynchronous branch in the install
flow. The dependency installer service doesn't exist yet, so resolution
will always fail.

Bug: 372862145
Test: atest PackageManagerShellCommandInstallTest
Test: Presubmit
FLAG: android.content.pm.sdk_dependency_installer
Change-Id: I78756e9850b48a3d275d95ca13b847412b297239
parent b87170e4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ public class ApkLiteParseUtilsTest {

    @Before
    public void setUp() throws IOException {
        mTmpDir = mTemporaryFolder.newFolder("DexMetadataHelperTest");
        mTmpDir = mTemporaryFolder.newFolder("ApkLiteParseUtilsTest");
    }

    @After
+67 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;

import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.PackageLite;
import android.os.OutcomeReceiver;

import java.util.List;

/**
 * Helper class to interact with SDK Dependency Installer service.
 */
public class InstallDependencyHelper {
    private final SharedLibrariesImpl mSharedLibraries;

    InstallDependencyHelper(SharedLibrariesImpl sharedLibraries) {
        mSharedLibraries = sharedLibraries;
    }

    void resolveLibraryDependenciesIfNeeded(PackageLite pkg,
            OutcomeReceiver<Void, PackageManagerException> callback) {
        final List<SharedLibraryInfo> missing;
        try {
            missing = mSharedLibraries.collectMissingSharedLibraryInfos(pkg);
        } catch (PackageManagerException e) {
            callback.onError(e);
            return;
        }

        if (missing.isEmpty()) {
            // No need for dependency resolution. Move to installation directly.
            callback.onResult(null);
            return;
        }

        try {
            bindToDependencyInstaller();
        } catch (Exception e) {
            PackageManagerException pe = new PackageManagerException(
                    INSTALL_FAILED_MISSING_SHARED_LIBRARY, e.getMessage());
            callback.onError(pe);
        }
    }

    private void bindToDependencyInstaller() {
        throw new IllegalStateException("Failed to bind to Dependency Installer");
    }


}
+6 −2
Original line number Diff line number Diff line
@@ -220,6 +220,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements

    private AppOpsManager mAppOps;
    private final VerifierController mVerifierController;
    private final InstallDependencyHelper mInstallDependencyHelper;

    private final HandlerThread mInstallThread;
    private final Handler mInstallHandler;
@@ -346,6 +347,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
        synchronized (mVerificationPolicyPerUser) {
            mVerificationPolicyPerUser.put(USER_SYSTEM, DEFAULT_VERIFICATION_POLICY);
        }
        mInstallDependencyHelper = new InstallDependencyHelper(
                mPm.mInjector.getSharedLibrariesImpl());

        LocalServices.getService(SystemServiceManager.class).startService(
                new Lifecycle(context, this));
@@ -543,7 +546,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                            session = PackageInstallerSession.readFromXml(in, mInternalCallback,
                                    mContext, mPm, mInstallThread.getLooper(), mStagingManager,
                                    mSessionsDir, this, mSilentUpdatePolicy,
                                    mVerifierController);
                                    mVerifierController, mInstallDependencyHelper);
                        } catch (Exception e) {
                            Slog.e(TAG, "Could not read session", e);
                            continue;
@@ -1065,7 +1068,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
                userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
                null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
                false, false, false, PackageManager.INSTALL_UNKNOWN, "", null,
                mVerifierController, verificationPolicy, verificationPolicy);
                mVerifierController, verificationPolicy, verificationPolicy,
                mInstallDependencyHelper);

        synchronized (mSessions) {
            mSessions.put(sessionId, session);
+44 −4
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.PersistableBundle;
@@ -433,6 +434,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
    private final StagingManager mStagingManager;
    @NonNull private final VerifierController mVerifierController;

    private final InstallDependencyHelper mInstallDependencyHelper;

    final int sessionId;
    final int userId;
    final SessionParams params;
@@ -1188,7 +1191,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            String sessionErrorMessage, DomainSet preVerifiedDomains,
            @NonNull VerifierController verifierController,
            @PackageInstaller.VerificationPolicy int initialVerificationPolicy,
            @PackageInstaller.VerificationPolicy int currentVerificationPolicy) {
            @PackageInstaller.VerificationPolicy int currentVerificationPolicy,
            InstallDependencyHelper installDependencyHelper) {
        mCallback = callback;
        mContext = context;
        mPm = pm;
@@ -1200,6 +1204,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        mVerifierController = verifierController;
        mInitialVerificationPolicy = initialVerificationPolicy;
        mCurrentVerificationPolicy = new AtomicInteger(currentVerificationPolicy);
        mInstallDependencyHelper = installDependencyHelper;

        this.sessionId = sessionId;
        this.userId = userId;
@@ -2611,6 +2616,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        maybeFinishChildSessions(error, msg);
    }

    private void onSessionDependencyResolveFailure(int error, String msg) {
        Slog.e(TAG, "Failed to resolve dependency for session " + sessionId);
        // Dispatch message to remove session from PackageInstallerService.
        dispatchSessionFinished(error, msg, null);
        maybeFinishChildSessions(error, msg);
    }

    private void onSystemDataLoaderUnrecoverable() {
        final String packageName = getPackageName();
        if (TextUtils.isEmpty(packageName)) {
@@ -3402,8 +3414,35 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                    /* extras= */ null, /* forPreapproval= */ false);
            return;
        }

        if (Flags.sdkDependencyInstaller() && !isMultiPackage()) {
            resolveLibraryDependenciesIfNeeded();
        } else {
            install();
        }
    }


    private void resolveLibraryDependenciesIfNeeded() {
        synchronized (mLock) {
            // TODO(b/372862145): Callback should be called on a handler passed as parameter
            mInstallDependencyHelper.resolveLibraryDependenciesIfNeeded(mPackageLite,
                    new OutcomeReceiver<>() {

                        @Override
                        public void onResult(Void result) {
                            install();
                        }

                        @Override
                        public void onError(@NonNull PackageManagerException e) {
                            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                            setSessionFailed(e.error, completeMsg);
                            onSessionDependencyResolveFailure(e.error, completeMsg);
                        }
                    });
        }
    }

    /**
     * Stages this session for install and returns a
@@ -6048,7 +6087,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
            @NonNull StagingManager stagingManager, @NonNull File sessionsDir,
            @NonNull PackageSessionProvider sessionProvider,
            @NonNull SilentUpdatePolicy silentUpdatePolicy,
            @NonNull VerifierController verifierController)
            @NonNull VerifierController verifierController,
            @NonNull InstallDependencyHelper installDependencyHelper)
            throws IOException, XmlPullParserException {
        final int sessionId = in.getAttributeInt(null, ATTR_SESSION_ID);
        final int userId = in.getAttributeInt(null, ATTR_USER_ID);
@@ -6257,6 +6297,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
                childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
                sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController,
                initialVerificationPolicy, currentVerificationPolicy);
                initialVerificationPolicy, currentVerificationPolicy, installDependencyHelper);
    }
}
+62 −18
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.VersionedPackage;
import android.content.pm.parsing.PackageLite;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -83,6 +84,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
    private static final boolean DEBUG_SHARED_LIBRARIES = false;

    private static final String LIBRARY_TYPE_SDK = "sdk";
    private static final String LIBRARY_TYPE_STATIC = "static shared";

    /**
     * Apps targeting Android S and above need to declare dependencies to the public native
@@ -926,18 +928,19 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
        if (!pkg.getUsesLibraries().isEmpty()) {
            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, null,
                    pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
                    availablePackages, newLibraries);
                    availablePackages, newLibraries, null);
        }
        if (!pkg.getUsesStaticLibraries().isEmpty()) {
            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
                    pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
                    null, pkg.getPackageName(), "static shared", true,
                    pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries);
                    null, pkg.getPackageName(), LIBRARY_TYPE_STATIC, true,
                    pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries,
                    null);
        }
        if (!pkg.getUsesOptionalLibraries().isEmpty()) {
            usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
                    null, pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
                    usesLibraryInfos, availablePackages, newLibraries);
                    usesLibraryInfos, availablePackages, newLibraries, null);
        }
        if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
                pkg.getPackageName(), pkg.getTargetSdkVersion())) {
@@ -945,13 +948,13 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
                        null, null, pkg.getPackageName(), "native shared", true,
                        pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
                        newLibraries);
                        newLibraries, null);
            }
            if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
                usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
                        null, null, null, pkg.getPackageName(), "native shared", false,
                        pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
                        newLibraries);
                        newLibraries, null);
            }
        }
        if (!pkg.getUsesSdkLibraries().isEmpty()) {
@@ -961,11 +964,34 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
                    pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
                    pkg.getUsesSdkLibrariesOptional(),
                    pkg.getPackageName(), LIBRARY_TYPE_SDK, required, pkg.getTargetSdkVersion(),
                    usesLibraryInfos, availablePackages, newLibraries);
                    usesLibraryInfos, availablePackages, newLibraries, null);
        }
        return usesLibraryInfos;
    }

    List<SharedLibraryInfo> collectMissingSharedLibraryInfos(PackageLite pkgLite)
            throws PackageManagerException {
        ArrayList<SharedLibraryInfo> missingSharedLibrary = new ArrayList<>();
        synchronized (mPm.mLock) {
            collectSharedLibraryInfos(pkgLite.getUsesSdkLibraries(),
                    pkgLite.getUsesSdkLibrariesVersionsMajor(),
                    pkgLite.getUsesSdkLibrariesCertDigests(),
                    /*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_SDK,
                    /*required=*/ true, pkgLite.getTargetSdk(),
                    /*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null,
                    missingSharedLibrary);

            collectSharedLibraryInfos(pkgLite.getUsesStaticLibraries(),
                    pkgLite.getUsesStaticLibrariesVersions(),
                    pkgLite.getUsesStaticLibrariesCertDigests(),
                    /*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_STATIC,
                    /*required=*/ true, pkgLite.getTargetSdk(),
                    /*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null,
                    missingSharedLibrary);
        }
        return missingSharedLibrary;
    }

    private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
            @NonNull List<String> requestedLibraries,
            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@@ -973,7 +999,8 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
            @NonNull String packageName, @NonNull String libraryType, boolean required,
            int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
            @NonNull final Map<String, AndroidPackage> availablePackages,
            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
            @Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries,
            @Nullable final List<SharedLibraryInfo> outMissingSharedLibraryInfos)
            throws PackageManagerException {
        final int libCount = requestedLibraries.size();
        for (int i = 0; i < libCount; i++) {
@@ -986,10 +1013,26 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
                        libName, libVersion, mSharedLibraries, newLibraries);
            }
            if (libraryInfo == null) {
                // Only allow app be installed if the app specifies the sdk-library dependency is
                // optional
                if (required || (LIBRARY_TYPE_SDK.equals(libraryType) && (libsOptional != null
                        && !libsOptional[i]))) {
                if (required) {
                    boolean isSdkOrStatic = libraryType.equals(LIBRARY_TYPE_SDK)
                            || libraryType.equals(LIBRARY_TYPE_STATIC);
                    if (isSdkOrStatic && outMissingSharedLibraryInfos != null) {
                        // TODO(b/372862145): Pass the CertDigest too
                        // If Dependency Installation is supported, try that instead of failing.
                        SharedLibraryInfo missingLibrary = new SharedLibraryInfo(
                                libName, libVersion, SharedLibraryInfo.TYPE_SDK_PACKAGE
                        );
                        outMissingSharedLibraryInfos.add(missingLibrary);
                    } else {
                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                "Package " + packageName + " requires unavailable " + libraryType
                                + " library " + libName + "; failing!");
                    }
                } else {
                    // Only allow app be installed if the app specifies the sdk-library
                    // dependency is optional
                    boolean isOptional = libsOptional != null && libsOptional[i];
                    if (LIBRARY_TYPE_SDK.equals(libraryType) && !isOptional) {
                        throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                "Package " + packageName + " requires unavailable " + libraryType
                                + " library " + libName + "; failing!");
@@ -997,6 +1040,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
                        Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
                                + " library " + libName + "; ignoring!");
                    }
                }
            } else {
                if (requiredVersions != null && requiredCertDigests != null) {
                    if (libraryInfo.getLongVersion() != requiredVersions[i]) {
Loading