Merge changes I761a9e5c,I7f03720f
* changes: Move shared library info out of PackageManagerService (3/n) Move shared library info out of PackageManagerService (2/n)
This commit is contained in:
@@ -4788,7 +4788,7 @@ public class ComputerEngine implements Computer {
|
||||
@Override
|
||||
public List<PackageStateInternal> findSharedNonSystemLibraries(
|
||||
@NonNull PackageStateInternal pkgSetting) {
|
||||
List<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
|
||||
List<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
|
||||
if (!deps.isEmpty()) {
|
||||
List<PackageStateInternal> retValue = new ArrayList<>();
|
||||
for (SharedLibraryInfo info : deps) {
|
||||
|
||||
@@ -379,7 +379,7 @@ final class DexOptHelper {
|
||||
// at boot, or background job), the passed 'targetCompilerFilter' stays the same,
|
||||
// and the first package that uses the library will dexopt it. The
|
||||
// others will see that the compiled code for the library is up to date.
|
||||
Collection<SharedLibraryInfo> deps = SharedLibraryHelper.findSharedLibraries(pkgSetting);
|
||||
Collection<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting);
|
||||
final String[] instructionSets = getAppDexInstructionSets(
|
||||
AndroidPackageUtils.getPrimaryCpuAbi(p, pkgSetting),
|
||||
AndroidPackageUtils.getSecondaryCpuAbi(p, pkgSetting));
|
||||
|
||||
@@ -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,11 +67,10 @@ final class ReconcilePackageUtils {
|
||||
|
||||
// in the first pass, we'll build up the set of incoming shared libraries
|
||||
final List<SharedLibraryInfo> allowedSharedLibInfos =
|
||||
SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
|
||||
request.mSharedLibrarySource);
|
||||
sharedLibraries.getAllowedSharedLibInfos(scanResult);
|
||||
if (allowedSharedLibInfos != null) {
|
||||
for (SharedLibraryInfo info : allowedSharedLibInfos) {
|
||||
if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
|
||||
if (!SharedLibraryUtils.addSharedLibraryToPackageVersionMap(
|
||||
incomingSharedLibraries, info)) {
|
||||
throw new ReconcileFailure("Shared Library " + info.getName()
|
||||
+ " is being installed twice in this set!");
|
||||
@@ -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 =
|
||||
SharedLibraryHelper.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 =
|
||||
SharedLibraryHelper.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.
|
||||
*/
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
|
||||
|
||||
import 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;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
final class SharedLibraryHelper {
|
||||
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
|
||||
* added.
|
||||
*/
|
||||
public static boolean addSharedLibraryToPackageVersionMap(
|
||||
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
|
||||
SharedLibraryInfo library) {
|
||||
final String name = library.getName();
|
||||
if (target.containsKey(name)) {
|
||||
if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
|
||||
// We've already added this non-version-specific library to the map.
|
||||
return false;
|
||||
} else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
|
||||
// We've already added this version of a version-specific library to the map.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
target.put(name, new WatchedLongSparseArray<>());
|
||||
}
|
||||
target.get(name).put(library.getLongVersion(), library);
|
||||
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,
|
||||
@Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
|
||||
if (newLibraries != null) {
|
||||
final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
|
||||
SharedLibraryInfo info = null;
|
||||
if (versionedLib != null) {
|
||||
info = versionedLib.get(version);
|
||||
}
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
|
||||
if (versionedLib == null) {
|
||||
return null;
|
||||
}
|
||||
return versionedLib.get(version);
|
||||
}
|
||||
|
||||
public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
|
||||
if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
|
||||
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
|
||||
Set<String> collectedNames = new HashSet<>();
|
||||
for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
|
||||
findSharedLibrariesRecursive(info, retValue, collectedNames);
|
||||
}
|
||||
return retValue;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
|
||||
ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
|
||||
if (!collectedNames.contains(info.getName())) {
|
||||
collectedNames.add(info.getName());
|
||||
collected.add(info);
|
||||
|
||||
if (info.getDependencies() != null) {
|
||||
for (SharedLibraryInfo dep : info.getDependencies()) {
|
||||
findSharedLibrariesRecursive(dep, collected, collectedNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
105
services/core/java/com/android/server/pm/SharedLibraryUtils.java
Normal file
105
services/core/java/com/android/server/pm/SharedLibraryUtils.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.SharedLibraryInfo;
|
||||
|
||||
import com.android.server.pm.pkg.PackageStateInternal;
|
||||
import com.android.server.utils.WatchedLongSparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
final class SharedLibraryUtils {
|
||||
|
||||
/**
|
||||
* Returns false if the adding shared library already exists in the map and so could not be
|
||||
* added.
|
||||
*/
|
||||
public static boolean addSharedLibraryToPackageVersionMap(
|
||||
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> target,
|
||||
SharedLibraryInfo library) {
|
||||
final String name = library.getName();
|
||||
if (target.containsKey(name)) {
|
||||
if (library.getType() != SharedLibraryInfo.TYPE_STATIC) {
|
||||
// We've already added this non-version-specific library to the map.
|
||||
return false;
|
||||
} else if (target.get(name).indexOfKey(library.getLongVersion()) >= 0) {
|
||||
// We've already added this version of a version-specific library to the map.
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
target.put(name, new WatchedLongSparseArray<>());
|
||||
}
|
||||
target.get(name).put(library.getLongVersion(), library);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static SharedLibraryInfo getSharedLibraryInfo(String name, long version,
|
||||
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
|
||||
@Nullable Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries) {
|
||||
if (newLibraries != null) {
|
||||
final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = newLibraries.get(name);
|
||||
SharedLibraryInfo info = null;
|
||||
if (versionedLib != null) {
|
||||
info = versionedLib.get(version);
|
||||
}
|
||||
if (info != null) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = existingLibraries.get(name);
|
||||
if (versionedLib == null) {
|
||||
return null;
|
||||
}
|
||||
return versionedLib.get(version);
|
||||
}
|
||||
|
||||
public static List<SharedLibraryInfo> findSharedLibraries(PackageStateInternal pkgSetting) {
|
||||
if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
|
||||
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
|
||||
Set<String> collectedNames = new HashSet<>();
|
||||
for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
|
||||
findSharedLibrariesRecursive(info, retValue, collectedNames);
|
||||
}
|
||||
return retValue;
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private static void findSharedLibrariesRecursive(SharedLibraryInfo info,
|
||||
ArrayList<SharedLibraryInfo> collected, Set<String> collectedNames) {
|
||||
if (!collectedNames.contains(info.getName())) {
|
||||
collectedNames.add(info.getName());
|
||||
collected.add(info);
|
||||
|
||||
if (info.getDependencies() != null) {
|
||||
for (SharedLibraryInfo dep : info.getDependencies()) {
|
||||
findSharedLibrariesRecursive(dep, collected, collectedNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -74,6 +74,7 @@ import com.android.server.testutils.mock
|
||||
import com.android.server.testutils.nullable
|
||||
import com.android.server.testutils.whenever
|
||||
import com.android.server.utils.WatchedArrayMap
|
||||
import libcore.util.HexEncoding
|
||||
import org.junit.Assert
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
@@ -140,6 +141,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
|
||||
.mockStatic(EventLog::class.java)
|
||||
.mockStatic(LocalServices::class.java)
|
||||
.mockStatic(DeviceConfig::class.java)
|
||||
.mockStatic(HexEncoding::class.java)
|
||||
.apply(withSession)
|
||||
session = apply.startMocking()
|
||||
whenever(mocks.settings.insertPackageSettingLPw(
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.SharedLibraryInfo
|
||||
import android.content.pm.VersionedPackage
|
||||
import android.os.Build
|
||||
import android.os.storage.StorageManager
|
||||
import android.util.ArrayMap
|
||||
import android.util.PackageUtils
|
||||
import com.android.server.SystemConfig.SharedLibraryEntry
|
||||
import com.android.server.compat.PlatformCompat
|
||||
import com.android.server.extendedtestutils.wheneverStatic
|
||||
import com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage
|
||||
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
|
||||
import com.android.server.utils.WatchedLongSparseArray
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import libcore.util.HexEncoding
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.doAnswer
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import java.io.File
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(JUnit4::class)
|
||||
class SharedLibrariesImplTest {
|
||||
|
||||
companion object {
|
||||
const val TEST_LIB_NAME = "test.lib"
|
||||
const val TEST_LIB_PACKAGE_NAME = "com.android.lib.test"
|
||||
const val BUILTIN_LIB_NAME = "builtin.lib"
|
||||
const val STATIC_LIB_NAME = "static.lib"
|
||||
const val STATIC_LIB_VERSION = 7L
|
||||
const val STATIC_LIB_PACKAGE_NAME = "com.android.lib.static.provider"
|
||||
const val DYNAMIC_LIB_NAME = "dynamic.lib"
|
||||
const val DYNAMIC_LIB_PACKAGE_NAME = "com.android.lib.dynamic.provider"
|
||||
const val CONSUMER_PACKAGE_NAME = "com.android.lib.consumer"
|
||||
const val VERSION_UNDEFINED = SharedLibraryInfo.VERSION_UNDEFINED.toLong()
|
||||
}
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val mRule = MockSystemRule()
|
||||
|
||||
private val mExistingPackages: ArrayMap<String, AndroidPackage> = ArrayMap()
|
||||
private val mExistingSettings: MutableMap<String, PackageSetting> = mutableMapOf()
|
||||
|
||||
private lateinit var mSharedLibrariesImpl: SharedLibrariesImpl
|
||||
private lateinit var mPms: PackageManagerService
|
||||
private lateinit var mSettings: Settings
|
||||
|
||||
@Mock
|
||||
private lateinit var mDeletePackageHelper: DeletePackageHelper
|
||||
@Mock
|
||||
private lateinit var mStorageManager: StorageManager
|
||||
@Mock
|
||||
private lateinit var mFile: File
|
||||
@Mock
|
||||
private lateinit var mPlatformCompat: PlatformCompat
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
mRule.system().stageNominalSystemState()
|
||||
addExistingPackages()
|
||||
|
||||
val testParams = PackageManagerServiceTestParams().apply {
|
||||
packages = mExistingPackages
|
||||
}
|
||||
mPms = spy(PackageManagerService(mRule.mocks().injector, testParams))
|
||||
mSettings = mRule.mocks().injector.settings
|
||||
mSharedLibrariesImpl = SharedLibrariesImpl(mPms, mRule.mocks().injector)
|
||||
mSharedLibrariesImpl.setDeletePackageHelper(mDeletePackageHelper)
|
||||
addExistingSharedLibraries()
|
||||
|
||||
whenever(mSettings.getPackageLPr(any())) { mExistingSettings[arguments[0]] }
|
||||
whenever(mRule.mocks().injector.getSystemService(StorageManager::class.java))
|
||||
.thenReturn(mStorageManager)
|
||||
whenever(mStorageManager.findPathForUuid(nullable())).thenReturn(mFile)
|
||||
doAnswer { it.arguments[0] }.`when`(mPms).resolveInternalPackageNameLPr(any(), any())
|
||||
whenever(mDeletePackageHelper.deletePackageX(any(), any(), any(), any(), any()))
|
||||
.thenReturn(PackageManager.DELETE_SUCCEEDED)
|
||||
whenever(mRule.mocks().injector.compatibility).thenReturn(mPlatformCompat)
|
||||
wheneverStatic { HexEncoding.decode(STATIC_LIB_NAME, false) }
|
||||
.thenReturn(PackageUtils.computeSha256DigestBytes(
|
||||
mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
|
||||
.pkg.signingDetails.signatures!![0].toByteArray()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun snapshot_shouldSealed() {
|
||||
val builtinLibs = mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME]
|
||||
assertThat(builtinLibs).isNotNull()
|
||||
|
||||
assertFailsWith(IllegalStateException::class) {
|
||||
mSharedLibrariesImpl.snapshot().all[BUILTIN_LIB_NAME] = WatchedLongSparseArray()
|
||||
}
|
||||
assertFailsWith(IllegalStateException::class) {
|
||||
builtinLibs!!.put(VERSION_UNDEFINED, libOfBuiltin(BUILTIN_LIB_NAME))
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addBuiltInSharedLibrary() {
|
||||
mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(TEST_LIB_NAME))
|
||||
|
||||
assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME)).isNotNull()
|
||||
assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(TEST_LIB_NAME, VERSION_UNDEFINED))
|
||||
.isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addBuiltInSharedLibrary_withDuplicateLibName() {
|
||||
val duplicate = libEntry(BUILTIN_LIB_NAME, "duplicate.path")
|
||||
mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(duplicate)
|
||||
val sharedLibInfo = mSharedLibrariesImpl
|
||||
.getSharedLibraryInfo(BUILTIN_LIB_NAME, VERSION_UNDEFINED)
|
||||
|
||||
assertThat(sharedLibInfo).isNotNull()
|
||||
assertThat(sharedLibInfo!!.path).isNotEqualTo(duplicate.filename)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun commitSharedLibraryInfo_withStaticSharedLib() {
|
||||
val testInfo = libOfStatic(TEST_LIB_PACKAGE_NAME, TEST_LIB_NAME, 1L)
|
||||
mSharedLibrariesImpl.commitSharedLibraryInfoLPw(testInfo)
|
||||
val sharedLibInfos = mSharedLibrariesImpl
|
||||
.getStaticLibraryInfos(testInfo.declaringPackage.packageName)
|
||||
|
||||
assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(TEST_LIB_NAME))
|
||||
.isNotNull()
|
||||
assertThat(mSharedLibrariesImpl.getSharedLibraryInfo(testInfo.name, testInfo.longVersion))
|
||||
.isNotNull()
|
||||
assertThat(sharedLibInfos).isNotNull()
|
||||
assertThat(sharedLibInfos.get(testInfo.longVersion)).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeSharedLibrary() {
|
||||
doAnswer { mutableListOf(VersionedPackage(CONSUMER_PACKAGE_NAME, 1L)) }.`when`(mPms)
|
||||
.getPackagesUsingSharedLibrary(any(), any(), any(), any())
|
||||
val staticInfo = mSharedLibrariesImpl
|
||||
.getSharedLibraryInfo(STATIC_LIB_NAME, STATIC_LIB_VERSION)!!
|
||||
|
||||
mSharedLibrariesImpl.removeSharedLibraryLPw(STATIC_LIB_NAME, STATIC_LIB_VERSION)
|
||||
|
||||
assertThat(mSharedLibrariesImpl.getSharedLibraryInfos(STATIC_LIB_NAME)).isNull()
|
||||
assertThat(mSharedLibrariesImpl
|
||||
.getStaticLibraryInfos(staticInfo.declaringPackage.packageName)).isNull()
|
||||
verify(mExistingSettings[CONSUMER_PACKAGE_NAME]!!)
|
||||
.setOverlayPathsForLibrary(any(), nullable(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pruneUnusedStaticSharedLibraries() {
|
||||
mSharedLibrariesImpl.pruneUnusedStaticSharedLibraries(Long.MAX_VALUE, 0)
|
||||
|
||||
verify(mDeletePackageHelper)
|
||||
.deletePackageX(eq(STATIC_LIB_PACKAGE_NAME), any(), any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getLatestSharedLibraVersion() {
|
||||
val newLibSetting = addPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
|
||||
staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
|
||||
|
||||
val latestInfo = mSharedLibrariesImpl.getLatestSharedLibraVersionLPr(newLibSetting.pkg)!!
|
||||
|
||||
assertThat(latestInfo).isNotNull()
|
||||
assertThat(latestInfo.name).isEqualTo(STATIC_LIB_NAME)
|
||||
assertThat(latestInfo.longVersion).isEqualTo(STATIC_LIB_VERSION)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getStaticSharedLibLatestVersionSetting() {
|
||||
val pair = createBasicAndroidPackage(STATIC_LIB_PACKAGE_NAME + "_" + 10, 10L,
|
||||
staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = 10L)
|
||||
val parsedPackage = pair.second as ParsedPackage
|
||||
val scanRequest = ScanRequest(parsedPackage, null, null, null,
|
||||
null, null, null, 0, 0, false, null, null)
|
||||
val scanResult = ScanResult(scanRequest, true, null, null, false, 0, null, null, null)
|
||||
|
||||
val latestInfoSetting =
|
||||
mSharedLibrariesImpl.getStaticSharedLibLatestVersionSetting(scanResult)!!
|
||||
|
||||
assertThat(latestInfoSetting).isNotNull()
|
||||
assertThat(latestInfoSetting.packageName).isEqualTo(STATIC_LIB_PACKAGE_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateSharedLibraries_withDynamicLibPackage() {
|
||||
val testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
|
||||
assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
|
||||
|
||||
mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
|
||||
null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
|
||||
|
||||
assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateSharedLibraries_withStaticLibPackage() {
|
||||
val testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
|
||||
assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
|
||||
|
||||
mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
|
||||
null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
|
||||
|
||||
assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateSharedLibraries_withConsumerPackage() {
|
||||
val testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
|
||||
assertThat(testPackageSetting.usesLibraryFiles).isEmpty()
|
||||
|
||||
mSharedLibrariesImpl.updateSharedLibrariesLPw(testPackageSetting.pkg, testPackageSetting,
|
||||
null /* changingLib */, null /* changingLibSetting */, mExistingPackages)
|
||||
|
||||
assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(STATIC_LIB_PACKAGE_NAME))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateAllSharedLibraries() {
|
||||
mExistingSettings.forEach {
|
||||
assertThat(it.value.usesLibraryFiles).isEmpty()
|
||||
}
|
||||
|
||||
mSharedLibrariesImpl.updateAllSharedLibrariesLPw(
|
||||
null /* updatedPkg */, null /* updatedPkgSetting */, mExistingPackages)
|
||||
|
||||
var testPackageSetting = mExistingSettings[DYNAMIC_LIB_PACKAGE_NAME]!!
|
||||
assertThat(testPackageSetting.usesLibraryFiles).hasSize(1)
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
|
||||
|
||||
testPackageSetting = mExistingSettings[STATIC_LIB_PACKAGE_NAME]!!
|
||||
assertThat(testPackageSetting.usesLibraryFiles).hasSize(2)
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
|
||||
|
||||
testPackageSetting = mExistingSettings[CONSUMER_PACKAGE_NAME]!!
|
||||
assertThat(testPackageSetting.usesLibraryFiles).hasSize(3)
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(builtinLibPath(BUILTIN_LIB_NAME))
|
||||
assertThat(testPackageSetting.usesLibraryFiles).contains(apkPath(DYNAMIC_LIB_PACKAGE_NAME))
|
||||
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,
|
||||
libraries = arrayOf(DYNAMIC_LIB_NAME),
|
||||
usesLibraries = arrayOf(BUILTIN_LIB_NAME))
|
||||
|
||||
// add a static shared library v7 that is using the dynamic shared library
|
||||
addPackage(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_VERSION,
|
||||
staticLibrary = STATIC_LIB_NAME, staticLibraryVersion = STATIC_LIB_VERSION,
|
||||
usesLibraries = arrayOf(DYNAMIC_LIB_NAME))
|
||||
|
||||
// add a consumer package that is using the dynamic and static shared library
|
||||
addPackage(CONSUMER_PACKAGE_NAME, 1L,
|
||||
usesLibraries = arrayOf(DYNAMIC_LIB_NAME),
|
||||
usesStaticLibraries = arrayOf(STATIC_LIB_NAME),
|
||||
usesStaticLibraryVersions = arrayOf(STATIC_LIB_VERSION))
|
||||
}
|
||||
|
||||
private fun addExistingSharedLibraries() {
|
||||
mSharedLibrariesImpl.addBuiltInSharedLibraryLPw(libEntry(BUILTIN_LIB_NAME))
|
||||
mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
|
||||
libOfDynamic(DYNAMIC_LIB_PACKAGE_NAME, DYNAMIC_LIB_NAME))
|
||||
mSharedLibrariesImpl.commitSharedLibraryInfoLPw(
|
||||
libOfStatic(STATIC_LIB_PACKAGE_NAME, STATIC_LIB_NAME, STATIC_LIB_VERSION))
|
||||
}
|
||||
|
||||
private fun addPackage(
|
||||
packageName: String,
|
||||
version: Long,
|
||||
libraries: Array<String>? = null,
|
||||
staticLibrary: String? = null,
|
||||
staticLibraryVersion: Long = 0L,
|
||||
usesLibraries: Array<String>? = null,
|
||||
usesStaticLibraries: Array<String>? = null,
|
||||
usesStaticLibraryVersions: Array<Long>? = null
|
||||
): PackageSetting {
|
||||
val pair = createBasicAndroidPackage(packageName, version, libraries, staticLibrary,
|
||||
staticLibraryVersion, usesLibraries, usesStaticLibraries, usesStaticLibraryVersions)
|
||||
val apkPath = pair.first
|
||||
val parsingPackage = pair.second
|
||||
val spyPkg = spy((parsingPackage as ParsedPackage).hideAsFinal())
|
||||
mExistingPackages[packageName] = spyPkg
|
||||
|
||||
val spyPackageSetting = spy(mRule.system()
|
||||
.createBasicSettingBuilder(apkPath.parentFile, spyPkg).build())
|
||||
mExistingSettings[spyPackageSetting.packageName] = spyPackageSetting
|
||||
|
||||
return spyPackageSetting
|
||||
}
|
||||
|
||||
private fun createBasicAndroidPackage(
|
||||
packageName: String,
|
||||
version: Long,
|
||||
libraries: Array<String>? = null,
|
||||
staticLibrary: String? = null,
|
||||
staticLibraryVersion: Long = 0L,
|
||||
usesLibraries: Array<String>? = null,
|
||||
usesStaticLibraries: Array<String>? = null,
|
||||
usesStaticLibraryVersions: Array<Long>? = null
|
||||
): Pair<File, PackageImpl> {
|
||||
assertFalse { libraries != null && staticLibrary != null }
|
||||
assertTrue { (usesStaticLibraries?.size ?: -1) == (usesStaticLibraryVersions?.size ?: -1) }
|
||||
|
||||
val pair = mRule.system()
|
||||
.createBasicAndroidPackage(mRule.system().dataAppDirectory, packageName, version)
|
||||
pair.second.apply {
|
||||
setTargetSdkVersion(Build.VERSION_CODES.S)
|
||||
libraries?.forEach { addLibraryName(it) }
|
||||
staticLibrary?.let {
|
||||
setStaticSharedLibName(it)
|
||||
setStaticSharedLibVersion(staticLibraryVersion)
|
||||
setStaticSharedLibrary(true)
|
||||
}
|
||||
usesLibraries?.forEach { addUsesLibrary(it) }
|
||||
usesStaticLibraries?.forEachIndexed { index, s ->
|
||||
addUsesStaticLibrary(s,
|
||||
usesStaticLibraryVersions?.get(index) ?: 0L,
|
||||
arrayOf(s))
|
||||
}
|
||||
}
|
||||
return pair
|
||||
}
|
||||
|
||||
private fun libEntry(libName: String, path: String? = null): SharedLibraryEntry =
|
||||
SharedLibraryEntry(libName, path ?: builtinLibPath(libName),
|
||||
arrayOfNulls(0), false /* isNative */)
|
||||
|
||||
private fun libOfBuiltin(libName: String): SharedLibraryInfo =
|
||||
SharedLibraryInfo(builtinLibPath(libName),
|
||||
null /* packageName */,
|
||||
null /* codePaths */,
|
||||
libName,
|
||||
VERSION_UNDEFINED,
|
||||
SharedLibraryInfo.TYPE_BUILTIN,
|
||||
VersionedPackage(PLATFORM_PACKAGE_NAME, 0L /* versionCode */),
|
||||
null /* dependentPackages */,
|
||||
null /* dependencies */,
|
||||
false /* isNative */)
|
||||
|
||||
private fun libOfStatic(
|
||||
packageName: String,
|
||||
libName: String,
|
||||
version: Long
|
||||
): SharedLibraryInfo =
|
||||
SharedLibraryInfo(null /* path */,
|
||||
packageName,
|
||||
listOf(apkPath(packageName)),
|
||||
libName,
|
||||
version,
|
||||
SharedLibraryInfo.TYPE_STATIC,
|
||||
VersionedPackage(packageName, version /* versionCode */),
|
||||
null /* dependentPackages */,
|
||||
null /* dependencies */,
|
||||
false /* isNative */)
|
||||
|
||||
private fun libOfDynamic(packageName: String, libName: String): SharedLibraryInfo =
|
||||
SharedLibraryInfo(null /* path */,
|
||||
packageName,
|
||||
listOf(apkPath(packageName)),
|
||||
libName,
|
||||
VERSION_UNDEFINED,
|
||||
SharedLibraryInfo.TYPE_DYNAMIC,
|
||||
VersionedPackage(packageName, 1L /* versionCode */),
|
||||
null /* dependentPackages */,
|
||||
null /* dependencies */,
|
||||
false /* isNative */)
|
||||
|
||||
private fun builtinLibPath(libName: String): String = "/system/app/$libName/$libName.jar"
|
||||
|
||||
private fun apkPath(packageName: String): String =
|
||||
File(mRule.system().dataAppDirectory, packageName).path
|
||||
}
|
||||
Reference in New Issue
Block a user