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:
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user