diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 27b6282055a28..9302aaddcdb24 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -894,8 +894,6 @@ final class InstallPackageHelper { final Map installResults = new ArrayMap<>(requests.size()); final Map prepareResults = new ArrayMap<>(requests.size()); final Map versionInfos = new ArrayMap<>(requests.size()); - final Map lastStaticSharedLibSettings = - new ArrayMap<>(requests.size()); final Map createdAppId = new ArrayMap<>(requests.size()); boolean success = false; try { @@ -955,35 +953,22 @@ final class InstallPackageHelper { createdAppId.put(packageName, optimisticallyRegisterAppId(result)); versionInfos.put(result.mPkgSetting.getPkg().getPackageName(), mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg())); - if (result.mStaticSharedLibraryInfo != null) { - final PackageSetting staticSharedLibLatestVersionSetting = - mSharedLibraries.getStaticSharedLibLatestVersionSetting(result); - if (staticSharedLibLatestVersionSetting != null) { - lastStaticSharedLibSettings.put( - result.mPkgSetting.getPkg().getPackageName(), - staticSharedLibLatestVersionSetting); - } - } } catch (PackageManagerException e) { request.mInstallResult.setError("Scanning Failed.", e); return; } } - ReconcileRequest - reconcileRequest = new ReconcileRequest(preparedScans, installArgs, - installResults, - prepareResults, - mSharedLibraries.getAll(), - Collections.unmodifiableMap(mPm.mPackages), versionInfos, - lastStaticSharedLibSettings); + ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs, + installResults, prepareResults, + Collections.unmodifiableMap(mPm.mPackages), versionInfos); CommitRequest commitRequest = null; synchronized (mPm.mLock) { Map reconciledPackages; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); reconciledPackages = ReconcilePackageUtils.reconcilePackages( - reconcileRequest, mPm.mSettings.getKeySetManagerService(), - mPm.mInjector); + reconcileRequest, mSharedLibraries, + mPm.mSettings.getKeySetManagerService()); } catch (ReconcileFailure e) { for (InstallRequest request : requests) { request.mInstallResult.setError("Reconciliation failed...", e); @@ -3586,15 +3571,12 @@ final class InstallPackageHelper { final String pkgName = scanResult.mPkgSetting.getPackageName(); final ReconcileRequest reconcileRequest = new ReconcileRequest( Collections.singletonMap(pkgName, scanResult), - mSharedLibraries.getAll(), mPm.mPackages, + mPm.mPackages, Collections.singletonMap(pkgName, - mPm.getSettingsVersionForPackage(parsedPackage)), - Collections.singletonMap(pkgName, - mSharedLibraries.getStaticSharedLibLatestVersionSetting( - scanResult))); + mPm.getSettingsVersionForPackage(parsedPackage))); final Map reconcileResult = ReconcilePackageUtils.reconcilePackages(reconcileRequest, - mPm.mSettings.getKeySetManagerService(), mPm.mInjector); + mSharedLibraries, mPm.mSettings.getKeySetManagerService()); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked(reconcileResult.get(pkgName), mPm.mUserManager.getUserIds()); diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 7d2aa535d5b9a..67f6b123d99bd 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -42,8 +42,8 @@ import java.util.Map; final class ReconcilePackageUtils { public static Map reconcilePackages( - final ReconcileRequest request, KeySetManagerService ksms, - PackageManagerServiceInjector injector) + final ReconcileRequest request, SharedLibrariesImpl sharedLibraries, + KeySetManagerService ksms) throws ReconcileFailure { final Map scannedPackages = request.mScannedPackages; @@ -67,8 +67,7 @@ final class ReconcilePackageUtils { // in the first pass, we'll build up the set of incoming shared libraries final List allowedSharedLibInfos = - SharedLibraryUtils.getAllowedSharedLibInfos(scanResult, - request.mSharedLibrarySource); + sharedLibraries.getAllowedSharedLibInfos(scanResult); if (allowedSharedLibInfos != null) { for (SharedLibraryInfo info : allowedSharedLibInfos) { if (!SharedLibraryUtils.addSharedLibraryToPackageVersionMap( @@ -113,7 +112,8 @@ final class ReconcilePackageUtils { final PackageSetting disabledPkgSetting = scanResult.mRequest.mDisabledPkgSetting; final PackageSetting lastStaticSharedLibSetting = - request.mLastStaticSharedLibSettings.get(installPackageName); + scanResult.mStaticSharedLibraryInfo == null ? null + : sharedLibraries.getStaticSharedLibLatestVersionSetting(scanResult); final PackageSetting signatureCheckPs = (prepareResult != null && lastStaticSharedLibSetting != null) ? lastStaticSharedLibSetting @@ -264,11 +264,9 @@ final class ReconcilePackageUtils { } try { result.get(installPackageName).mCollectedSharedLibraryInfos = - SharedLibraryUtils.collectSharedLibraryInfos( - scanResult.mRequest.mParsedPackage, - combinedPackages, request.mSharedLibrarySource, - incomingSharedLibraries, injector.getCompatibility()); - + sharedLibraries.collectSharedLibraryInfos( + scanResult.mRequest.mParsedPackage, combinedPackages, + incomingSharedLibraries); } catch (PackageManagerException e) { throw new ReconcileFailure(e.error, e.getMessage()); } diff --git a/services/core/java/com/android/server/pm/ReconcileRequest.java b/services/core/java/com/android/server/pm/ReconcileRequest.java index 31881388e6ec0..9e4e986d1fb55 100644 --- a/services/core/java/com/android/server/pm/ReconcileRequest.java +++ b/services/core/java/com/android/server/pm/ReconcileRequest.java @@ -16,10 +16,7 @@ package com.android.server.pm; -import android.content.pm.SharedLibraryInfo; - import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.utils.WatchedLongSparseArray; import java.util.Collections; import java.util.Map; @@ -37,38 +34,29 @@ final class ReconcileRequest { public final Map mScannedPackages; public final Map mAllPackages; - public final Map> mSharedLibrarySource; public final Map mInstallArgs; public final Map mInstallResults; public final Map mPreparedPackages; public final Map mVersionInfos; - public final Map mLastStaticSharedLibSettings; ReconcileRequest(Map scannedPackages, Map installArgs, Map installResults, Map preparedPackages, - Map> sharedLibrarySource, Map allPackages, - Map versionInfos, - Map lastStaticSharedLibSettings) { + Map versionInfos) { mScannedPackages = scannedPackages; mInstallArgs = installArgs; mInstallResults = installResults; mPreparedPackages = preparedPackages; - mSharedLibrarySource = sharedLibrarySource; mAllPackages = allPackages; mVersionInfos = versionInfos; - mLastStaticSharedLibSettings = lastStaticSharedLibSettings; } ReconcileRequest(Map scannedPackages, - Map> sharedLibrarySource, Map allPackages, - Map versionInfos, - Map lastStaticSharedLibSettings) { + Map versionInfos) { this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(), - Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos, - lastStaticSharedLibSettings); + Collections.emptyMap(), allPackages, versionInfos); } } diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java index 2335cc1845b5e..aa230508287ec 100644 --- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java +++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java @@ -16,19 +16,27 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; + import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.PackageManagerService.TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; +import android.content.pm.Signature; +import android.content.pm.SigningDetails; import android.content.pm.VersionedPackage; +import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.os.storage.StorageManager; import android.service.pm.PackageServiceDumpProto; import android.util.ArraySet; +import android.util.PackageUtils; import android.util.Pair; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -36,8 +44,10 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.server.SystemConfig; +import com.android.server.compat.PlatformCompat; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.utils.Snappable; import com.android.server.utils.SnapshotCache; import com.android.server.utils.Watchable; @@ -47,10 +57,13 @@ import com.android.server.utils.WatchedArrayMap; import com.android.server.utils.WatchedLongSparseArray; import com.android.server.utils.Watcher; +import libcore.util.HexEncoding; + import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; @@ -62,6 +75,24 @@ import java.util.function.BiConsumer; * Current known shared libraries on the device. */ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable, Snappable { + private static final boolean DEBUG_SHARED_LIBRARIES = false; + + /** + * Apps targeting Android S and above need to declare dependencies to the public native + * shared libraries that are defined by the device maker using {@code uses-native-library} tag + * in its {@code AndroidManifest.xml}. + * + * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist, + * the package manager rejects to install the app. The dependency can be specified as optional + * using {@code android:required} attribute in the tag, in which case failing to satisfy the + * dependency doesn't stop the installation. + *

Once installed, an app is provided with only the native shared libraries that are + * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear + * in the app manifest will fail even if it actually exists on the device. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088; // TODO(b/200588896): remove PMS dependency private final PackageManagerService mPm; @@ -493,10 +524,8 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting, @NonNull Map availablePackages) throws PackageManagerException { - final ArrayList sharedLibraryInfos = - SharedLibraryUtils.collectSharedLibraryInfos( - pkgSetting.getPkg(), availablePackages, mSharedLibraries, - null /* newLibraries */, mInjector.getCompatibility()); + final ArrayList sharedLibraryInfos = collectSharedLibraryInfos( + pkgSetting.getPkg(), availablePackages, null /* newLibraries */); executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting, sharedLibraryInfos, mPm.mUserManager.getUserIds()); } @@ -734,6 +763,234 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable return true; } + /** + * Compare the newly scanned package with current system state to see which of its declared + * shared libraries should be allowed to be added to the system. + */ + List getAllowedSharedLibInfos(ScanResult scanResult) { + // Let's used the parsed package as scanResult.pkgSetting may be null + final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage; + if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null + && scanResult.mDynamicSharedLibraryInfos == null) { + return null; + } + + // Any app can add new SDKs and static shared libraries. + if (scanResult.mSdkSharedLibraryInfo != null) { + return Collections.singletonList(scanResult.mSdkSharedLibraryInfo); + } + if (scanResult.mStaticSharedLibraryInfo != null) { + return Collections.singletonList(scanResult.mStaticSharedLibraryInfo); + } + final boolean hasDynamicLibraries = parsedPackage.isSystem() + && scanResult.mDynamicSharedLibraryInfos != null; + if (!hasDynamicLibraries) { + return null; + } + final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState() + .isUpdatedSystemApp(); + // We may not yet have disabled the updated package yet, so be sure to grab the + // current setting if that's the case. + final PackageSetting updatedSystemPs = isUpdatedSystemApp + ? scanResult.mRequest.mDisabledPkgSetting == null + ? scanResult.mRequest.mOldPkgSetting + : scanResult.mRequest.mDisabledPkgSetting + : null; + if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null + || updatedSystemPs.getPkg().getLibraryNames() == null)) { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " declares libraries that are not declared on the system image; skipping"); + return null; + } + final ArrayList infos = + new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size()); + for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) { + final String name = info.getName(); + if (isUpdatedSystemApp) { + // New library entries can only be added through the + // system image. This is important to get rid of a lot + // of nasty edge cases: for example if we allowed a non- + // system update of the app to add a library, then uninstalling + // the update would make the library go away, and assumptions + // we made such as through app install filtering would now + // have allowed apps on the device which aren't compatible + // with it. Better to just have the restriction here, be + // conservative, and create many fewer cases that can negatively + // impact the user experience. + if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + + " declares library " + name + + " that is not declared on system image; skipping"); + continue; + } + } + synchronized (mPm.mLock) { + if (getSharedLibraryInfo(name, SharedLibraryInfo.VERSION_UNDEFINED) != null) { + Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library " + + name + " that already exists; skipping"); + continue; + } + } + infos.add(info); + } + return infos; + } + + /** + * Collects shared library infos that are being used by the given package. + * + * @param pkg The package using shared libraries. + * @param availablePackages The available packages which are installed and being installed, + * @param newLibraries Shared libraries defined by packages which are being installed. + * @return A list of shared library infos + */ + ArrayList collectSharedLibraryInfos(@Nullable AndroidPackage pkg, + @NonNull Map availablePackages, + @Nullable final Map> newLibraries) + throws PackageManagerException { + if (pkg == null) { + return null; + } + final PlatformCompat platformCompat = mInjector.getCompatibility(); + // The collection used here must maintain the order of addition (so + // that libraries are searched in the correct order) and must have no + // duplicates. + ArrayList usesLibraryInfos = null; + if (!pkg.getUsesLibraries().isEmpty()) { + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, + pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null, + availablePackages, newLibraries); + } + if (!pkg.getUsesStaticLibraries().isEmpty()) { + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(), + pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(), + pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(), + usesLibraryInfos, availablePackages, newLibraries); + } + if (!pkg.getUsesOptionalLibraries().isEmpty()) { + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null, + pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(), + usesLibraryInfos, availablePackages, newLibraries); + } + if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES, + pkg.getPackageName(), pkg.getTargetSdkVersion())) { + if (!pkg.getUsesNativeLibraries().isEmpty()) { + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null, + null, pkg.getPackageName(), "native shared", true, + pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, + newLibraries); + } + if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) { + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(), + null, null, pkg.getPackageName(), "native shared", false, + pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, + newLibraries); + } + } + if (!pkg.getUsesSdkLibraries().isEmpty()) { + usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(), + pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(), + pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos, + availablePackages, newLibraries); + } + return usesLibraryInfos; + } + + private ArrayList collectSharedLibraryInfos( + @NonNull List requestedLibraries, + @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, + @NonNull String packageName, @NonNull String libraryType, boolean required, + int targetSdk, @Nullable ArrayList outUsedLibraries, + @NonNull final Map availablePackages, + @Nullable final Map> newLibraries) + throws PackageManagerException { + final int libCount = requestedLibraries.size(); + for (int i = 0; i < libCount; i++) { + final String libName = requestedLibraries.get(i); + final long libVersion = requiredVersions != null ? requiredVersions[i] + : SharedLibraryInfo.VERSION_UNDEFINED; + final SharedLibraryInfo libraryInfo; + synchronized (mPm.mLock) { + libraryInfo = SharedLibraryUtils.getSharedLibraryInfo( + libName, libVersion, mSharedLibraries, newLibraries); + } + if (libraryInfo == null) { + if (required) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires unavailable " + libraryType + + " library " + libName + "; failing!"); + } else if (DEBUG_SHARED_LIBRARIES) { + Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType + + " library " + libName + "; ignoring!"); + } + } else { + if (requiredVersions != null && requiredCertDigests != null) { + if (libraryInfo.getLongVersion() != requiredVersions[i]) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires unavailable " + libraryType + + " library " + libName + " version " + + libraryInfo.getLongVersion() + "; failing!"); + } + AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName()); + SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails(); + if (libPkg == null) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires unavailable " + libraryType + + " library; failing!"); + } + final String[] expectedCertDigests = requiredCertDigests[i]; + if (expectedCertDigests.length > 1) { + // For apps targeting O MR1 we require explicit enumeration of all certs. + final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1) + ? PackageUtils.computeSignaturesSha256Digests( + libPkg.getSignatures()) + : PackageUtils.computeSignaturesSha256Digests( + new Signature[]{libPkg.getSignatures()[0]}); + + // Take a shortcut if sizes don't match. Note that if an app doesn't + // target O we don't parse the "additional-certificate" tags similarly + // how we only consider all certs only for apps targeting O (see above). + // Therefore, the size check is safe to make. + if (expectedCertDigests.length != libCertDigests.length) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires differently signed " + + libraryType + " library; failing!"); + } + + // Use a predictable order as signature order may vary + Arrays.sort(libCertDigests); + Arrays.sort(expectedCertDigests); + + final int certCount = libCertDigests.length; + for (int j = 0; j < certCount; j++) { + if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) { + throw new PackageManagerException( + INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires differently signed " + + libraryType + " library; failing!"); + } + } + } else { + // lib signing cert could have rotated beyond the one expected, check to see + // if the new one has been blessed by the old + byte[] digestBytes = HexEncoding.decode( + expectedCertDigests[0], false /* allowSingleChar */); + if (!libPkg.hasSha256Certificate(digestBytes)) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, + "Package " + packageName + " requires differently signed " + + libraryType + " library; failing!"); + } + } + } + if (outUsedLibraries == null) { + outUsedLibraries = new ArrayList<>(); + } + outUsedLibraries.add(libraryInfo); + } + } + return outUsedLibraries; + } + /** * Dump all shared libraries. */ diff --git a/services/core/java/com/android/server/pm/SharedLibraryUtils.java b/services/core/java/com/android/server/pm/SharedLibraryUtils.java index 5259c58aa2e3c..274870dad6f06 100644 --- a/services/core/java/com/android/server/pm/SharedLibraryUtils.java +++ b/services/core/java/com/android/server/pm/SharedLibraryUtils.java @@ -16,31 +16,13 @@ package com.android.server.pm; -import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY; - -import static com.android.server.pm.PackageManagerService.TAG; - -import android.annotation.NonNull; import android.annotation.Nullable; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.content.pm.SharedLibraryInfo; -import android.content.pm.Signature; -import android.content.pm.SigningDetails; -import android.os.Build; -import android.util.PackageUtils; -import android.util.Slog; -import com.android.server.compat.PlatformCompat; -import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.utils.WatchedLongSparseArray; -import libcore.util.HexEncoding; - import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -48,104 +30,6 @@ import java.util.Map; import java.util.Set; final class SharedLibraryUtils { - private static final boolean DEBUG_SHARED_LIBRARIES = false; - - /** - * Apps targeting Android S and above need to declare dependencies to the public native - * shared libraries that are defined by the device maker using {@code uses-native-library} tag - * in its {@code AndroidManifest.xml}. - * - * If any of the dependencies cannot be satisfied, i.e. one of the dependency doesn't exist, - * the package manager rejects to install the app. The dependency can be specified as optional - * using {@code android:required} attribute in the tag, in which case failing to satisfy the - * dependency doesn't stop the installation. - *

Once installed, an app is provided with only the native shared libraries that are - * specified in the app manifest. {@code dlopen}ing a native shared library that doesn't appear - * in the app manifest will fail even if it actually exists on the device. - */ - @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) - private static final long ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES = 142191088; - - /** - * Compare the newly scanned package with current system state to see which of its declared - * shared libraries should be allowed to be added to the system. - */ - public static List getAllowedSharedLibInfos( - ScanResult scanResult, - Map> existingSharedLibraries) { - // Let's used the parsed package as scanResult.pkgSetting may be null - final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage; - if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null - && scanResult.mDynamicSharedLibraryInfos == null) { - return null; - } - - // Any app can add new SDKs and static shared libraries. - if (scanResult.mSdkSharedLibraryInfo != null) { - return Collections.singletonList(scanResult.mSdkSharedLibraryInfo); - } - if (scanResult.mStaticSharedLibraryInfo != null) { - return Collections.singletonList(scanResult.mStaticSharedLibraryInfo); - } - final boolean hasDynamicLibraries = parsedPackage.isSystem() - && scanResult.mDynamicSharedLibraryInfos != null; - if (!hasDynamicLibraries) { - return null; - } - final boolean isUpdatedSystemApp = scanResult.mPkgSetting.getPkgState() - .isUpdatedSystemApp(); - // We may not yet have disabled the updated package yet, so be sure to grab the - // current setting if that's the case. - final PackageSetting updatedSystemPs = isUpdatedSystemApp - ? scanResult.mRequest.mDisabledPkgSetting == null - ? scanResult.mRequest.mOldPkgSetting - : scanResult.mRequest.mDisabledPkgSetting - : null; - if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null - || updatedSystemPs.getPkg().getLibraryNames() == null)) { - Slog.w(TAG, "Package " + parsedPackage.getPackageName() - + " declares libraries that are not declared on the system image; skipping"); - return null; - } - final ArrayList infos = - new ArrayList<>(scanResult.mDynamicSharedLibraryInfos.size()); - for (SharedLibraryInfo info : scanResult.mDynamicSharedLibraryInfos) { - final String name = info.getName(); - if (isUpdatedSystemApp) { - // New library entries can only be added through the - // system image. This is important to get rid of a lot - // of nasty edge cases: for example if we allowed a non- - // system update of the app to add a library, then uninstalling - // the update would make the library go away, and assumptions - // we made such as through app install filtering would now - // have allowed apps on the device which aren't compatible - // with it. Better to just have the restriction here, be - // conservative, and create many fewer cases that can negatively - // impact the user experience. - if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) { - Slog.w(TAG, "Package " + parsedPackage.getPackageName() - + " declares library " + name - + " that is not declared on system image; skipping"); - continue; - } - } - if (sharedLibExists( - name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) { - Slog.w(TAG, "Package " + parsedPackage.getPackageName() + " declares library " - + name + " that already exists; skipping"); - continue; - } - infos.add(info); - } - return infos; - } - - public static boolean sharedLibExists(final String name, final long version, - Map> librarySource) { - WatchedLongSparseArray versionedLib = librarySource.get(name); - return versionedLib != null && versionedLib.indexOfKey(version) >= 0; - } /** * Returns false if the adding shared library already exists in the map and so could not be @@ -170,151 +54,6 @@ final class SharedLibraryUtils { return true; } - public static ArrayList collectSharedLibraryInfos(AndroidPackage pkg, - Map availablePackages, - @NonNull final Map> existingLibraries, - @Nullable final Map> newLibraries, - PlatformCompat platformCompat) throws PackageManagerException { - if (pkg == null) { - return null; - } - // The collection used here must maintain the order of addition (so - // that libraries are searched in the correct order) and must have no - // duplicates. - ArrayList usesLibraryInfos = null; - if (!pkg.getUsesLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, - pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null, - availablePackages, existingLibraries, newLibraries); - } - if (!pkg.getUsesStaticLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(), - pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(), - pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(), - usesLibraryInfos, availablePackages, existingLibraries, newLibraries); - } - if (!pkg.getUsesOptionalLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null, - pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(), - usesLibraryInfos, availablePackages, existingLibraries, newLibraries); - } - if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES, - pkg.getPackageName(), pkg.getTargetSdkVersion())) { - if (!pkg.getUsesNativeLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null, - null, pkg.getPackageName(), "native shared", true, - pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, - existingLibraries, newLibraries); - } - if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(), - null, null, pkg.getPackageName(), "native shared", false, - pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, - existingLibraries, newLibraries); - } - } - if (!pkg.getUsesSdkLibraries().isEmpty()) { - usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(), - pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(), - pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos, - availablePackages, existingLibraries, newLibraries); - } - return usesLibraryInfos; - } - - public static ArrayList collectSharedLibraryInfos( - @NonNull List requestedLibraries, - @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, - @NonNull String packageName, @NonNull String libraryType, boolean required, - int targetSdk, @Nullable ArrayList outUsedLibraries, - @NonNull final Map availablePackages, - @NonNull final Map> existingLibraries, - @Nullable final Map> newLibraries) - throws PackageManagerException { - final int libCount = requestedLibraries.size(); - for (int i = 0; i < libCount; i++) { - final String libName = requestedLibraries.get(i); - final long libVersion = requiredVersions != null ? requiredVersions[i] - : SharedLibraryInfo.VERSION_UNDEFINED; - final SharedLibraryInfo libraryInfo = - getSharedLibraryInfo(libName, libVersion, existingLibraries, newLibraries); - if (libraryInfo == null) { - if (required) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires unavailable " + libraryType - + " library " + libName + "; failing!"); - } else if (DEBUG_SHARED_LIBRARIES) { - Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType - + " library " + libName + "; ignoring!"); - } - } else { - if (requiredVersions != null && requiredCertDigests != null) { - if (libraryInfo.getLongVersion() != requiredVersions[i]) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires unavailable " + libraryType - + " library " + libName + " version " - + libraryInfo.getLongVersion() + "; failing!"); - } - AndroidPackage pkg = availablePackages.get(libraryInfo.getPackageName()); - SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails(); - if (libPkg == null) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires unavailable " + libraryType - + " library; failing!"); - } - final String[] expectedCertDigests = requiredCertDigests[i]; - if (expectedCertDigests.length > 1) { - // For apps targeting O MR1 we require explicit enumeration of all certs. - final String[] libCertDigests = (targetSdk >= Build.VERSION_CODES.O_MR1) - ? PackageUtils.computeSignaturesSha256Digests( - libPkg.getSignatures()) - : PackageUtils.computeSignaturesSha256Digests( - new Signature[]{libPkg.getSignatures()[0]}); - - // Take a shortcut if sizes don't match. Note that if an app doesn't - // target O we don't parse the "additional-certificate" tags similarly - // how we only consider all certs only for apps targeting O (see above). - // Therefore, the size check is safe to make. - if (expectedCertDigests.length != libCertDigests.length) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires differently signed " - + libraryType + " library; failing!"); - } - - // Use a predictable order as signature order may vary - Arrays.sort(libCertDigests); - Arrays.sort(expectedCertDigests); - - final int certCount = libCertDigests.length; - for (int j = 0; j < certCount; j++) { - if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) { - throw new PackageManagerException( - INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires differently signed " - + libraryType + " library; failing!"); - } - } - } else { - // lib signing cert could have rotated beyond the one expected, check to see - // if the new one has been blessed by the old - byte[] digestBytes = HexEncoding.decode( - expectedCertDigests[0], false /* allowSingleChar */); - if (!libPkg.hasSha256Certificate(digestBytes)) { - throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY, - "Package " + packageName + " requires differently signed " - + libraryType + " library; failing!"); - } - } - } - if (outUsedLibraries == null) { - outUsedLibraries = new ArrayList<>(); - } - outUsedLibraries.add(libraryInfo); - } - } - return outUsedLibraries; - } - @Nullable public static SharedLibraryInfo getSharedLibraryInfo(String name, long version, Map> existingLibraries, diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt index f30532123d5b7..6de12cb987198 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt @@ -16,6 +16,7 @@ package com.android.server.pm +import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.content.pm.SharedLibraryInfo import android.content.pm.VersionedPackage @@ -32,6 +33,7 @@ import com.android.server.pm.parsing.pkg.PackageImpl import com.android.server.pm.parsing.pkg.ParsedPackage import com.android.server.testutils.any import com.android.server.testutils.eq +import com.android.server.testutils.mock import com.android.server.testutils.nullable import com.android.server.testutils.spy import com.android.server.testutils.whenever @@ -279,6 +281,40 @@ class SharedLibrariesImplTest { assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME)) } + @Test + fun getAllowedSharedLibInfos_withStaticSharedLibInfo() { + val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L) + val scanResult = ScanResult(mock(), true, null, null, + false, 0, null, testInfo, null) + + val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult) + + assertThat(allowedInfos).hasSize(1) + assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME) + } + + @Test + fun getAllowedSharedLibInfos_withDynamicSharedLibInfo() { + val testInfo = libOfDynamic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME) + val pair = createBasicAndroidPackage( + TEST_LIB_PACKAGE_NAME, 10L, libraries = arrayOf(TEST_LIB_NAME)) + val parsedPackage = pair.second.apply { + isSystem = true + } as ParsedPackage + val packageSetting = mRule.system() + .createBasicSettingBuilder(pair.first.parentFile, parsedPackage.hideAsFinal()) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM).build() + val scanRequest = ScanRequest(parsedPackage, null, null, null, + null, null, null, 0, 0, false, null, null) + val scanResult = ScanResult(scanRequest, true, packageSetting, null, + false, 0, null, null, listOf(testInfo)) + + val allowedInfos = mSharedLibrariesImpl.getAllowedSharedLibInfos(scanResult) + + assertThat(allowedInfos).hasSize(1) + assertThat(allowedInfos[0].name).isEqualTo(TEST_LIB_NAME) + } + private fun addExistingPackages() { // add a dynamic shared library that is using the builtin library addPackage(DYNAMIC_LIB_PACKAGE_NAME, 1L,