| /* |
| * 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 android.content.pm.PackageManager.INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST; |
| |
| import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; |
| import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; |
| 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.Flags; |
| 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; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.pm.parsing.pkg.ParsedPackage; |
| import com.android.internal.util.ArrayUtils; |
| import com.android.server.SystemConfig; |
| import com.android.server.compat.PlatformCompat; |
| import com.android.server.pm.parsing.pkg.AndroidPackageUtils; |
| import com.android.server.pm.pkg.AndroidPackage; |
| import com.android.server.pm.pkg.PackageStateInternal; |
| import com.android.server.utils.Snappable; |
| import com.android.server.utils.SnapshotCache; |
| import com.android.server.utils.Watchable; |
| import com.android.server.utils.WatchableImpl; |
| import com.android.server.utils.Watched; |
| 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; |
| import java.util.Map; |
| import java.util.Set; |
| 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; |
| |
| private static final String LIBRARY_TYPE_SDK = "sdk"; |
| |
| /** |
| * 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; |
| private final PackageManagerServiceInjector mInjector; |
| private DeletePackageHelper mDeletePackageHelper; // late init |
| |
| // A map of library name to a list of {@link SharedLibraryInfo}s with different versions. |
| @Watched |
| private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> |
| mSharedLibraries; |
| private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>> |
| mSharedLibrariesSnapshot; |
| |
| // A map of declaring package name to a list of {@link SharedLibraryInfo}s with different |
| // versions. |
| @Watched |
| private final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> |
| mStaticLibsByDeclaringPackage; |
| private final SnapshotCache<WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>> |
| mStaticLibsByDeclaringPackageSnapshot; |
| |
| /** |
| * Watchable machinery |
| */ |
| private final WatchableImpl mWatchable = new WatchableImpl(); |
| |
| /** |
| * The observer that watches for changes from array members |
| */ |
| private final Watcher mObserver = new Watcher() { |
| @Override |
| public void onChange(@Nullable Watchable what) { |
| SharedLibrariesImpl.this.dispatchChange(what); |
| } |
| }; |
| |
| private final SnapshotCache<SharedLibrariesImpl> mSnapshot; |
| |
| // Create a snapshot cache |
| private SnapshotCache<SharedLibrariesImpl> makeCache() { |
| return new SnapshotCache<SharedLibrariesImpl>(this /* source */, this /* watchable */) { |
| @Override |
| public SharedLibrariesImpl createSnapshot() { |
| final SharedLibrariesImpl sharedLibrariesImpl = new SharedLibrariesImpl(mSource); |
| sharedLibrariesImpl.mWatchable.seal(); |
| return sharedLibrariesImpl; |
| }}; |
| } |
| |
| /** |
| * Default constructor used in PackageManagerService. |
| */ |
| SharedLibrariesImpl(PackageManagerService pm, PackageManagerServiceInjector injector) { |
| mPm = pm; |
| mInjector = injector; |
| |
| mSharedLibraries = new WatchedArrayMap<>(); |
| mSharedLibrariesSnapshot = new SnapshotCache.Auto<>(mSharedLibraries, mSharedLibraries, |
| "SharedLibrariesImpl.mSharedLibraries"); |
| mStaticLibsByDeclaringPackage = new WatchedArrayMap<>(); |
| mStaticLibsByDeclaringPackageSnapshot = new SnapshotCache.Auto<>( |
| mStaticLibsByDeclaringPackage, mStaticLibsByDeclaringPackage, |
| "SharedLibrariesImpl.mStaticLibsByDeclaringPackage"); |
| |
| registerObservers(); |
| Watchable.verifyWatchedAttributes(this, mObserver); |
| mSnapshot = makeCache(); |
| } |
| |
| /** |
| * Invoked by PMS constructor after the instance of {@link DeletePackageHelper} is ready. |
| */ |
| void setDeletePackageHelper(DeletePackageHelper deletePackageHelper) { |
| mDeletePackageHelper = deletePackageHelper; |
| } |
| |
| private void registerObservers() { |
| mSharedLibraries.registerObserver(mObserver); |
| mStaticLibsByDeclaringPackage.registerObserver(mObserver); |
| } |
| |
| /** |
| * A copy constructor used in snapshot(). |
| */ |
| private SharedLibrariesImpl(SharedLibrariesImpl source) { |
| mPm = source.mPm; |
| mInjector = source.mInjector; |
| |
| mSharedLibraries = source.mSharedLibrariesSnapshot.snapshot(); |
| mSharedLibrariesSnapshot = new SnapshotCache.Sealed<>(); |
| mStaticLibsByDeclaringPackage = source.mStaticLibsByDeclaringPackageSnapshot.snapshot(); |
| mStaticLibsByDeclaringPackageSnapshot = new SnapshotCache.Sealed<>(); |
| |
| // Do not register any Watchables and do not create a snapshot cache. |
| mSnapshot = new SnapshotCache.Sealed(); |
| } |
| |
| /** |
| * Ensures an observer is in the list, exactly once. The observer cannot be null. The |
| * function quietly returns if the observer is already in the list. |
| * |
| * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes. |
| */ |
| @Override |
| public void registerObserver(@NonNull Watcher observer) { |
| mWatchable.registerObserver(observer); |
| } |
| |
| /** |
| * Ensures an observer is not in the list. The observer must not be null. The function |
| * quietly returns if the objserver is not in the list. |
| * |
| * @param observer The {@link Watcher} that should not be in the notification list. |
| */ |
| @Override |
| public void unregisterObserver(@NonNull Watcher observer) { |
| mWatchable.unregisterObserver(observer); |
| } |
| |
| /** |
| * Return true if the {@link Watcher} is a registered observer. |
| * @param observer A {@link Watcher} that might be registered |
| * @return true if the observer is registered with this {@link Watchable}. |
| */ |
| @Override |
| public boolean isRegisteredObserver(@NonNull Watcher observer) { |
| return mWatchable.isRegisteredObserver(observer); |
| } |
| |
| /** |
| * Invokes {@link Watcher#onChange} on each registered observer. The method can be called |
| * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this |
| * is generally the first (deepest) {@link Watchable} to detect a change. |
| * |
| * @param what The {@link Watchable} that generated the event. |
| */ |
| @Override |
| public void dispatchChange(@Nullable Watchable what) { |
| mWatchable.dispatchChange(what); |
| } |
| |
| /** |
| * Create an immutable copy of the object, suitable for read-only methods. A snapshot |
| * is free to omit state that is only needed for mutating methods. |
| */ |
| @Override |
| public @NonNull SharedLibrariesRead snapshot() { |
| return mSnapshot.snapshot(); |
| } |
| |
| /** |
| * Returns all shared libraries on the device. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Override |
| public @NonNull WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getAll() { |
| return mSharedLibraries; |
| } |
| |
| /** |
| * Given the library name, returns a list of shared libraries on all versions. |
| * TODO: Remove, this is used for live mutation outside of the defined commit path |
| */ |
| |
| @Override |
| public @NonNull WatchedLongSparseArray<SharedLibraryInfo> getSharedLibraryInfos( |
| @NonNull String libName) { |
| synchronized (mPm.mLock) { |
| return mSharedLibraries.get(libName); |
| } |
| } |
| |
| @VisibleForTesting |
| public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> getSharedLibraries() { |
| return mSharedLibraries; |
| } |
| |
| /** |
| * Returns the shared library with given library name and version number. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Override |
| public @Nullable SharedLibraryInfo getSharedLibraryInfo(@NonNull String libName, long version) { |
| final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = |
| mSharedLibraries.get(libName); |
| if (versionedLib == null) { |
| return null; |
| } |
| return versionedLib.get(version); |
| } |
| |
| /** |
| * Given the declaring package name, returns a list of static shared libraries on all versions. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Override |
| public @NonNull WatchedLongSparseArray<SharedLibraryInfo> getStaticLibraryInfos( |
| @NonNull String declaringPackageName) { |
| return mStaticLibsByDeclaringPackage.get(declaringPackageName); |
| } |
| |
| @Nullable |
| private PackageStateInternal getLibraryPackage(@NonNull Computer computer, |
| @NonNull SharedLibraryInfo libInfo) { |
| final VersionedPackage declaringPackage = libInfo.getDeclaringPackage(); |
| if (libInfo.isStatic()) { |
| // Resolve the package name - we use synthetic package names internally |
| final String internalPackageName = computer.resolveInternalPackageName( |
| declaringPackage.getPackageName(), |
| declaringPackage.getLongVersionCode()); |
| return computer.getPackageStateInternal(internalPackageName); |
| } |
| if (libInfo.isSdk()) { |
| return computer.getPackageStateInternal(declaringPackage.getPackageName()); |
| } |
| return null; |
| } |
| |
| /** |
| * Finds all unused shared libraries which have cached more than the given |
| * {@code maxCachePeriod}. Deletes them one by one until the available storage space on the |
| * device is larger than {@code neededSpace}. |
| * |
| * @param neededSpace A minimum available storage space the device needs to reach |
| * @param maxCachePeriod A maximum period of time an unused shared library can be cached |
| * on the device. |
| * @return {@code true} if the available storage space is reached. |
| */ |
| boolean pruneUnusedStaticSharedLibraries(@NonNull Computer computer, long neededSpace, |
| long maxCachePeriod) |
| throws IOException { |
| final StorageManager storage = mInjector.getSystemService(StorageManager.class); |
| final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL); |
| |
| final ArrayList<VersionedPackage> packagesToDelete = new ArrayList<>(); |
| final long now = System.currentTimeMillis(); |
| |
| // Important: We skip shared libs used for some user since |
| // in such a case we need to keep the APK on the device. The check for |
| // a lib being used for any user is performed by the uninstall call. |
| final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> |
| sharedLibraries = computer.getSharedLibraries(); |
| final int libCount = sharedLibraries.size(); |
| for (int i = 0; i < libCount; i++) { |
| final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = |
| sharedLibraries.valueAt(i); |
| if (versionedLib == null) { |
| continue; |
| } |
| final int versionCount = versionedLib.size(); |
| for (int j = 0; j < versionCount; j++) { |
| SharedLibraryInfo libInfo = versionedLib.valueAt(j); |
| final PackageStateInternal ps = getLibraryPackage(computer, libInfo); |
| if (ps == null) { |
| continue; |
| } |
| // Skip unused libs cached less than the min period to prevent pruning a lib |
| // needed by a subsequently installed package. |
| if (now - ps.getLastUpdateTime() < maxCachePeriod) { |
| continue; |
| } |
| |
| if (ps.isSystem()) { |
| continue; |
| } |
| |
| packagesToDelete.add(new VersionedPackage(ps.getPkg().getPackageName(), |
| libInfo.getDeclaringPackage().getLongVersionCode())); |
| } |
| } |
| |
| final int packageCount = packagesToDelete.size(); |
| for (int i = 0; i < packageCount; i++) { |
| final VersionedPackage pkgToDelete = packagesToDelete.get(i); |
| // Delete the package synchronously (will fail of the lib used for any user). |
| if (mDeletePackageHelper.deletePackageX(pkgToDelete.getPackageName(), |
| pkgToDelete.getLongVersionCode(), UserHandle.USER_SYSTEM, |
| PackageManager.DELETE_ALL_USERS, |
| true /*removedBySystem*/) == PackageManager.DELETE_SUCCEEDED) { |
| if (volume.getUsableSpace() >= neededSpace) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| @Nullable SharedLibraryInfo getLatestStaticSharedLibraVersion(@NonNull AndroidPackage pkg) { |
| synchronized (mPm.mLock) { |
| return getLatestStaticSharedLibraVersionLPr(pkg); |
| } |
| } |
| /** |
| * Given a package of static shared library, returns its shared library info of |
| * the latest version. |
| * |
| * @param pkg A package of static shared library. |
| * @return The latest version of shared library info. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Nullable |
| private SharedLibraryInfo getLatestStaticSharedLibraVersionLPr(@NonNull AndroidPackage pkg) { |
| WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get( |
| pkg.getStaticSharedLibraryName()); |
| if (versionedLib == null) { |
| return null; |
| } |
| long previousLibVersion = -1; |
| final int versionCount = versionedLib.size(); |
| for (int i = 0; i < versionCount; i++) { |
| final long libVersion = versionedLib.keyAt(i); |
| if (libVersion < pkg.getStaticSharedLibraryVersion()) { |
| previousLibVersion = Math.max(previousLibVersion, libVersion); |
| } |
| } |
| if (previousLibVersion >= 0) { |
| return versionedLib.get(previousLibVersion); |
| } |
| return null; |
| } |
| |
| /** |
| * Given a package scanned result of a static shared library, returns its package setting of |
| * the latest version |
| * |
| * @param installRequest The install result of a static shared library package. |
| * @return The package setting that represents the latest version of shared library info. |
| */ |
| @Nullable |
| PackageSetting getStaticSharedLibLatestVersionSetting(@NonNull InstallRequest installRequest) { |
| if (installRequest.getParsedPackage() == null) { |
| return null; |
| } |
| PackageSetting sharedLibPackage = null; |
| synchronized (mPm.mLock) { |
| final SharedLibraryInfo latestSharedLibraVersionLPr = |
| getLatestStaticSharedLibraVersionLPr(installRequest.getParsedPackage()); |
| if (latestSharedLibraVersionLPr != null) { |
| sharedLibPackage = mPm.mSettings.getPackageLPr( |
| latestSharedLibraVersionLPr.getPackageName()); |
| } |
| } |
| return sharedLibPackage; |
| } |
| |
| /** |
| * Apply a given {@code action} to all the libraries defining in the package. |
| * |
| * @param pkg A package defining libraries. |
| * @param libInfo An extra shared library info passing to the action. |
| * @param action The action to apply. |
| */ |
| @GuardedBy("mPm.mLock") |
| private void applyDefiningSharedLibraryUpdateLPr( |
| @NonNull AndroidPackage pkg, @Nullable SharedLibraryInfo libInfo, |
| @NonNull BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) { |
| // Note that libraries defined by this package may be null if: |
| // - Package manager was unable to create the shared library. The package still |
| // gets installed, but the shared library does not get created. |
| // Or: |
| // - Package manager is in a state where package isn't scanned yet. This will |
| // get called again after scanning to fix the dependencies. |
| if (AndroidPackageUtils.isLibrary(pkg)) { |
| if (pkg.getSdkLibraryName() != null) { |
| SharedLibraryInfo definedLibrary = getSharedLibraryInfo( |
| pkg.getSdkLibraryName(), pkg.getSdkLibVersionMajor()); |
| if (definedLibrary != null) { |
| action.accept(definedLibrary, libInfo); |
| } |
| } else if (pkg.getStaticSharedLibraryName() != null) { |
| SharedLibraryInfo definedLibrary = getSharedLibraryInfo( |
| pkg.getStaticSharedLibraryName(), pkg.getStaticSharedLibraryVersion()); |
| if (definedLibrary != null) { |
| action.accept(definedLibrary, libInfo); |
| } |
| } else { |
| for (String libraryName : pkg.getLibraryNames()) { |
| SharedLibraryInfo definedLibrary = getSharedLibraryInfo( |
| libraryName, SharedLibraryInfo.VERSION_UNDEFINED); |
| if (definedLibrary != null) { |
| action.accept(definedLibrary, libInfo); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Adds shared library {@code libInfo}'s self code paths and using library files to the list |
| * {@code usesLibraryFiles}. Also, adds the dependencies to the shared libraries that are |
| * defining in the {@code pkg}. |
| * |
| * @param pkg A package that is using the {@code libInfo}. |
| * @param usesLibraryFiles A list to add code paths to. |
| * @param libInfo A shared library info that is used by the {@code pkg}. |
| * @param changingLib The updating library package. |
| * @param changingLibSetting The updating library package setting. |
| */ |
| @GuardedBy("mPm.mLock") |
| private void addSharedLibraryLPr(@NonNull AndroidPackage pkg, |
| @NonNull Set<String> usesLibraryFiles, @NonNull SharedLibraryInfo libInfo, |
| @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting) { |
| if (libInfo.getPath() != null) { |
| usesLibraryFiles.add(libInfo.getPath()); |
| return; |
| } |
| AndroidPackage pkgForCodePaths = mPm.mPackages.get(libInfo.getPackageName()); |
| PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(libInfo.getPackageName()); |
| if (changingLib != null && changingLib.getPackageName().equals(libInfo.getPackageName())) { |
| // If we are doing this while in the middle of updating a library apk, |
| // then we need to make sure to use that new apk for determining the |
| // dependencies here. (We haven't yet finished committing the new apk |
| // to the package manager state.) |
| if (pkgForCodePaths == null |
| || pkgForCodePaths.getPackageName().equals(changingLib.getPackageName())) { |
| pkgForCodePaths = changingLib; |
| pkgSetting = changingLibSetting; |
| } |
| } |
| if (pkgForCodePaths != null) { |
| usesLibraryFiles.addAll(AndroidPackageUtils.getAllCodePaths(pkgForCodePaths)); |
| // If the package provides libraries, add the dependency to them. |
| applyDefiningSharedLibraryUpdateLPr(pkg, libInfo, SharedLibraryInfo::addDependency); |
| if (pkgSetting != null) { |
| usesLibraryFiles.addAll(pkgSetting.getPkgState().getUsesLibraryFiles()); |
| } |
| } |
| } |
| |
| /** |
| * Collects all shared libraries being used by the target package. Rebuilds the dependencies |
| * of shared libraries and update the correct shared library code paths for it. |
| * |
| * @param pkg The target package to update shared library dependency. |
| * @param pkgSetting The target's package setting. |
| * @param changingLib The updating library package. |
| * @param changingLibSetting The updating library package setting. |
| * @param availablePackages All installed packages and current being installed packages. |
| */ |
| void updateSharedLibraries(@NonNull AndroidPackage pkg, @NonNull PackageSetting pkgSetting, |
| @Nullable AndroidPackage changingLib, @Nullable PackageSetting changingLibSetting, |
| @NonNull Map<String, AndroidPackage> availablePackages) |
| throws PackageManagerException { |
| final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos( |
| pkg, availablePackages, null /* newLibraries */); |
| synchronized (mPm.mLock) { |
| executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting, |
| sharedLibraryInfos, mPm.mUserManager.getUserIds()); |
| } |
| } |
| |
| void executeSharedLibrariesUpdate(AndroidPackage pkg, |
| @NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib, |
| @Nullable PackageSetting changingLibSetting, |
| ArrayList<SharedLibraryInfo> usesLibraryInfos, int[] allUsers) { |
| synchronized (mPm.mLock) { |
| executeSharedLibrariesUpdateLPw(pkg, pkgSetting, changingLib, changingLibSetting, |
| usesLibraryInfos, allUsers); |
| } |
| } |
| |
| /** |
| * Rebuilds the dependencies of shared libraries for the target package, and update the |
| * shared library code paths to its package setting. |
| * |
| * @param pkg The target package to update shared library dependency. |
| * @param pkgSetting The target's package setting. |
| * @param changingLib The updating library package. |
| * @param changingLibSetting The updating library package setting. |
| * @param usesLibraryInfos The shared libraries used by the target package. |
| * @param allUsers All user ids on the device. |
| */ |
| @GuardedBy("mPm.mLock") |
| private void executeSharedLibrariesUpdateLPw(AndroidPackage pkg, |
| @NonNull PackageSetting pkgSetting, @Nullable AndroidPackage changingLib, |
| @Nullable PackageSetting changingLibSetting, |
| ArrayList<SharedLibraryInfo> usesLibraryInfos, int[] allUsers) { |
| // If the package provides libraries, clear their old dependencies. |
| // This method will set them up again. |
| applyDefiningSharedLibraryUpdateLPr(pkg, null, (definingLibrary, dependency) -> { |
| definingLibrary.clearDependencies(); |
| }); |
| if (usesLibraryInfos != null) { |
| pkgSetting.getPkgState().setUsesLibraryInfos(usesLibraryInfos); |
| // Use LinkedHashSet to preserve the order of files added to |
| // usesLibraryFiles while eliminating duplicates. |
| Set<String> usesLibraryFiles = new LinkedHashSet<>(); |
| for (SharedLibraryInfo libInfo : usesLibraryInfos) { |
| addSharedLibraryLPr(pkg, usesLibraryFiles, libInfo, changingLib, |
| changingLibSetting); |
| } |
| pkgSetting.setPkgStateLibraryFiles(usesLibraryFiles); |
| |
| // let's make sure we mark all static shared libraries as installed for the same users |
| // that its dependent packages are installed for. |
| int[] installedUsers = new int[allUsers.length]; |
| int installedUserCount = 0; |
| for (int u = 0; u < allUsers.length; u++) { |
| if (pkgSetting.getInstalled(allUsers[u])) { |
| installedUsers[installedUserCount++] = allUsers[u]; |
| } |
| } |
| for (SharedLibraryInfo sharedLibraryInfo : usesLibraryInfos) { |
| if (!sharedLibraryInfo.isStatic()) { |
| continue; |
| } |
| final PackageSetting staticLibPkgSetting = |
| mPm.getPackageSettingForMutation(sharedLibraryInfo.getPackageName()); |
| if (staticLibPkgSetting == null) { |
| Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo); |
| continue; |
| } |
| for (int u = 0; u < installedUserCount; u++) { |
| staticLibPkgSetting.setInstalled(true, installedUsers[u]); |
| } |
| } |
| } else { |
| pkgSetting.getPkgState().setUsesLibraryInfos(Collections.emptyList()) |
| .setUsesLibraryFiles(Collections.emptyList()); |
| } |
| } |
| |
| private static boolean hasString(List<String> list, List<String> which) { |
| if (list == null || which == null) { |
| return false; |
| } |
| for (int i = list.size() - 1; i >= 0; i--) { |
| for (int j = which.size() - 1; j >= 0; j--) { |
| if (which.get(j).equals(list.get(i))) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| ArrayList<AndroidPackage> commitSharedLibraryChanges(@NonNull AndroidPackage pkg, |
| @NonNull PackageSetting pkgSetting, List<SharedLibraryInfo> allowedSharedLibraryInfos, |
| @NonNull Map<String, AndroidPackage> combinedSigningDetails, int scanFlags) { |
| if (ArrayUtils.isEmpty(allowedSharedLibraryInfos)) { |
| return null; |
| } |
| synchronized (mPm.mLock) { |
| for (SharedLibraryInfo info : allowedSharedLibraryInfos) { |
| commitSharedLibraryInfoLPw(info); |
| } |
| try { |
| // Shared libraries for the package need to be updated. |
| updateSharedLibraries(pkg, pkgSetting, null, null, combinedSigningDetails); |
| } catch (PackageManagerException e) { |
| Slog.e(TAG, "updateSharedLibraries failed: ", e); |
| } |
| // Update all applications that use this library. Skip when booting |
| // since this will be done after all packages are scaned. |
| if ((scanFlags & SCAN_BOOTING) == 0) { |
| return updateAllSharedLibrariesLPw(pkg, pkgSetting, combinedSigningDetails); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Update shared library dependencies and code paths for applications that are using the |
| * library {@code updatedPkg}. Update all applications if the {@code updatedPkg} is null. |
| * |
| * @param updatedPkg The updating shared library package. |
| * @param updatedPkgSetting The updating shared library package setting. |
| * @param availablePackages All available packages on the device. |
| * @return Packages that has been updated. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Nullable ArrayList<AndroidPackage> updateAllSharedLibrariesLPw( |
| @Nullable AndroidPackage updatedPkg, @Nullable PackageSetting updatedPkgSetting, |
| @NonNull Map<String, AndroidPackage> availablePackages) { |
| ArrayList<AndroidPackage> resultList = null; |
| // Set of all descendants of a library; used to eliminate cycles |
| ArraySet<String> descendants = null; |
| // The current list of packages that need updating |
| List<Pair<AndroidPackage, PackageSetting>> needsUpdating = null; |
| if (updatedPkg != null && updatedPkgSetting != null) { |
| needsUpdating = new ArrayList<>(1); |
| needsUpdating.add(Pair.create(updatedPkg, updatedPkgSetting)); |
| } |
| do { |
| final Pair<AndroidPackage, PackageSetting> changingPkgPair = |
| (needsUpdating == null) ? null : needsUpdating.remove(0); |
| final AndroidPackage changingPkg = changingPkgPair != null |
| ? changingPkgPair.first : null; |
| final PackageSetting changingPkgSetting = changingPkgPair != null |
| ? changingPkgPair.second : null; |
| for (int i = mPm.mPackages.size() - 1; i >= 0; --i) { |
| final AndroidPackage pkg = mPm.mPackages.valueAt(i); |
| final PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName()); |
| if (changingPkg != null |
| && !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames()) |
| && !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames()) |
| && !ArrayUtils.contains(pkg.getUsesStaticLibraries(), |
| changingPkg.getStaticSharedLibraryName()) |
| && !ArrayUtils.contains(pkg.getUsesSdkLibraries(), |
| changingPkg.getSdkLibraryName())) { |
| continue; |
| } |
| if (resultList == null) { |
| resultList = new ArrayList<>(); |
| } |
| resultList.add(pkg); |
| // if we're updating a shared library, all of its descendants must be updated |
| if (changingPkg != null) { |
| if (descendants == null) { |
| descendants = new ArraySet<>(); |
| } |
| if (!descendants.contains(pkg.getPackageName())) { |
| descendants.add(pkg.getPackageName()); |
| needsUpdating.add(Pair.create(pkg, pkgSetting)); |
| } |
| } |
| try { |
| updateSharedLibraries(pkg, pkgSetting, changingPkg, |
| changingPkgSetting, availablePackages); |
| } catch (PackageManagerException e) { |
| // If a system app update or an app and a required lib missing we |
| // delete the package and for updated system apps keep the data as |
| // it is better for the user to reinstall than to be in an limbo |
| // state. Also libs disappearing under an app should never happen |
| // - just in case. |
| if (!pkgSetting.isSystem() || pkgSetting.isUpdatedSystemApp()) { |
| final int flags = pkgSetting.isUpdatedSystemApp() |
| ? PackageManager.DELETE_KEEP_DATA : 0; |
| synchronized (mPm.mInstallLock) { |
| mDeletePackageHelper.deletePackageLIF(pkg.getPackageName(), null, true, |
| mPm.mUserManager.getUserIds(), flags, new PackageRemovedInfo(), |
| true); |
| } |
| } |
| Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage()); |
| } |
| } |
| } while (needsUpdating != null && needsUpdating.size() > 0); |
| return resultList; |
| } |
| |
| /** |
| * Add a build-in shared library info by given system configuration. |
| */ |
| @GuardedBy("mPm.mLock") |
| void addBuiltInSharedLibraryLPw(@NonNull SystemConfig.SharedLibraryEntry entry) { |
| // check if built-in or dynamic library exists |
| if (getSharedLibraryInfo(entry.name, SharedLibraryInfo.VERSION_UNDEFINED) != null) { |
| return; |
| } |
| |
| SharedLibraryInfo libraryInfo = new SharedLibraryInfo(entry.filename, null, null, |
| entry.name, SharedLibraryInfo.VERSION_UNDEFINED, |
| SharedLibraryInfo.TYPE_BUILTIN, |
| new VersionedPackage(PLATFORM_PACKAGE_NAME, 0L), null, null, |
| entry.isNative); |
| |
| commitSharedLibraryInfoLPw(libraryInfo); |
| } |
| |
| /** |
| * Add a shared library info to the system. This is invoked when the package is being added or |
| * scanned. |
| */ |
| @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) |
| @GuardedBy("mPm.mLock") |
| void commitSharedLibraryInfoLPw(@NonNull SharedLibraryInfo libraryInfo) { |
| final String name = libraryInfo.getName(); |
| WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); |
| if (versionedLib == null) { |
| versionedLib = new WatchedLongSparseArray<>(); |
| mSharedLibraries.put(name, versionedLib); |
| } |
| final String declaringPackageName = libraryInfo.getDeclaringPackage().getPackageName(); |
| if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) { |
| mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib); |
| } |
| versionedLib.put(libraryInfo.getLongVersion(), libraryInfo); |
| } |
| |
| /** |
| * Remove a shared library from the system. |
| */ |
| boolean removeSharedLibrary(@NonNull String libName, long version) { |
| synchronized (mPm.mLock) { |
| WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(libName); |
| if (versionedLib == null) { |
| return false; |
| } |
| final int libIdx = versionedLib.indexOfKey(version); |
| if (libIdx < 0) { |
| return false; |
| } |
| SharedLibraryInfo libraryInfo = versionedLib.valueAt(libIdx); |
| |
| final Computer snapshot = mPm.snapshotComputer(); |
| |
| // Remove the shared library overlays from its dependent packages. |
| for (int currentUserId : mPm.mUserManager.getUserIds()) { |
| var usingSharedLibraryPair = snapshot.getPackagesUsingSharedLibrary(libraryInfo, 0, |
| Process.SYSTEM_UID, currentUserId); |
| final List<VersionedPackage> dependents = usingSharedLibraryPair.first; |
| if (dependents == null) { |
| continue; |
| } |
| for (VersionedPackage dependentPackage : dependents) { |
| final PackageSetting ps = mPm.mSettings.getPackageLPr( |
| dependentPackage.getPackageName()); |
| if (ps != null) { |
| ps.setOverlayPathsForLibrary(libraryInfo.getName(), null, currentUserId); |
| } |
| } |
| } |
| |
| versionedLib.remove(version); |
| if (versionedLib.size() <= 0) { |
| mSharedLibraries.remove(libName); |
| if (libraryInfo.getType() == SharedLibraryInfo.TYPE_STATIC) { |
| mStaticLibsByDeclaringPackage.remove(libraryInfo.getDeclaringPackage() |
| .getPackageName()); |
| } |
| } |
| 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(InstallRequest installRequest) { |
| // Let's used the parsed package as scanResult.pkgSetting may be null |
| final ParsedPackage parsedPackage = installRequest.getParsedPackage(); |
| if (installRequest.getSdkSharedLibraryInfo() == null |
| && installRequest.getStaticSharedLibraryInfo() == null |
| && installRequest.getDynamicSharedLibraryInfos() == null) { |
| return null; |
| } |
| |
| // Any app can add new SDKs and static shared libraries. |
| if (installRequest.getSdkSharedLibraryInfo() != null) { |
| return Collections.singletonList(installRequest.getSdkSharedLibraryInfo()); |
| } |
| if (installRequest.getStaticSharedLibraryInfo() != null) { |
| return Collections.singletonList(installRequest.getStaticSharedLibraryInfo()); |
| } |
| boolean isSystemApp = installRequest.getScannedPackageSetting() != null |
| && installRequest.getScannedPackageSetting().isSystem(); |
| final boolean hasDynamicLibraries = parsedPackage != null && isSystemApp |
| && installRequest.getDynamicSharedLibraryInfos() != null; |
| if (!hasDynamicLibraries) { |
| return null; |
| } |
| final boolean isUpdatedSystemApp = installRequest.getScannedPackageSetting() != null |
| && installRequest.getScannedPackageSetting().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 |
| ? installRequest.getScanRequestDisabledPackageSetting() == null |
| ? installRequest.getScanRequestOldPackageSetting() |
| : installRequest.getScanRequestDisabledPackageSetting() |
| : 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<>(installRequest.getDynamicSharedLibraryInfos().size()); |
| for (SharedLibraryInfo info : installRequest.getDynamicSharedLibraryInfos()) { |
| 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, null, |
| pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null, |
| availablePackages, newLibraries); |
| } |
| if (!pkg.getUsesStaticLibraries().isEmpty()) { |
| usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(), |
| pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(), |
| null, pkg.getPackageName(), "static shared", true, |
| pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries); |
| } |
| if (!pkg.getUsesOptionalLibraries().isEmpty()) { |
| usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, 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, null, pkg.getPackageName(), "native shared", true, |
| pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, |
| newLibraries); |
| } |
| if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) { |
| usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(), |
| null, null, null, pkg.getPackageName(), "native shared", false, |
| pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, |
| newLibraries); |
| } |
| } |
| if (!pkg.getUsesSdkLibraries().isEmpty()) { |
| // Allow installation even if sdk-library dependency doesn't exist |
| boolean required = !Flags.sdkLibIndependence(); |
| usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(), |
| pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(), |
| pkg.getUsesSdkLibrariesOptional(), |
| pkg.getPackageName(), LIBRARY_TYPE_SDK, required, pkg.getTargetSdkVersion(), |
| usesLibraryInfos, availablePackages, newLibraries); |
| } |
| return usesLibraryInfos; |
| } |
| |
| private ArrayList<SharedLibraryInfo> collectSharedLibraryInfos( |
| @NonNull List<String> requestedLibraries, |
| @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, |
| @Nullable boolean[] libsOptional, |
| @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) { |
| // Only allow app be installed if the app specifies the sdk-library dependency is |
| // optional |
| if (required || (LIBRARY_TYPE_SDK.equals(libraryType) && (libsOptional != null |
| && !libsOptional[i]))) { |
| 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 |
| final byte[] digestBytes; |
| try { |
| digestBytes = HexEncoding.decode( |
| expectedCertDigests[0], false /* allowSingleChar */); |
| } catch (IllegalArgumentException e) { |
| throw new PackageManagerException( |
| INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST, |
| "Package " + packageName + " declares bad certificate digest " |
| + "for " + libraryType + " library " + libName |
| + "; failing!"); |
| } |
| 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. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Override |
| public void dump(@NonNull PrintWriter pw, @NonNull DumpState dumpState) { |
| final boolean checkin = dumpState.isCheckIn(); |
| boolean printedHeader = false; |
| final int numSharedLibraries = mSharedLibraries.size(); |
| for (int index = 0; index < numSharedLibraries; index++) { |
| final String libName = mSharedLibraries.keyAt(index); |
| final WatchedLongSparseArray<SharedLibraryInfo> versionedLib = |
| mSharedLibraries.get(libName); |
| if (versionedLib == null) { |
| continue; |
| } |
| final int versionCount = versionedLib.size(); |
| for (int i = 0; i < versionCount; i++) { |
| SharedLibraryInfo libraryInfo = versionedLib.valueAt(i); |
| if (!checkin) { |
| if (!printedHeader) { |
| if (dumpState.onTitlePrinted()) { |
| pw.println(); |
| } |
| pw.println("Libraries:"); |
| printedHeader = true; |
| } |
| pw.print(" "); |
| } else { |
| pw.print("lib,"); |
| } |
| pw.print(libraryInfo.getName()); |
| if (libraryInfo.isStatic()) { |
| pw.print(" version=" + libraryInfo.getLongVersion()); |
| } |
| if (!checkin) { |
| pw.print(" -> "); |
| } |
| if (libraryInfo.getPath() != null) { |
| if (libraryInfo.isNative()) { |
| pw.print(" (so) "); |
| } else { |
| pw.print(" (jar) "); |
| } |
| pw.print(libraryInfo.getPath()); |
| } else { |
| pw.print(" (apk) "); |
| pw.print(libraryInfo.getPackageName()); |
| } |
| pw.println(); |
| } |
| } |
| } |
| |
| /** |
| * Dump all shared libraries to given proto output stream. |
| */ |
| @GuardedBy("mPm.mLock") |
| @Override |
| public void dumpProto(@NonNull ProtoOutputStream proto) { |
| final int count = mSharedLibraries.size(); |
| for (int i = 0; i < count; i++) { |
| final String libName = mSharedLibraries.keyAt(i); |
| WatchedLongSparseArray<SharedLibraryInfo> versionedLib = |
| mSharedLibraries.get(libName); |
| if (versionedLib == null) { |
| continue; |
| } |
| final int versionCount = versionedLib.size(); |
| for (int j = 0; j < versionCount; j++) { |
| final SharedLibraryInfo libraryInfo = versionedLib.valueAt(j); |
| final long sharedLibraryToken = |
| proto.start(PackageServiceDumpProto.SHARED_LIBRARIES); |
| proto.write(PackageServiceDumpProto.SharedLibraryProto.NAME, libraryInfo.getName()); |
| final boolean isJar = (libraryInfo.getPath() != null); |
| proto.write(PackageServiceDumpProto.SharedLibraryProto.IS_JAR, isJar); |
| if (isJar) { |
| proto.write(PackageServiceDumpProto.SharedLibraryProto.PATH, |
| libraryInfo.getPath()); |
| } else { |
| proto.write(PackageServiceDumpProto.SharedLibraryProto.APK, |
| libraryInfo.getPackageName()); |
| } |
| proto.end(sharedLibraryToken); |
| } |
| } |
| } |
| } |