blob: 82622d9a4ea82da44b6df3680b75fb0862587cc2 [file] [log] [blame]
/*
* Copyright (C) 2019 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.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_NULL;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__BOOT;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_CREATED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_DELETED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__COMPAT_CHANGED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_ADDED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_DELETED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_REPLACED;
import static com.android.server.pm.AppsFilterUtils.canQueryAsInstaller;
import static com.android.server.pm.AppsFilterUtils.canQueryAsUpdateOwner;
import static com.android.server.pm.AppsFilterUtils.canQueryViaComponents;
import static com.android.server.pm.AppsFilterUtils.canQueryViaPackage;
import static com.android.server.pm.AppsFilterUtils.canQueryViaUsesLibrary;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseSetArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedPermission;
import com.android.internal.pm.pkg.component.ParsedUsesPermission;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
import com.android.server.compat.CompatChange;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.AppsFilterUtils.ParallelComputeComponentVisibility;
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.pm.pkg.SharedUserApi;
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.WatchedArraySet;
import com.android.server.utils.WatchedSparseBooleanMatrix;
import com.android.server.utils.WatchedSparseSetArray;
import com.android.server.utils.Watcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Implementation of the methods that update the internal structures of AppsFilter. Because of the
* mutations, all the read accesses to those internal structures need to be locked, thus extending
* {@link AppsFilterLocked}.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class AppsFilterImpl extends AppsFilterLocked implements Watchable, Snappable {
/**
* A cached snapshot.
*/
@NonNull
private final SnapshotCache<AppsFilterSnapshot> mSnapshot;
/**
* Watchable machinery
*/
private final WatchableImpl mWatchable = new WatchableImpl();
/**
* A cache that maps parsed {@link android.R.styleable#AndroidManifestPermission
* &lt;permission&gt;} to the packages that define them. While computing visibility based on
* permissions, this cache is used to save the cost of reading through every existing package
* to determine the App Ids that define a particular permission.
*/
@GuardedBy("mQueryableViaUsesPermissionLock")
@NonNull
private final ArrayMap<String, ArraySet<Integer>> mPermissionToUids;
/**
* A cache that maps parsed {@link android.R.styleable#AndroidManifestUsesPermission
* &lt;uses-permission&gt;} to the packages that request them. While computing visibility based
* on permissions, this cache is used to save the cost of reading through every existing
* package to determine the App Ids that request a particular permission.
*/
@GuardedBy("mQueryableViaUsesPermissionLock")
@NonNull
private final ArrayMap<String, ArraySet<Integer>> mUsesPermissionToUids;
/**
* 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);
}
/**
* Report a change to observers.
*/
private void onChanged() {
dispatchChange(this);
}
private void invalidateCache(String reason) {
if (mCacheValid.compareAndSet(CACHE_VALID, CACHE_INVALID)) {
Slog.i(TAG, "Invalidating cache: " + reason);
}
}
@VisibleForTesting(visibility = PRIVATE)
AppsFilterImpl(FeatureConfig featureConfig,
String[] forceQueryableList,
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider,
Handler handler) {
mFeatureConfig = featureConfig;
mForceQueryableByDevicePackageNames = forceQueryableList;
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
mHandler = handler;
mShouldFilterCache = new WatchedSparseBooleanMatrix();
mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>(
mShouldFilterCache, mShouldFilterCache, "AppsFilter.mShouldFilterCache");
mImplicitlyQueryable = new WatchedSparseSetArray<>();
mImplicitQueryableSnapshot = new SnapshotCache.Auto<>(
mImplicitlyQueryable, mImplicitlyQueryable, "AppsFilter.mImplicitlyQueryable");
mRetainedImplicitlyQueryable = new WatchedSparseSetArray<>();
mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Auto<>(
mRetainedImplicitlyQueryable, mRetainedImplicitlyQueryable,
"AppsFilter.mRetainedImplicitlyQueryable");
mQueriesViaPackage = new WatchedSparseSetArray<>();
mQueriesViaPackageSnapshot = new SnapshotCache.Auto<>(
mQueriesViaPackage, mQueriesViaPackage, "AppsFilter.mQueriesViaPackage");
mQueriesViaComponent = new WatchedSparseSetArray<>();
mQueriesViaComponentSnapshot = new SnapshotCache.Auto<>(
mQueriesViaComponent, mQueriesViaComponent, "AppsFilter.mQueriesViaComponent");
mQueryableViaUsesLibrary = new WatchedSparseSetArray<>();
mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Auto<>(
mQueryableViaUsesLibrary, mQueryableViaUsesLibrary,
"AppsFilter.mQueryableViaUsesLibrary");
mQueryableViaUsesPermission = new WatchedSparseSetArray<>();
mQueryableViaUsesPermissionSnapshot = new SnapshotCache.Auto<>(
mQueryableViaUsesPermission, mQueryableViaUsesPermission,
"AppsFilter.mQueryableViaUsesPermission");
mForceQueryable = new WatchedArraySet<>();
mForceQueryableSnapshot = new SnapshotCache.Auto<>(
mForceQueryable, mForceQueryable, "AppsFilter.mForceQueryable");
mProtectedBroadcasts = new WatchedArraySet<>();
mProtectedBroadcastsSnapshot = new SnapshotCache.Auto<>(
mProtectedBroadcasts, mProtectedBroadcasts, "AppsFilter.mProtectedBroadcasts");
mPermissionToUids = new ArrayMap<>();
mUsesPermissionToUids = new ArrayMap<>();
mSnapshot = new SnapshotCache<AppsFilterSnapshot>(this, this) {
@Override
public AppsFilterSnapshot createSnapshot() {
return new AppsFilterSnapshotImpl(AppsFilterImpl.this);
}
};
readCacheEnabledSysProp();
SystemProperties.addChangeCallback(this::readCacheEnabledSysProp);
}
private void readCacheEnabledSysProp() {
mCacheEnabled = SystemProperties.getBoolean("debug.pm.use_app_filter_cache", true);
}
/**
* Return a snapshot. If the cached snapshot is null, build a new one. The logic in
* the function ensures that this function returns a valid snapshot even if a race
* condition causes the cached snapshot to be cleared asynchronously to this method.
*/
public AppsFilterSnapshot snapshot() {
return mSnapshot.snapshot();
}
private static class FeatureConfigImpl implements FeatureConfig,
CompatChange.ChangeListener {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
private final PackageManagerServiceInjector mInjector;
private final PackageManagerInternal mPmInternal;
private volatile boolean mFeatureEnabled =
PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT;
@GuardedBy("mDisabledPackages")
private final ArraySet<String> mDisabledPackages = new ArraySet<>();
@Nullable
private SparseBooleanArray mLoggingEnabled = null;
private AppsFilterImpl mAppsFilter;
private FeatureConfigImpl(
PackageManagerInternal pmInternal, PackageManagerServiceInjector injector) {
mPmInternal = pmInternal;
mInjector = injector;
}
FeatureConfigImpl(FeatureConfigImpl orig) {
mInjector = null;
mPmInternal = null;
mFeatureEnabled = orig.mFeatureEnabled;
synchronized (orig.mDisabledPackages) {
mDisabledPackages.addAll(orig.mDisabledPackages);
}
mLoggingEnabled = orig.mLoggingEnabled;
}
public void setAppsFilter(AppsFilterImpl filter) {
mAppsFilter = filter;
}
@Override
public void onSystemReady() {
mFeatureEnabled = DeviceConfig.getBoolean(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT);
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
properties -> {
if (properties.getKeyset().contains(FILTERING_ENABLED_NAME)) {
synchronized (FeatureConfigImpl.this) {
mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME,
PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT);
}
}
});
mInjector.getCompatibility().registerListener(
PackageManager.FILTER_APPLICATION_QUERY, this);
}
@Override
public boolean isGloballyEnabled() {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "isGloballyEnabled");
}
try {
return mFeatureEnabled;
} finally {
if (DEBUG_TRACING) {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
}
@Override
public boolean packageIsEnabled(AndroidPackage pkg) {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "packageIsEnabled");
}
try {
synchronized (mDisabledPackages) {
return !mDisabledPackages.contains(pkg.getPackageName());
}
} finally {
if (DEBUG_TRACING) {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
}
@Override
public boolean isLoggingEnabled(int uid) {
return mLoggingEnabled != null && mLoggingEnabled.indexOfKey(uid) >= 0;
}
@Override
public void enableLogging(int appId, boolean enable) {
if (enable) {
if (mLoggingEnabled == null) {
mLoggingEnabled = new SparseBooleanArray();
}
mLoggingEnabled.put(appId, true);
} else {
if (mLoggingEnabled != null) {
final int index = mLoggingEnabled.indexOfKey(appId);
if (index >= 0) {
mLoggingEnabled.removeAt(index);
if (mLoggingEnabled.size() == 0) {
mLoggingEnabled = null;
}
}
}
}
}
@Override
public void onCompatChange(String packageName) {
Computer snapshot = (Computer) mPmInternal.snapshot();
AndroidPackage pkg = snapshot.getPackage(packageName);
if (pkg == null) {
return;
}
final long currentTimeUs = SystemClock.currentTimeMicro();
updateEnabledState(pkg);
mAppsFilter.updateShouldFilterCacheForPackage(snapshot, packageName);
mAppsFilter.logCacheUpdated(
PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__COMPAT_CHANGED,
SystemClock.currentTimeMicro() - currentTimeUs,
snapshot.getUserInfos().length,
snapshot.getPackageStates().size(),
pkg.getUid());
}
private void updateEnabledState(@NonNull AndroidPackage pkg) {
// TODO(b/135203078): Do not use toAppInfo
// TODO(b/167551701): Make changeId non-logging
final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternalNoLogging(
PackageManager.FILTER_APPLICATION_QUERY,
AndroidPackageUtils.generateAppInfoWithoutState(pkg));
synchronized (mDisabledPackages) {
if (enabled) {
mDisabledPackages.remove(pkg.getPackageName());
} else {
mDisabledPackages.add(pkg.getPackageName());
}
}
if (mAppsFilter != null) {
mAppsFilter.onChanged();
}
}
@Override
public void updatePackageState(PackageStateInternal setting, boolean removed) {
final boolean enableLogging = setting.getPkg() != null
&& !removed && (setting.getPkg().isTestOnly()
|| setting.getPkg().isDebuggable());
enableLogging(setting.getAppId(), enableLogging);
if (removed) {
synchronized (mDisabledPackages) {
mDisabledPackages.remove(setting.getPackageName());
}
if (mAppsFilter != null) {
mAppsFilter.onChanged();
}
} else if (setting.getPkg() != null) {
updateEnabledState(setting.getPkg());
}
}
@Override
public FeatureConfig snapshot() {
return new FeatureConfigImpl(this);
}
}
/** Builder method for an AppsFilter */
public static AppsFilterImpl create(@NonNull PackageManagerServiceInjector injector,
@NonNull PackageManagerInternal pmInt) {
final boolean forceSystemAppsQueryable =
injector.getContext().getResources()
.getBoolean(R.bool.config_forceSystemPackagesQueryable);
final FeatureConfigImpl featureConfig = new FeatureConfigImpl(pmInt, injector);
final String[] forcedQueryablePackageNames;
if (forceSystemAppsQueryable) {
// all system apps already queryable, no need to read and parse individual exceptions
forcedQueryablePackageNames = new String[]{};
} else {
forcedQueryablePackageNames =
injector.getContext().getResources()
.getStringArray(R.array.config_forceQueryablePackages);
for (int i = 0; i < forcedQueryablePackageNames.length; i++) {
forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
}
}
AppsFilterImpl appsFilter = new AppsFilterImpl(featureConfig,
forcedQueryablePackageNames, forceSystemAppsQueryable, null,
injector.getHandler());
featureConfig.setAppsFilter(appsFilter);
return appsFilter;
}
public FeatureConfig getFeatureConfig() {
return mFeatureConfig;
}
/**
* Grants access based on an interaction between a calling and target package, granting
* visibility of the caller from the target.
*
* @param recipientUid the uid gaining visibility of the {@code visibleUid}.
* @param visibleUid the uid becoming visible to the {@recipientUid}
* @param retainOnUpdate if the implicit access retained across package updates.
* @return {@code true} if implicit access was not already granted.
*/
public boolean grantImplicitAccess(int recipientUid, int visibleUid, boolean retainOnUpdate) {
if (recipientUid == visibleUid) {
return false;
}
final boolean changed;
synchronized (mImplicitlyQueryableLock) {
changed = retainOnUpdate
? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid)
: mImplicitlyQueryable.add(recipientUid, visibleUid);
if (!mCacheReady && changed) {
mNeedToUpdateCacheForImplicitAccess = true;
}
}
if (changed && DEBUG_LOGGING) {
Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: "
+ recipientUid + " -> " + visibleUid);
}
if (mCacheReady) {
synchronized (mCacheLock) {
// Update the cache in a one-off manner since we've got all the information we need.
mShouldFilterCache.put(recipientUid, visibleUid, false);
}
}
if (changed) {
onChanged();
}
return changed;
}
public void onSystemReady(PackageManagerInternal pmInternal) {
mOverlayReferenceMapper.rebuildIfDeferred();
mFeatureConfig.onSystemReady();
updateEntireShouldFilterCacheAsync(pmInternal,
PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__BOOT);
}
/**
* Adds a package that should be considered when filtering visibility between apps.
*
* @param newPkgSetting the new setting being added
* @param isReplace if the package is being replaced and may need extra cleanup.
* @param retainImplicitGrantOnReplace {@code true} to retain implicit grant access if
* the package is being replaced.
*/
public void addPackage(Computer snapshot, PackageStateInternal newPkgSetting,
boolean isReplace, boolean retainImplicitGrantOnReplace) {
final long currentTimeUs = SystemClock.currentTimeMicro();
final int logType = isReplace
? PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_REPLACED
: PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_ADDED;
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "filter.addPackage");
}
try {
if (isReplace) {
// let's first remove any prior rules for this package
removePackageInternal(snapshot, newPkgSetting,
true /*isReplace*/, retainImplicitGrantOnReplace);
}
final ArrayMap<String, ? extends PackageStateInternal> settings =
snapshot.getPackageStates();
final UserInfo[] users = snapshot.getUserInfos();
final ArraySet<String> additionalChangedPackages =
addPackageInternal(newPkgSetting, settings);
if (mCacheReady) {
synchronized (mCacheLock) {
updateShouldFilterCacheForPackage(snapshot, null, newPkgSetting,
settings, users, USER_ALL, settings.size());
if (additionalChangedPackages != null) {
for (int index = 0; index < additionalChangedPackages.size(); index++) {
String changedPackage = additionalChangedPackages.valueAt(index);
PackageStateInternal changedPkgSetting = settings.get(changedPackage);
if (changedPkgSetting == null) {
// It's possible for the overlay mapper to know that an actor
// package changed via an explicit reference, even if the actor
// isn't installed, so skip if that's the case.
continue;
}
updateShouldFilterCacheForPackage(snapshot, null, changedPkgSetting,
settings, users, USER_ALL, settings.size());
}
}
}
logCacheUpdated(logType, SystemClock.currentTimeMicro() - currentTimeUs,
users.length, settings.size(), newPkgSetting.getAppId());
} else {
invalidateCache("addPackage: " + newPkgSetting.getPackageName());
}
} finally {
onChanged();
if (DEBUG_TRACING) {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
}
/**
* @return Additional packages that may have had their viewing visibility changed and may need
* to be updated in the cache. Returns null if there are no additional packages.
*/
@Nullable
private ArraySet<String> addPackageInternal(PackageStateInternal newPkgSetting,
ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
if (Objects.equals("android", newPkgSetting.getPackageName())) {
// let's set aside the framework signatures
mSystemSigningDetails = newPkgSetting.getSigningDetails();
// and since we add overlays before we add the framework, let's revisit already added
// packages for signature matches
for (PackageStateInternal setting : existingSettings.values()) {
if (isSystemSigned(mSystemSigningDetails, setting)) {
synchronized (mForceQueryableLock) {
mForceQueryable.add(setting.getAppId());
}
}
}
}
final AndroidPackage newPkg = newPkgSetting.getPkg();
if (newPkg == null) {
return null;
}
final List<String> newBroadcasts = newPkg.getProtectedBroadcasts();
if (newBroadcasts.size() != 0) {
final boolean protectedBroadcastsChanged;
synchronized (mProtectedBroadcastsLock) {
final int oldSize = mProtectedBroadcasts.size();
mProtectedBroadcasts.addAll(newBroadcasts);
protectedBroadcastsChanged = mProtectedBroadcasts.size() != oldSize;
}
if (protectedBroadcastsChanged) {
mQueriesViaComponentRequireRecompute.set(true);
}
}
final boolean newIsForceQueryable;
synchronized (mForceQueryableLock) {
newIsForceQueryable = mForceQueryable.contains(newPkgSetting.getAppId())
/* shared user that is already force queryable */
|| newPkgSetting.isForceQueryableOverride() /* adb override */
|| (newPkgSetting.isSystem() && (mSystemAppsQueryable
|| newPkg.isForceQueryable()
|| ArrayUtils.contains(mForceQueryableByDevicePackageNames,
newPkg.getPackageName())));
if (newIsForceQueryable
|| (mSystemSigningDetails != null
&& isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
mForceQueryable.add(newPkgSetting.getAppId());
}
}
if (!newPkg.getUsesPermissions().isEmpty()) {
// newPkg requests some permissions
synchronized (mQueryableViaUsesPermissionLock) {
for (ParsedUsesPermission usesPermission : newPkg.getUsesPermissions()) {
String usesPermissionName = usesPermission.getName();
// Lookup in the mPermissionToUids cache if installed packages have
// defined this permission.
if (mPermissionToUids.containsKey(usesPermissionName)) {
final ArraySet<Integer> permissionDefiners =
mPermissionToUids.get(usesPermissionName);
for (int j = 0; j < permissionDefiners.size(); j++) {
final int targetAppId = permissionDefiners.valueAt(j);
if (targetAppId != newPkgSetting.getAppId()) {
mQueryableViaUsesPermission.add(newPkgSetting.getAppId(),
targetAppId);
}
}
}
// Record in mUsesPermissionToUids that a permission was requested
// by a new package
if (!mUsesPermissionToUids.containsKey(usesPermissionName)) {
mUsesPermissionToUids.put(usesPermissionName, new ArraySet<>());
}
mUsesPermissionToUids.get(usesPermissionName).add(newPkgSetting.getAppId());
}
}
}
if (!newPkg.getPermissions().isEmpty()) {
synchronized (mQueryableViaUsesPermissionLock) {
// newPkg defines some permissions
for (ParsedPermission permission : newPkg.getPermissions()) {
String permissionName = permission.getName();
// Lookup in the mUsesPermissionToUids cache if installed packages have
// requested this permission.
if (mUsesPermissionToUids.containsKey(permissionName)) {
final ArraySet<Integer> permissionUsers = mUsesPermissionToUids.get(
permissionName);
for (int j = 0; j < permissionUsers.size(); j++) {
final int queryingAppId = permissionUsers.valueAt(j);
if (queryingAppId != newPkgSetting.getAppId()) {
mQueryableViaUsesPermission.add(queryingAppId,
newPkgSetting.getAppId());
}
}
}
// Record in mPermissionToUids that a permission was defined by a new package
if (!mPermissionToUids.containsKey(permissionName)) {
mPermissionToUids.put(permissionName, new ArraySet<>());
}
mPermissionToUids.get(permissionName).add(newPkgSetting.getAppId());
}
}
}
for (int i = existingSettings.size() - 1; i >= 0; i--) {
final PackageStateInternal existingSetting = existingSettings.valueAt(i);
if (existingSetting.getAppId() == newPkgSetting.getAppId()
|| existingSetting.getPkg()
== null) {
continue;
}
final AndroidPackage existingPkg = existingSetting.getPkg();
// let's evaluate the ability of already added packages to see this new package
if (!newIsForceQueryable) {
if (!mQueriesViaComponentRequireRecompute.get()
&& canQueryViaComponents(existingPkg, newPkg, mProtectedBroadcasts)) {
synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.add(existingSetting.getAppId(),
newPkgSetting.getAppId());
}
}
if (canQueryViaPackage(existingPkg, newPkg)
|| canQueryAsInstaller(existingSetting, newPkg)
|| canQueryAsUpdateOwner(existingSetting, newPkg)) {
synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.add(existingSetting.getAppId(),
newPkgSetting.getAppId());
}
}
if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
synchronized (mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary.add(existingSetting.getAppId(),
newPkgSetting.getAppId());
}
}
}
final boolean existingIsForceQueryable;
synchronized (mForceQueryableLock) {
existingIsForceQueryable = mForceQueryable.contains(existingSetting.getAppId());
}
// now we'll evaluate our new package's ability to see existing packages
if (!existingIsForceQueryable) {
if (!mQueriesViaComponentRequireRecompute.get()
&& canQueryViaComponents(newPkg, existingPkg, mProtectedBroadcasts)) {
synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.add(newPkgSetting.getAppId(),
existingSetting.getAppId());
}
}
if (canQueryViaPackage(newPkg, existingPkg)
|| canQueryAsInstaller(newPkgSetting, existingPkg)
|| canQueryAsUpdateOwner(newPkgSetting, existingPkg)) {
synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.add(newPkgSetting.getAppId(),
existingSetting.getAppId());
}
}
if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
synchronized (mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(),
existingSetting.getAppId());
}
}
}
// if either package instruments the other, mark both as visible to one another
if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null
&& (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg())
|| pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) {
synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId());
mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId());
}
}
}
int existingSize = existingSettings.size();
ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
for (int index = 0; index < existingSize; index++) {
PackageStateInternal pkgSetting = existingSettings.valueAt(index);
if (pkgSetting.getPkg() != null) {
existingPkgs.put(pkgSetting.getPackageName(), pkgSetting.getPkg());
}
}
ArraySet<String> changedPackages =
mOverlayReferenceMapper.addPkg(newPkgSetting.getPkg(), existingPkgs);
mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
return changedPackages;
}
private void removeAppIdFromVisibilityCache(int appId) {
synchronized (mCacheLock) {
for (int i = 0; i < mShouldFilterCache.size(); i++) {
if (UserHandle.getAppId(mShouldFilterCache.keyAt(i)) == appId) {
mShouldFilterCache.removeAt(i);
// The key was deleted so the list of keys has shifted left. That means i
// is now pointing at the next key to be examined. The decrement here and
// the loop increment together mean that i will be unchanged in the need
// iteration and will correctly point to the next key to be examined.
i--;
}
}
}
}
private void updateEntireShouldFilterCache(Computer snapshot, int subjectUserId) {
final ArrayMap<String, ? extends PackageStateInternal> settings =
snapshot.getPackageStates();
final UserInfo[] users = snapshot.getUserInfos();
int userId = USER_NULL;
for (int u = 0; u < users.length; u++) {
if (subjectUserId == users[u].id) {
userId = subjectUserId;
break;
}
}
if (userId == USER_NULL) {
Slog.e(TAG, "We encountered a new user that isn't a member of known users, "
+ "updating the whole cache");
userId = USER_ALL;
}
updateEntireShouldFilterCacheInner(snapshot, settings, users, userId);
onChanged();
}
private void updateEntireShouldFilterCacheInner(Computer snapshot,
ArrayMap<String, ? extends PackageStateInternal> settings,
UserInfo[] users,
int subjectUserId) {
synchronized (mCacheLock) {
if (subjectUserId == USER_ALL) {
mShouldFilterCache.clear();
}
mShouldFilterCache.setCapacity(users.length * settings.size());
for (int i = settings.size() - 1; i >= 0; i--) {
updateShouldFilterCacheForPackage(snapshot,
null /*skipPackage*/, settings.valueAt(i), settings, users,
subjectUserId, i);
}
}
}
private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal, int reason) {
updateEntireShouldFilterCacheAsync(pmInternal, CACHE_REBUILD_DELAY_MIN_MS, reason);
}
private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal,
long delayMs, int reason) {
mHandler.postDelayed(() -> {
if (!mCacheValid.compareAndSet(CACHE_INVALID, CACHE_VALID)) {
// Cache is already valid.
return;
}
final long currentTimeUs = SystemClock.currentTimeMicro();
final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>();
final UserInfo[][] usersRef = new UserInfo[1][];
final Computer snapshot = (Computer) pmInternal.snapshot();
final ArrayMap<String, ? extends PackageStateInternal> settings =
snapshot.getPackageStates();
final UserInfo[] users = snapshot.getUserInfos();
packagesCache.ensureCapacity(settings.size());
usersRef[0] = users;
// store away the references to the immutable packages, since settings are retained
// during updates.
for (int i = 0, max = settings.size(); i < max; i++) {
final AndroidPackage pkg = settings.valueAt(i).getPkg();
packagesCache.put(settings.keyAt(i), pkg);
}
updateEntireShouldFilterCacheInner(snapshot, settings, usersRef[0], USER_ALL);
logCacheRebuilt(reason, SystemClock.currentTimeMicro() - currentTimeUs,
users.length, settings.size());
if (!mCacheValid.compareAndSet(CACHE_VALID, CACHE_VALID)) {
Slog.i(TAG, "Cache invalidated while building, retrying.");
updateEntireShouldFilterCacheAsync(pmInternal,
Math.min(delayMs * 2, CACHE_REBUILD_DELAY_MAX_MS), reason);
return;
}
synchronized (mImplicitlyQueryableLock) {
if (mNeedToUpdateCacheForImplicitAccess) {
updateShouldFilterCacheForImplicitAccess();
mNeedToUpdateCacheForImplicitAccess = false;
}
mCacheReady = true;
}
onChanged();
}, delayMs);
}
public void onUserCreated(Computer snapshot, int newUserId) {
if (!mCacheReady) {
return;
}
final long currentTimeUs = SystemClock.currentTimeMicro();
updateEntireShouldFilterCache(snapshot, newUserId);
logCacheRebuilt(
PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_CREATED,
SystemClock.currentTimeMicro() - currentTimeUs,
snapshot.getUserInfos().length,
snapshot.getPackageStates().size());
}
public void onUserDeleted(Computer snapshot, @UserIdInt int userId) {
if (!mCacheReady) {
return;
}
final long currentTimeUs = SystemClock.currentTimeMicro();
removeShouldFilterCacheForUser(userId);
onChanged();
logCacheRebuilt(
PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED__EVENT_TYPE__USER_DELETED,
SystemClock.currentTimeMicro() - currentTimeUs,
snapshot.getUserInfos().length,
snapshot.getPackageStates().size());
}
@GuardedBy("mImplicitlyQueryableLock")
private void updateShouldFilterCacheForImplicitAccess() {
updateShouldFilterCacheForImplicitAccess(mRetainedImplicitlyQueryable);
updateShouldFilterCacheForImplicitAccess(mImplicitlyQueryable);
}
private void updateShouldFilterCacheForImplicitAccess(
WatchedSparseSetArray<Integer> queriesMap) {
synchronized (mCacheLock) {
for (int i = 0; i < queriesMap.size(); i++) {
Integer callingUid = queriesMap.keyAt(i);
ArraySet<Integer> targetUids = queriesMap.get(callingUid);
for (Integer targetUid : targetUids) {
mShouldFilterCache.put(callingUid, targetUid, false);
}
}
}
}
private void updateShouldFilterCacheForPackage(Computer snapshot,
String packageName) {
if (!mCacheReady) {
return;
}
final ArrayMap<String, ? extends PackageStateInternal> settings =
snapshot.getPackageStates();
final UserInfo[] users = snapshot.getUserInfos();
synchronized (mCacheLock) {
updateShouldFilterCacheForPackage(snapshot, null /* skipPackage */,
settings.get(packageName), settings, users, USER_ALL,
settings.size() /*maxIndex*/);
}
onChanged();
}
@GuardedBy("mCacheLock")
private void updateShouldFilterCacheForPackage(Computer snapshot,
@Nullable String skipPackageName, PackageStateInternal subjectSetting, ArrayMap<String,
? extends PackageStateInternal> allSettings, UserInfo[] allUsers, int subjectUserId,
int maxIndex) {
for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
PackageStateInternal otherSetting = allSettings.valueAt(i);
if (subjectSetting.getAppId() == otherSetting.getAppId()) {
continue;
}
//noinspection StringEquality
if (subjectSetting.getPackageName() == skipPackageName || otherSetting.getPackageName()
== skipPackageName) {
continue;
}
if (subjectUserId == USER_ALL) {
for (int su = 0; su < allUsers.length; su++) {
updateShouldFilterCacheForUser(snapshot, subjectSetting, allUsers, otherSetting,
allUsers[su].id);
}
} else {
updateShouldFilterCacheForUser(snapshot, subjectSetting, allUsers, otherSetting,
subjectUserId);
}
}
}
@GuardedBy("mCacheLock")
private void updateShouldFilterCacheForUser(Computer snapshot,
PackageStateInternal subjectSetting, UserInfo[] allUsers,
PackageStateInternal otherSetting, int subjectUserId) {
for (int ou = 0; ou < allUsers.length; ou++) {
int otherUser = allUsers[ou].id;
int subjectUid = UserHandle.getUid(subjectUserId, subjectSetting.getAppId());
int otherUid = UserHandle.getUid(otherUser, otherSetting.getAppId());
final boolean shouldFilterSubjectToOther = shouldFilterApplicationInternal(snapshot,
subjectUid, subjectSetting, otherSetting, otherUser);
final boolean shouldFilterOtherToSubject = shouldFilterApplicationInternal(snapshot,
otherUid, otherSetting, subjectSetting, subjectUserId);
mShouldFilterCache.put(subjectUid, otherUid, shouldFilterSubjectToOther);
mShouldFilterCache.put(otherUid, subjectUid, shouldFilterOtherToSubject);
}
}
private void removeShouldFilterCacheForUser(int userId) {
synchronized (mCacheLock) {
// Sorted uids with the ascending order
final int[] cacheUids = mShouldFilterCache.keys();
final int size = cacheUids.length;
int pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId, 0));
final int fromIndex = (pos >= 0 ? pos : ~pos);
if (fromIndex >= size || UserHandle.getUserId(cacheUids[fromIndex]) != userId) {
Slog.w(TAG, "Failed to remove should filter cache for user " + userId
+ ", fromIndex=" + fromIndex);
return;
}
pos = Arrays.binarySearch(cacheUids, UserHandle.getUid(userId + 1, 0) - 1);
final int toIndex = (pos >= 0 ? pos + 1 : ~pos);
if (fromIndex >= toIndex || UserHandle.getUserId(cacheUids[toIndex - 1]) != userId) {
Slog.w(TAG, "Failed to remove should filter cache for user " + userId
+ ", fromIndex=" + fromIndex + ", toIndex=" + toIndex);
return;
}
mShouldFilterCache.removeRange(fromIndex, toIndex);
mShouldFilterCache.compact();
}
}
private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
PackageStateInternal pkgSetting) {
return pkgSetting.isSystem()
&& pkgSetting.getSigningDetails().signaturesMatchExactly(sysSigningDetails);
}
private void collectProtectedBroadcasts(
ArrayMap<String, ? extends PackageStateInternal> existingSettings,
@Nullable String excludePackage) {
synchronized (mProtectedBroadcastsLock) {
mProtectedBroadcasts.clear();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageStateInternal setting = existingSettings.valueAt(i);
if (setting.getPkg() == null || setting.getPkg().getPackageName().equals(
excludePackage)) {
continue;
}
final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts();
if (!protectedBroadcasts.isEmpty()) {
mProtectedBroadcasts.addAll(protectedBroadcasts);
}
}
}
}
@Override
protected boolean isQueryableViaComponentWhenRequireRecompute(
ArrayMap<String, ? extends PackageStateInternal> existingSettings,
PackageStateInternal callingPkgSetting,
ArraySet<PackageStateInternal> callingSharedPkgSettings,
AndroidPackage targetPkg,
int callingAppId, int targetAppId) {
// Recompute the whole mQueriesViaComponent because mProtectedBroadcasts have changed
recomputeComponentVisibility(existingSettings);
return isQueryableViaComponent(callingAppId, targetAppId);
}
/**
* This method recomputes all component / intent-based visibility and is intended to match the
* relevant logic of {@link #addPackageInternal(PackageStateInternal, ArrayMap)}
*/
private void recomputeComponentVisibility(
ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
final WatchedArraySet<String> protectedBroadcasts;
final WatchedArraySet<Integer> forceQueryable;
synchronized (mProtectedBroadcastsLock) {
protectedBroadcasts = mProtectedBroadcasts.snapshot();
}
synchronized (mForceQueryableLock) {
forceQueryable = mForceQueryable.snapshot();
}
final ParallelComputeComponentVisibility computer = new ParallelComputeComponentVisibility(
existingSettings, forceQueryable, protectedBroadcasts);
SparseSetArray<Integer> queriesViaComponent = computer.execute();
synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.copyFrom(queriesViaComponent);
}
mQueriesViaComponentRequireRecompute.set(false);
onChanged();
}
/**
* Equivalent to calling {@link #addPackage(Computer, PackageStateInternal, boolean, boolean)}
* with {@code isReplace} and {@code retainImplicitGrantOnReplace} equal to {@code false}.
*
* @see AppsFilterImpl#addPackage(Computer, PackageStateInternal, boolean, boolean)
*/
public void addPackage(Computer snapshot, PackageStateInternal newPkgSetting) {
addPackage(snapshot, newPkgSetting, false /* isReplace */,
false /* retainImplicitGrantOnReplace */);
}
/**
* Removes a package for consideration when filtering visibility between apps.
*
* @param setting the setting of the package being removed.
*/
public void removePackage(Computer snapshot, PackageStateInternal setting) {
final long currentTimeUs = SystemClock.currentTimeMicro();
removePackageInternal(snapshot, setting,
false /* isReplace */, false /* retainImplicitGrantOnReplace */);
logCacheUpdated(
PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED__EVENT_TYPE__PACKAGE_DELETED,
SystemClock.currentTimeMicro() - currentTimeUs,
snapshot.getUserInfos().length,
snapshot.getPackageStates().size(),
setting.getAppId());
}
/**
* Removes a package for consideration when filtering visibility between apps.
*
* @param setting the setting of the package being removed.
* @param isReplace if the package is being replaced.
* @param retainImplicitGrantOnReplace {@code true} to retain implicit grant access if
* the package is being replaced.
*/
private void removePackageInternal(Computer snapshot, PackageStateInternal setting,
boolean isReplace, boolean retainImplicitGrantOnReplace) {
final ArraySet<String> additionalChangedPackages;
final ArrayMap<String, ? extends PackageStateInternal> settings =
snapshot.getPackageStates();
final UserInfo[] users = snapshot.getUserInfos();
final int userCount = users.length;
if (!isReplace || !retainImplicitGrantOnReplace) {
synchronized (mImplicitlyQueryableLock) {
for (int u = 0; u < userCount; u++) {
final int userId = users[u].id;
final int removingUid = UserHandle.getUid(userId, setting.getAppId());
mImplicitlyQueryable.remove(removingUid);
for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i),
removingUid);
}
if (isReplace) {
continue;
}
mRetainedImplicitlyQueryable.remove(removingUid);
for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) {
mRetainedImplicitlyQueryable.remove(
mRetainedImplicitlyQueryable.keyAt(i), removingUid);
}
}
}
}
if (!mQueriesViaComponentRequireRecompute.get()) {
synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.remove(setting.getAppId());
for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.getAppId());
}
}
}
synchronized (mQueriesViaPackageLock) {
mQueriesViaPackage.remove(setting.getAppId());
for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i),
setting.getAppId());
}
}
synchronized (mQueryableViaUsesLibraryLock) {
mQueryableViaUsesLibrary.remove(setting.getAppId());
for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i),
setting.getAppId());
}
}
synchronized (mQueryableViaUsesPermissionLock) {
if (setting.getPkg() != null && !setting.getPkg().getPermissions().isEmpty()) {
for (ParsedPermission permission : setting.getPkg().getPermissions()) {
String permissionName = permission.getName();
if (mPermissionToUids.containsKey(permissionName)) {
mPermissionToUids.get(permissionName).remove(setting.getAppId());
if (mPermissionToUids.get(permissionName).isEmpty()) {
mPermissionToUids.remove(permissionName);
}
}
}
}
if (setting.getPkg() != null && !setting.getPkg().getUsesPermissions().isEmpty()) {
for (ParsedUsesPermission usesPermission : setting.getPkg().getUsesPermissions()) {
String usesPermissionName = usesPermission.getName();
if (mUsesPermissionToUids.containsKey(usesPermissionName)) {
mUsesPermissionToUids.get(usesPermissionName).remove(setting.getAppId());
if (mUsesPermissionToUids.get(usesPermissionName).isEmpty()) {
mUsesPermissionToUids.remove(usesPermissionName);
}
}
}
}
mQueryableViaUsesPermission.remove(setting.getAppId());
}
synchronized (mForceQueryableLock) {
mForceQueryable.remove(setting.getAppId());
}
boolean protectedBroadcastsChanged = false;
synchronized (mProtectedBroadcastsLock) {
if (setting.getPkg() != null
&& !setting.getPkg().getProtectedBroadcasts().isEmpty()) {
final String removingPackageName = setting.getPkg().getPackageName();
final ArrayList<String> protectedBroadcasts = new ArrayList<>(
mProtectedBroadcasts.untrackedStorage());
collectProtectedBroadcasts(settings, removingPackageName);
for (int i = 0; i < protectedBroadcasts.size(); ++i) {
if (!mProtectedBroadcasts.contains(protectedBroadcasts.get(i))) {
protectedBroadcastsChanged = true;
break;
}
}
}
}
if (protectedBroadcastsChanged) {
mQueriesViaComponentRequireRecompute.set(true);
}
additionalChangedPackages = mOverlayReferenceMapper.removePkg(setting.getPackageName());
mFeatureConfig.updatePackageState(setting, true /*removed*/);
// After removing all traces of the package, if it's part of a shared user, re-add other
// shared user members to re-establish visibility between them and other packages.
// NOTE: this must come after all removals from data structures but before we update the
// cache
final SharedUserApi sharedUserApi = setting.hasSharedUser()
? snapshot.getSharedUser(setting.getSharedUserAppId()) : null;
if (sharedUserApi != null) {
final ArraySet<? extends PackageStateInternal> sharedUserPackages =
sharedUserApi.getPackageStates();
for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
if (sharedUserPackages.valueAt(i) == setting) {
continue;
}
addPackageInternal(
sharedUserPackages.valueAt(i), settings);
}
}
if (mCacheReady) {
removeAppIdFromVisibilityCache(setting.getAppId());
if (sharedUserApi != null) {
final ArraySet<? extends PackageStateInternal> sharedUserPackages =
sharedUserApi.getPackageStates();
for (int i = sharedUserPackages.size() - 1; i >= 0; i--) {
PackageStateInternal siblingSetting =
sharedUserPackages.valueAt(i);
if (siblingSetting == setting) {
continue;
}
synchronized (mCacheLock) {
updateShouldFilterCacheForPackage(snapshot,
setting.getPackageName(), siblingSetting, settings,
users, USER_ALL, settings.size());
}
break;
}
}
if (additionalChangedPackages != null) {
for (int index = 0; index < additionalChangedPackages.size(); index++) {
String changedPackage = additionalChangedPackages.valueAt(index);
PackageStateInternal changedPkgSetting = settings.get(changedPackage);
if (changedPkgSetting == null) {
// It's possible for the overlay mapper to know that an actor
// package changed via an explicit reference, even if the actor
// isn't installed, so skip if that's the case.
continue;
}
synchronized (mCacheLock) {
updateShouldFilterCacheForPackage(snapshot, null, changedPkgSetting,
settings, users, USER_ALL, settings.size());
}
}
}
} else {
invalidateCache("removePackage: " + setting.getPackageName());
}
onChanged();
}
/** Returns {@code true} if the source package instruments the target package. */
private static boolean pkgInstruments(
@NonNull AndroidPackage source, @NonNull AndroidPackage target) {
try {
if (DEBUG_TRACING) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
}
final String packageName = target.getPackageName();
final List<ParsedInstrumentation> inst = source.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
if (Objects.equals(inst.get(i).getTargetPackage(), packageName)) {
return true;
}
}
return false;
} finally {
if (DEBUG_TRACING) {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
}
private void logCacheRebuilt(int eventId, long latency, int userCount, int packageCount) {
FrameworkStatsLog.write(PACKAGE_MANAGER_APPS_FILTER_CACHE_BUILD_REPORTED,
eventId, latency, userCount, packageCount, mShouldFilterCache.size());
}
private void logCacheUpdated(int eventId, long latency, int userCount, int packageCount,
int appId) {
if (!mCacheReady) {
return;
}
FrameworkStatsLog.write(PACKAGE_MANAGER_APPS_FILTER_CACHE_UPDATE_REPORTED,
eventId, appId, latency, userCount, packageCount, mShouldFilterCache.size());
}
}