Move shared library info out of PackageManagerService (3/n)

- Moving more functions into SharedLibrariesImpl in order to reduce
  the usage of SharedLibrariesImpl#getAll. This is to prevent the
  map of shared libraries from being manipulated directly.

- Move shared libraries out of the ReconcileRequest. Using
  SharedLibrariesImpl to get infos instead.

Bug: 200588896
Test: atest StaticSharedLibsHostTests
Test: atest SharedLibrariesImplTest
Change-Id: I761a9e5c00d0e2a91aeee23d7587f8f6d6274299
This commit is contained in:
Rhed Jao
2022-01-04 19:42:32 +08:00
parent ae838fdc87
commit 35276de543
6 changed files with 316 additions and 316 deletions

View File

@@ -894,8 +894,6 @@ final class InstallPackageHelper {
final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
final Map<String, PackageSetting> lastStaticSharedLibSettings =
new ArrayMap<>(requests.size());
final Map<String, Boolean> 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<String, ReconciledPackage> 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<String, ReconciledPackage> reconcileResult =
ReconcilePackageUtils.reconcilePackages(reconcileRequest,
mPm.mSettings.getKeySetManagerService(), mPm.mInjector);
mSharedLibraries, mPm.mSettings.getKeySetManagerService());
appIdCreated = optimisticallyRegisterAppId(scanResult);
commitReconciledScanResultLocked(reconcileResult.get(pkgName),
mPm.mUserManager.getUserIds());

View File

@@ -42,8 +42,8 @@ import java.util.Map;
final class ReconcilePackageUtils {
public static Map<String, ReconciledPackage> reconcilePackages(
final ReconcileRequest request, KeySetManagerService ksms,
PackageManagerServiceInjector injector)
final ReconcileRequest request, SharedLibrariesImpl sharedLibraries,
KeySetManagerService ksms)
throws ReconcileFailure {
final Map<String, ScanResult> 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<SharedLibraryInfo> 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());
}

View File

@@ -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<String, ScanResult> mScannedPackages;
public final Map<String, AndroidPackage> mAllPackages;
public final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> mSharedLibrarySource;
public final Map<String, InstallArgs> mInstallArgs;
public final Map<String, PackageInstalledInfo> mInstallResults;
public final Map<String, PrepareResult> mPreparedPackages;
public final Map<String, Settings.VersionInfo> mVersionInfos;
public final Map<String, PackageSetting> mLastStaticSharedLibSettings;
ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, InstallArgs> installArgs,
Map<String, PackageInstalledInfo> installResults,
Map<String, PrepareResult> preparedPackages,
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos,
Map<String, PackageSetting> lastStaticSharedLibSettings) {
Map<String, Settings.VersionInfo> versionInfos) {
mScannedPackages = scannedPackages;
mInstallArgs = installArgs;
mInstallResults = installResults;
mPreparedPackages = preparedPackages;
mSharedLibrarySource = sharedLibrarySource;
mAllPackages = allPackages;
mVersionInfos = versionInfos;
mLastStaticSharedLibSettings = lastStaticSharedLibSettings;
}
ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibrarySource,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos,
Map<String, PackageSetting> lastStaticSharedLibSettings) {
Map<String, Settings.VersionInfo> versionInfos) {
this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), sharedLibrarySource, allPackages, versionInfos,
lastStaticSharedLibSettings);
Collections.emptyMap(), allPackages, versionInfos);
}
}

View File

@@ -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.
* <p>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<String, AndroidPackage> availablePackages)
throws PackageManagerException {
final ArrayList<SharedLibraryInfo> sharedLibraryInfos =
SharedLibraryUtils.collectSharedLibraryInfos(
pkgSetting.getPkg(), availablePackages, mSharedLibraries,
null /* newLibraries */, mInjector.getCompatibility());
final ArrayList<SharedLibraryInfo> 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<SharedLibraryInfo> 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<SharedLibraryInfo> 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<SharedLibraryInfo> collectSharedLibraryInfos(@Nullable AndroidPackage pkg,
@NonNull Map<String, AndroidPackage> availablePackages,
@Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> 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<SharedLibraryInfo> 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<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@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)
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.
*/

View File

@@ -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.
* <p>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<SharedLibraryInfo> getAllowedSharedLibInfos(
ScanResult scanResult,
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> 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<SharedLibraryInfo> 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<String, WatchedLongSparseArray<SharedLibraryInfo>> librarySource) {
WatchedLongSparseArray<SharedLibraryInfo> 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<SharedLibraryInfo> collectSharedLibraryInfos(AndroidPackage pkg,
Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> 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<SharedLibraryInfo> 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<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
@NonNull String packageName, @NonNull String libraryType, boolean required,
int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> 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<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,

View File

@@ -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,