Merge changes Iccf02e4d,Ibc931dab,Iba80142b into sc-dev

* changes:
  Watch ComponentResolver
  Thread WatchedIntentFilter in PackageManagerService
  WatchedIntentResolver now uses WatchedIntentFilter
This commit is contained in:
Lee Shombert
2021-05-04 22:46:21 +00:00
committed by Android (Google) Code Review
15 changed files with 645 additions and 122 deletions

View File

@@ -839,33 +839,45 @@ public abstract class IntentResolver<F, R extends Object> {
}
};
// Method to take the snapshot of an F.
protected F snapshot(F f) {
return f;
}
// Helper method to copy some of the maps.
private static <E> void copyInto(ArrayMap<String, E[]> l, ArrayMap<String, E[]> r) {
protected void copyInto(ArrayMap<String, F[]> l, ArrayMap<String, F[]> r) {
final int end = r.size();
l.clear();
l.ensureCapacity(end);
for (int i = 0; i < end; i++) {
final F[] val = r.valueAt(i);
final String key = r.keyAt(i);
final F[] newval = Arrays.copyOf(val, val.length);
for (int j = 0; j < newval.length; j++) {
newval[j] = snapshot(newval[j]);
}
l.put(key, newval);
}
}
protected void copyInto(ArraySet<F> l, ArraySet<F> r) {
l.clear();
final int end = r.size();
l.ensureCapacity(end);
for (int i = 0; i < end; i++) {
final E[] val = r.valueAt(i);
final String key = r.keyAt(i);
l.put(key, Arrays.copyOf(val, val.length));
l.append(snapshot(r.valueAt(i)));
}
}
// Make <this> a copy of <orig>. The presumption is that <this> is empty but all
// arrays are cleared out explicitly, just to be sure.
protected void copyFrom(IntentResolver orig) {
mFilters.clear();
mFilters.addAll(orig.mFilters);
mTypeToFilter.clear();
copyInto(mFilters, orig.mFilters);
copyInto(mTypeToFilter, orig.mTypeToFilter);
mBaseTypeToFilter.clear();
copyInto(mBaseTypeToFilter, orig.mBaseTypeToFilter);
mWildTypeToFilter.clear();
copyInto(mWildTypeToFilter, orig.mWildTypeToFilter);
mSchemeToFilter.clear();
copyInto(mSchemeToFilter, orig.mSchemeToFilter);
mActionToFilter.clear();
copyInto(mActionToFilter, orig.mActionToFilter);
mTypedActionToFilter.clear();
copyInto(mTypedActionToFilter, orig.mTypedActionToFilter);
}

View File

@@ -19,10 +19,13 @@ package com.android.server;
import android.annotation.NonNull;
import android.annotation.Nullable;
import com.android.server.pm.WatchedIntentFilter;
import com.android.server.utils.Snappable;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
import com.android.server.utils.Watcher;
import java.util.ArrayList;
import java.util.List;
/**
@@ -31,9 +34,9 @@ import java.util.List;
* @param <R> The resolver type.
* {@hide}
*/
public abstract class WatchedIntentResolver<F, R extends Object>
public abstract class WatchedIntentResolver<F extends Watchable, R extends Object>
extends IntentResolver<F, R>
implements Watchable {
implements Watchable, Snappable {
/**
* Watchable machinery
@@ -78,6 +81,13 @@ public abstract class WatchedIntentResolver<F, R extends Object>
mWatchable.dispatchChange(what);
}
private final Watcher mWatcher = new Watcher() {
@Override
public void onChange(@Nullable Watchable what) {
dispatchChange(what);
}
};
/**
* Notify listeners that this object has changed.
*/
@@ -88,17 +98,20 @@ public abstract class WatchedIntentResolver<F, R extends Object>
@Override
public void addFilter(F f) {
super.addFilter(f);
f.registerObserver(mWatcher);
onChanged();
}
@Override
public void removeFilter(F f) {
f.unregisterObserver(mWatcher);
super.removeFilter(f);
onChanged();
}
@Override
protected void removeFilterInternal(F f) {
f.unregisterObserver(mWatcher);
super.removeFilterInternal(f);
onChanged();
}
@@ -109,4 +122,17 @@ public abstract class WatchedIntentResolver<F, R extends Object>
super.sortResults(results);
onChanged();
}
/**
* @see IntentResolver#findFilters(IntentFilter)
*/
public ArrayList<F> findFilters(WatchedIntentFilter matching) {
return super.findFilters(matching.getIntentFilter());
}
// Make <this> a copy of <orig>. The presumption is that <this> is empty but all
// arrays are cleared out explicitly, just to be sure.
protected void copyFrom(WatchedIntentResolver orig) {
super.copyFrom(orig);
}
}

View File

@@ -59,6 +59,9 @@ import com.android.server.IntentResolver;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.WatchableImpl;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -72,12 +75,19 @@ import java.util.Set;
import java.util.function.Function;
/** Resolves all Android component types [activities, services, providers and receivers]. */
public class ComponentResolver {
public class ComponentResolver
extends WatchableImpl
implements Snappable {
private static final boolean DEBUG = false;
private static final String TAG = "PackageManager";
private static final boolean DEBUG_FILTERS = false;
private static final boolean DEBUG_SHOW_INFO = false;
// Convenience function to report that this object has changed.
private void onChanged() {
dispatchChange(this);
}
/**
* The set of all protected actions [i.e. those actions for which a high priority
* intent filter is disallowed].
@@ -158,27 +168,27 @@ public class ComponentResolver {
* would be able to hold its lock while checking the package setting state.</li>
* </ol>
*/
private final Object mLock;
private final PackageManagerTracedLock mLock;
/** All available activities, for your resolving pleasure. */
@GuardedBy("mLock")
private final ActivityIntentResolver mActivities = new ActivityIntentResolver();
private final ActivityIntentResolver mActivities;
/** All available providers, for your resolving pleasure. */
@GuardedBy("mLock")
private final ProviderIntentResolver mProviders = new ProviderIntentResolver();
private final ProviderIntentResolver mProviders;
/** All available receivers, for your resolving pleasure. */
@GuardedBy("mLock")
private final ActivityIntentResolver mReceivers = new ReceiverIntentResolver();
private final ReceiverIntentResolver mReceivers;
/** All available services, for your resolving pleasure. */
@GuardedBy("mLock")
private final ServiceIntentResolver mServices = new ServiceIntentResolver();
private final ServiceIntentResolver mServices;
/** Mapping from provider authority [first directory in content URI codePath) to provider. */
@GuardedBy("mLock")
private final ArrayMap<String, ParsedProvider> mProvidersByAuthority = new ArrayMap<>();
private final ArrayMap<String, ParsedProvider> mProvidersByAuthority;
/** Whether or not processing protected filters should be deferred. */
private boolean mDeferProtectedFilters = true;
@@ -200,12 +210,57 @@ public class ComponentResolver {
ComponentResolver(UserManagerService userManager,
PackageManagerInternal packageManagerInternal,
Object lock) {
PackageManagerTracedLock lock) {
sPackageManagerInternal = packageManagerInternal;
sUserManager = userManager;
mLock = lock;
mActivities = new ActivityIntentResolver();
mProviders = new ProviderIntentResolver();
mReceivers = new ReceiverIntentResolver();
mServices = new ServiceIntentResolver();
mProvidersByAuthority = new ArrayMap<>();
mDeferProtectedFilters = true;
mSnapshot = new SnapshotCache<ComponentResolver>(this, this) {
@Override
public ComponentResolver createSnapshot() {
return new ComponentResolver(mSource);
}};
}
// Copy constructor used in creating snapshots.
private ComponentResolver(ComponentResolver orig) {
// Do not set the static variables that are set in the default constructor. Do
// create a new object for the lock. The snapshot is read-only, so a lock is not
// strictly required. However, the current code is simpler if the lock exists,
// but does not contend with any outside class.
// TODO: make the snapshot lock-free
mLock = new PackageManagerTracedLock();
mActivities = new ActivityIntentResolver(orig.mActivities);
mProviders = new ProviderIntentResolver(orig.mProviders);
mReceivers = new ReceiverIntentResolver(orig.mReceivers);
mServices = new ServiceIntentResolver(orig.mServices);
mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
mDeferProtectedFilters = orig.mDeferProtectedFilters;
mProtectedFilters = (mProtectedFilters == null)
? null
: new ArrayList<>(orig.mProtectedFilters);
mSnapshot = null;
}
final SnapshotCache<ComponentResolver> mSnapshot;
/**
* Create a snapshot.
*/
public ComponentResolver snapshot() {
return mSnapshot.snapshot();
}
/** Returns the given activity */
@Nullable
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -474,6 +529,7 @@ public class ComponentResolver {
addReceiversLocked(pkg, chatty);
addProvidersLocked(pkg, chatty);
addServicesLocked(pkg, chatty);
onChanged();
}
// expect single setupwizard package
final String setupWizardPackage = ArrayUtils.firstOrNull(
@@ -489,6 +545,7 @@ public class ComponentResolver {
final List<ParsedActivity> systemActivities =
disabledPkg != null ? disabledPkg.getActivities() : null;
adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
onChanged();
}
}
@@ -496,6 +553,7 @@ public class ComponentResolver {
void removeAllComponents(AndroidPackage pkg, boolean chatty) {
synchronized (mLock) {
removeAllComponentsLocked(pkg, chatty);
onChanged();
}
}
@@ -504,51 +562,54 @@ public class ComponentResolver {
* all of the filters defined on the /system partition and know the special components.
*/
void fixProtectedFilterPriorities() {
if (!mDeferProtectedFilters) {
return;
}
mDeferProtectedFilters = false;
synchronized (mLock) {
if (!mDeferProtectedFilters) {
return;
}
mDeferProtectedFilters = false;
if (mProtectedFilters == null || mProtectedFilters.size() == 0) {
return;
}
final List<Pair<ParsedMainComponent, ParsedIntentInfo>> protectedFilters =
mProtectedFilters;
mProtectedFilters = null;
if (mProtectedFilters == null || mProtectedFilters.size() == 0) {
return;
}
final List<Pair<ParsedMainComponent, ParsedIntentInfo>> protectedFilters =
mProtectedFilters;
mProtectedFilters = null;
// expect single setupwizard package
final String setupWizardPackage = ArrayUtils.firstOrNull(
// expect single setupwizard package
final String setupWizardPackage = ArrayUtils.firstOrNull(
sPackageManagerInternal.getKnownPackageNames(
PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
if (DEBUG_FILTERS && setupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
}
for (int i = protectedFilters.size() - 1; i >= 0; --i) {
final Pair<ParsedMainComponent, ParsedIntentInfo> pair = protectedFilters.get(i);
ParsedMainComponent component = pair.first;
ParsedIntentInfo filter = pair.second;
String packageName = component.getPackageName();
String className = component.getClassName();
if (packageName.equals(setupWizardPackage)) {
if (DEBUG_FILTERS && setupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
}
for (int i = protectedFilters.size() - 1; i >= 0; --i) {
final Pair<ParsedMainComponent, ParsedIntentInfo> pair = protectedFilters.get(i);
ParsedMainComponent component = pair.first;
ParsedIntentInfo filter = pair.second;
String packageName = component.getPackageName();
String className = component.getClassName();
if (packageName.equals(setupWizardPackage)) {
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found setup wizard;"
+ " allow priority " + filter.getPriority() + ";"
+ " package: " + packageName
+ " activity: " + className
+ " priority: " + filter.getPriority());
}
// skip setup wizard; allow it to keep the high priority filter
continue;
}
if (DEBUG_FILTERS) {
Slog.i(TAG, "Found setup wizard;"
+ " allow priority " + filter.getPriority() + ";"
Slog.i(TAG, "Protected action; cap priority to 0;"
+ " package: " + packageName
+ " activity: " + className
+ " priority: " + filter.getPriority());
+ " origPrio: " + filter.getPriority());
}
// skip setup wizard; allow it to keep the high priority filter
continue;
filter.setPriority(0);
}
if (DEBUG_FILTERS) {
Slog.i(TAG, "Protected action; cap priority to 0;"
+ " package: " + packageName
+ " activity: " + className
+ " origPrio: " + filter.getPriority());
}
filter.setPriority(0);
onChanged();
}
}
@@ -1181,9 +1242,20 @@ public class ComponentResolver {
private abstract static class MimeGroupsAwareIntentResolver<F extends Pair<?
extends ParsedComponent, ParsedIntentInfo>, R>
extends IntentResolver<F, R> {
private ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
private boolean mIsUpdatingMimeGroup = false;
// Default constructor
MimeGroupsAwareIntentResolver() {
}
// Copy constructor used in creating snapshots
MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig) {
copyFrom(orig);
copyInto(mMimeGroupToFilter, orig.mMimeGroupToFilter);
mIsUpdatingMimeGroup = orig.mIsUpdatingMimeGroup;
}
@Override
public void addFilter(F f) {
IntentFilter intentFilter = getIntentFilter(f);
@@ -1282,6 +1354,17 @@ public class ComponentResolver {
private static class ActivityIntentResolver
extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
ActivityIntentResolver() {
}
// Copy constructor used in creating snapshots
ActivityIntentResolver(ActivityIntentResolver orig) {
super(orig);
mActivities.putAll(orig.mActivities);
mFlags = orig.mFlags;
}
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
@@ -1330,7 +1413,7 @@ public class ComponentResolver {
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
}
private void addActivity(ParsedActivity a, String type,
protected void addActivity(ParsedActivity a, String type,
List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
mActivities.put(a.getComponentName(), a);
if (DEBUG_SHOW_INFO) {
@@ -1354,7 +1437,7 @@ public class ComponentResolver {
}
}
private void removeActivity(ParsedActivity a, String type) {
protected void removeActivity(ParsedActivity a, String type) {
mActivities.remove(a.getComponentName());
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " " + type + ":");
@@ -1567,8 +1650,11 @@ public class ComponentResolver {
return pkg.getActivities();
}
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, ParsedActivity> mActivities =
// Keys are String (activity class name), values are Activity. This attribute is
// protected because it is accessed directly from ComponentResolver. That works
// even if the attribute is private, but fails for subclasses of
// ActivityIntentResolver.
protected final ArrayMap<ComponentName, ParsedActivity> mActivities =
new ArrayMap<>();
private int mFlags;
}
@@ -1576,6 +1662,15 @@ public class ComponentResolver {
// Both receivers and activities share a class, but point to different get methods
private static final class ReceiverIntentResolver extends ActivityIntentResolver {
// Default constructor
ReceiverIntentResolver() {
}
// Copy constructor used in creating snapshots
ReceiverIntentResolver(ReceiverIntentResolver orig) {
super(orig);
}
@Override
protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
return pkg.getReceivers();
@@ -1584,6 +1679,17 @@ public class ComponentResolver {
private static final class ProviderIntentResolver
extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
ProviderIntentResolver() {
}
// Copy constructor used in creating snapshots
ProviderIntentResolver(ProviderIntentResolver orig) {
super(orig);
mProviders.putAll(orig.mProviders);
mFlags = orig.mFlags;
}
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
@@ -1829,6 +1935,17 @@ public class ComponentResolver {
private static final class ServiceIntentResolver
extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
// Default constructor
ServiceIntentResolver() {
}
// Copy constructor used in creating snapshots
ServiceIntentResolver(ServiceIntentResolver orig) {
copyFrom(orig);
mServices.putAll(orig.mServices);
mFlags = orig.mFlags;
}
@Override
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
@@ -2213,11 +2330,16 @@ public class ComponentResolver {
* @return true if any intent filters were changed due to this update
*/
boolean updateMimeGroup(String packageName, String group) {
boolean hasChanges = mActivities.updateMimeGroup(packageName, group);
hasChanges |= mProviders.updateMimeGroup(packageName, group);
hasChanges |= mReceivers.updateMimeGroup(packageName, group);
hasChanges |= mServices.updateMimeGroup(packageName, group);
boolean hasChanges = false;
synchronized (mLock) {
hasChanges |= mActivities.updateMimeGroup(packageName, group);
hasChanges |= mProviders.updateMimeGroup(packageName, group);
hasChanges |= mReceivers.updateMimeGroup(packageName, group);
hasChanges |= mServices.updateMimeGroup(packageName, group);
if (hasChanges) {
onChanged();
}
}
return hasChanges;
}
}

View File

@@ -24,6 +24,7 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.utils.SnapshotCache;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -35,7 +36,7 @@ import java.io.IOException;
* If an {@link Intent} matches the {@link CrossProfileIntentFilter}, then activities in the user
* {@link #mTargetUserId} can access it.
*/
class CrossProfileIntentFilter extends IntentFilter {
class CrossProfileIntentFilter extends WatchedIntentFilter {
private static final String ATTR_TARGET_USER_ID = "targetUserId";
private static final String ATTR_FLAGS = "flags";
private static final String ATTR_OWNER_PACKAGE = "ownerPackage";
@@ -48,12 +49,41 @@ class CrossProfileIntentFilter extends IntentFilter {
final String mOwnerPackage; // packageName of the app.
final int mFlags;
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
final SnapshotCache<CrossProfileIntentFilter> mSnapshot;
private SnapshotCache makeCache() {
return new SnapshotCache<CrossProfileIntentFilter>(this, this) {
@Override
public CrossProfileIntentFilter createSnapshot() {
CrossProfileIntentFilter s = new CrossProfileIntentFilter(mSource);
s.seal();
return s;
}};
}
CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId,
int flags) {
super(filter);
mTargetUserId = targetUserId;
mOwnerPackage = ownerPackage;
mFlags = flags;
mSnapshot = makeCache();
}
CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId,
int flags) {
this(filter.mFilter, ownerPackage, targetUserId, flags);
}
// Copy constructor used only to create a snapshot.
private CrossProfileIntentFilter(CrossProfileIntentFilter f) {
super(f);
mTargetUserId = f.mTargetUserId;
mOwnerPackage = f.mOwnerPackage;
mFlags = f.mFlags;
mSnapshot = new SnapshotCache.Sealed();
}
public int getTargetUserId() {
@@ -72,6 +102,7 @@ class CrossProfileIntentFilter extends IntentFilter {
mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL);
mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, "");
mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0);
mSnapshot = makeCache();
int outerDepth = parser.getDepth();
String tagName = parser.getName();
@@ -94,7 +125,7 @@ class CrossProfileIntentFilter extends IntentFilter {
}
}
if (tagName.equals(ATTR_FILTER)) {
readFromXml(parser);
mFilter.readFromXml(parser);
} else {
String msg = "Missing element under " + TAG + ": " + ATTR_FILTER +
" at " + parser.getPositionDescription();
@@ -103,7 +134,8 @@ class CrossProfileIntentFilter extends IntentFilter {
}
}
String getStringFromXml(TypedXmlPullParser parser, String attribute, String defaultValue) {
private String getStringFromXml(TypedXmlPullParser parser, String attribute,
String defaultValue) {
String value = parser.getAttributeValue(null, attribute);
if (value == null) {
String msg = "Missing element under " + TAG +": " + attribute + " at " +
@@ -120,7 +152,7 @@ class CrossProfileIntentFilter extends IntentFilter {
serializer.attributeInt(null, ATTR_FLAGS, mFlags);
serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage);
serializer.startTag(null, ATTR_FILTER);
super.writeToXml(serializer);
mFilter.writeToXml(serializer);
serializer.endTag(null, ATTR_FILTER);
}
@@ -135,4 +167,8 @@ class CrossProfileIntentFilter extends IntentFilter {
&& mOwnerPackage.equals(other.mOwnerPackage)
&& mFlags == other.mFlags;
}
public CrossProfileIntentFilter snapshot() {
return mSnapshot.snapshot();
}
}

View File

@@ -21,6 +21,7 @@ import android.content.IntentFilter;
import com.android.server.WatchedIntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import java.util.List;
@@ -47,7 +48,34 @@ class CrossProfileIntentResolver
@Override
protected IntentFilter getIntentFilter(@NonNull CrossProfileIntentFilter input) {
return input;
return input.getIntentFilter();
}
CrossProfileIntentResolver() {
mSnapshot = makeCache();
}
// Take the snapshot of F
protected CrossProfileIntentFilter snapshot(CrossProfileIntentFilter f) {
return (f == null) ? null : f.snapshot();
}
// Copy constructor used only to create a snapshot.
private CrossProfileIntentResolver(CrossProfileIntentResolver f) {
copyFrom(f);
mSnapshot = new SnapshotCache.Sealed();
}
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
final SnapshotCache<CrossProfileIntentResolver> mSnapshot;
private SnapshotCache makeCache() {
return new SnapshotCache<CrossProfileIntentResolver>(this, this) {
@Override
public CrossProfileIntentResolver createSnapshot() {
return new CrossProfileIntentResolver(mSource);
}};
}
/**
@@ -56,8 +84,6 @@ class CrossProfileIntentResolver
* @return A snapshot of the current object.
*/
public CrossProfileIntentResolver snapshot() {
CrossProfileIntentResolver result = new CrossProfileIntentResolver();
result.copyFrom(this);
return result;
return mSnapshot.snapshot();
}
}

View File

@@ -43,7 +43,7 @@ final class DefaultCrossProfileIntentFilter {
}
/** The intent filter that's used */
public final IntentFilter filter;
public final WatchedIntentFilter filter;
/**
* The flags related to the forwarding, e.g.
@@ -66,7 +66,7 @@ final class DefaultCrossProfileIntentFilter {
*/
public final boolean letsPersonalDataIntoProfile;
private DefaultCrossProfileIntentFilter(IntentFilter filter, int flags,
private DefaultCrossProfileIntentFilter(WatchedIntentFilter filter, int flags,
@Direction int direction, boolean letsPersonalDataIntoProfile) {
this.filter = requireNonNull(filter);
this.flags = flags;
@@ -75,7 +75,7 @@ final class DefaultCrossProfileIntentFilter {
}
static final class Builder {
private IntentFilter mFilter = new IntentFilter();
private WatchedIntentFilter mFilter = new WatchedIntentFilter();
private int mFlags;
private @Direction int mDirection;
private boolean mLetsPersonalDataIntoProfile;

View File

@@ -1482,7 +1482,9 @@ public class PackageManagerService extends IPackageManager.Stub
// Internal interface for permission manager
private final PermissionManagerServiceInternal mPermissionManager;
@Watched
private final ComponentResolver mComponentResolver;
// List of packages names to keep cached, even if they are uninstalled for all users
private List<String> mKeepUninstalledPackages;
@@ -1826,6 +1828,7 @@ public class PackageManagerService extends IPackageManager.Stub
public final ApplicationInfo androidApplication;
public final String appPredictionServicePackage;
public final AppsFilter appsFilter;
public final ComponentResolver componentResolver;
public final PackageManagerService service;
Snapshot(int type) {
@@ -1851,6 +1854,7 @@ public class PackageManagerService extends IPackageManager.Stub
: new ApplicationInfo(mAndroidApplication);
appPredictionServicePackage = mAppPredictionServicePackage;
appsFilter = mAppsFilter.snapshot();
componentResolver = mComponentResolver.snapshot();
} else if (type == Snapshot.LIVE) {
settings = mSettings;
isolatedOwners = mIsolatedOwners;
@@ -1867,6 +1871,7 @@ public class PackageManagerService extends IPackageManager.Stub
androidApplication = mAndroidApplication;
appPredictionServicePackage = mAppPredictionServicePackage;
appsFilter = mAppsFilter;
componentResolver = mComponentResolver;
} else {
throw new IllegalArgumentException();
}
@@ -1956,8 +1961,8 @@ public class PackageManagerService extends IPackageManager.Stub
int callingUid);
ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
String resolvedType, int flags, int sourceUserId);
ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId,
int targetUserId);
ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId);
ResolveInfo queryCrossProfileIntents(List<CrossProfileIntentFilter> matchingFilters,
Intent intent, String resolvedType, int flags, int sourceUserId,
boolean matchInCurrentProfile);
@@ -2114,6 +2119,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInstantAppRegistry = args.instantAppRegistry;
mLocalAndroidApplication = args.androidApplication;
mAppsFilter = args.appsFilter;
mComponentResolver = args.componentResolver;
mAppPredictionServicePackage = args.appPredictionServicePackage;
@@ -2124,7 +2130,6 @@ public class PackageManagerService extends IPackageManager.Stub
mContext = args.service.mContext;
mInjector = args.service.mInjector;
mApexManager = args.service.mApexManager;
mComponentResolver = args.service.mComponentResolver;
mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
mDefaultAppProvider = args.service.mDefaultAppProvider;
mDomainVerificationManager = args.service.mDomainVerificationManager;
@@ -2860,8 +2865,8 @@ public class PackageManagerService extends IPackageManager.Stub
}
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
sourceUserId, parentUserId);
result.resolveInfo = createForwardingResolveInfoUnchecked(
new WatchedIntentFilter(), sourceUserId, parentUserId);
}
result.highestApprovalLevel = Math.max(mDomainVerificationManager
@@ -3469,15 +3474,15 @@ public class PackageManagerService extends IPackageManager.Stub
for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
& ApplicationInfo.FLAG_SUSPENDED) == 0) {
return createForwardingResolveInfoUnchecked(filter, sourceUserId,
targetUserId);
return createForwardingResolveInfoUnchecked(filter,
sourceUserId, targetUserId);
}
}
}
return null;
}
public ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
int sourceUserId, int targetUserId) {
ResolveInfo forwardingResolveInfo = new ResolveInfo();
final long ident = Binder.clearCallingIdentity();
@@ -3507,7 +3512,7 @@ public class PackageManagerService extends IPackageManager.Stub
forwardingResolveInfo.preferredOrder = 0;
forwardingResolveInfo.match = 0;
forwardingResolveInfo.isDefault = true;
forwardingResolveInfo.filter = filter;
forwardingResolveInfo.filter = new IntentFilter(filter.getIntentFilter());
forwardingResolveInfo.targetUserId = targetUserId;
return forwardingResolveInfo;
}
@@ -6144,6 +6149,7 @@ public class PackageManagerService extends IPackageManager.Stub
mInstantAppRegistry.registerObserver(mWatcher);
mSettings.registerObserver(mWatcher);
mIsolatedOwners.registerObserver(mWatcher);
mComponentResolver.registerObserver(mWatcher);
// If neither "build" attribute is true then this may be a mockito test, and verification
// can fail as a false positive.
Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
@@ -9457,6 +9463,15 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
IntentFilter filter, int match, ComponentName activity) {
setLastChosenActivity(intent, resolvedType, flags,
new WatchedIntentFilter(filter), match, activity);
}
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
WatchedIntentFilter filter, int match, ComponentName activity) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return;
}
@@ -9477,7 +9492,7 @@ public class PackageManagerService extends IPackageManager.Stub
findPreferredActivityNotLocked(
intent, resolvedType, flags, query, 0, false, true, false, userId);
// Add the new activity as the last chosen for this filter
addPreferredActivityInternal(filter, match, null, activity, false, userId,
addPreferredActivity(filter, match, null, activity, false, userId,
"Setting last chosen", false);
}
@@ -10195,12 +10210,6 @@ public class PackageManagerService extends IPackageManager.Stub
resolvedType, flags, sourceUserId);
}
private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
int sourceUserId, int targetUserId) {
return liveComputer().createForwardingResolveInfoUnchecked(filter,
sourceUserId, targetUserId);
}
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
Intent[] specifics, String[] specificTypes, Intent intent,
@@ -16477,7 +16486,7 @@ public class PackageManagerService extends IPackageManager.Stub
return new ParceledListSlice<IntentFilter>(result) {
@Override
protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
// IntentFilter has final Parcelable methods, so redirect to the subclass
// WatchedIntentFilter has final Parcelable methods, so redirect to the subclass
((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
callFlags);
}
@@ -21964,11 +21973,14 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void addPreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId, boolean removeExisting) {
addPreferredActivityInternal(filter, match, set, activity, true, userId,
addPreferredActivity(new WatchedIntentFilter(filter), match, set, activity, true, userId,
"Adding preferred", removeExisting);
}
private void addPreferredActivityInternal(IntentFilter filter, int match,
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
public void addPreferredActivity(WatchedIntentFilter filter, int match,
ComponentName[] set, ComponentName activity, boolean always, int userId,
String opname, boolean removeExisting) {
// writer
@@ -22034,6 +22046,15 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void replacePreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
replacePreferredActivity(new WatchedIntentFilter(filter), match,
set, activity, userId);
}
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
public void replacePreferredActivity(WatchedIntentFilter filter, int match,
ComponentName[] set, ComponentName activity, int userId) {
if (filter.countActions() != 1) {
throw new IllegalArgumentException(
"replacePreferredActivity expects filter to have only 1 action.");
@@ -22106,7 +22127,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
}
addPreferredActivityInternal(filter, match, set, activity, true, userId,
addPreferredActivity(filter, match, set, activity, true, userId,
"Replacing preferred", false);
}
@@ -22213,6 +22234,22 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public int getPreferredActivities(List<IntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
List<WatchedIntentFilter> temp =
WatchedIntentFilter.toWatchedIntentFilterList(outFilters);
final int result = getPreferredActivitiesInternal(
temp, outActivities, packageName);
outFilters.clear();
for (int i = 0; i < temp.size(); i++) {
outFilters.add(temp.get(i).getIntentFilter());
}
return result;
}
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
public int getPreferredActivitiesInternal(List<WatchedIntentFilter> outFilters,
List<ComponentName> outActivities, String packageName) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return 0;
}
@@ -22229,7 +22266,7 @@ public class PackageManagerService extends IPackageManager.Stub
|| (pa.mPref.mComponent.getPackageName().equals(packageName)
&& pa.mPref.mAlways)) {
if (outFilters != null) {
outFilters.add(new IntentFilter(pa));
outFilters.add(new WatchedIntentFilter(pa.getIntentFilter()));
}
if (outActivities != null) {
outActivities.add(pa.mPref.mComponent);
@@ -22245,6 +22282,14 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
int userId) {
addPersistentPreferredActivity(new WatchedIntentFilter(filter), activity, userId);
}
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
public void addPersistentPreferredActivity(WatchedIntentFilter filter, ComponentName activity,
int userId) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException(
@@ -22488,6 +22533,15 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
int sourceUserId, int targetUserId, int flags) {
addCrossProfileIntentFilter(new WatchedIntentFilter(intentFilter), ownerPackage,
sourceUserId, targetUserId, flags);
}
/**
* Variant that takes a {@link WatchedIntentFilter}
*/
public void addCrossProfileIntentFilter(WatchedIntentFilter intentFilter, String ownerPackage,
int sourceUserId, int targetUserId, int flags) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
int callingUid = Binder.getCallingUid();
@@ -22617,8 +22671,8 @@ public class PackageManagerService extends IPackageManager.Stub
return liveComputer().getHomeIntent();
}
private IntentFilter getHomeFilter() {
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
private WatchedIntentFilter getHomeFilter() {
WatchedIntentFilter filter = new WatchedIntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_HOME);
filter.addCategory(Intent.CATEGORY_DEFAULT);
return filter;

View File

@@ -23,13 +23,14 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.utils.SnapshotCache;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
class PersistentPreferredActivity extends IntentFilter {
class PersistentPreferredActivity extends WatchedIntentFilter {
private static final String ATTR_NAME = "name"; // component name
private static final String ATTR_FILTER = "filter"; // filter
private static final String ATTR_SET_BY_DPM = "set-by-dpm"; // set by DPM
@@ -41,10 +42,38 @@ class PersistentPreferredActivity extends IntentFilter {
final ComponentName mComponent;
final boolean mIsSetByDpm;
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
final SnapshotCache<PersistentPreferredActivity> mSnapshot;
private SnapshotCache makeCache() {
return new SnapshotCache<PersistentPreferredActivity>(this, this) {
@Override
public PersistentPreferredActivity createSnapshot() {
PersistentPreferredActivity s = new PersistentPreferredActivity(mSource);
s.seal();
return s;
}};
}
PersistentPreferredActivity(IntentFilter filter, ComponentName activity, boolean isSetByDpm) {
super(filter);
mComponent = activity;
mIsSetByDpm = isSetByDpm;
mSnapshot = makeCache();
}
PersistentPreferredActivity(WatchedIntentFilter filter, ComponentName activity,
boolean isSetByDpm) {
this(filter.mFilter, activity, isSetByDpm);
}
// Copy constructor used only to create a snapshot
private PersistentPreferredActivity(PersistentPreferredActivity f) {
super(f);
mComponent = f.mComponent;
mIsSetByDpm = f.mIsSetByDpm;
mSnapshot = new SnapshotCache.Sealed();
}
PersistentPreferredActivity(TypedXmlPullParser parser)
@@ -79,27 +108,36 @@ class PersistentPreferredActivity extends IntentFilter {
}
}
if (tagName.equals(ATTR_FILTER)) {
readFromXml(parser);
mFilter.readFromXml(parser);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Missing element filter at " +
parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
}
mSnapshot = makeCache();
}
public void writeToXml(TypedXmlSerializer serializer) throws IOException {
serializer.attribute(null, ATTR_NAME, mComponent.flattenToShortString());
serializer.attributeBoolean(null, ATTR_SET_BY_DPM, mIsSetByDpm);
serializer.startTag(null, ATTR_FILTER);
super.writeToXml(serializer);
mFilter.writeToXml(serializer);
serializer.endTag(null, ATTR_FILTER);
}
public IntentFilter getIntentFilter() {
return mFilter;
}
@Override
public String toString() {
return "PersistentPreferredActivity{0x" + Integer.toHexString(System.identityHashCode(this))
+ " " + mComponent.flattenToShortString()
+ ", mIsSetByDpm=" + mIsSetByDpm + "}";
}
public PersistentPreferredActivity snapshot() {
return mSnapshot.snapshot();
}
}

View File

@@ -21,6 +21,7 @@ import android.content.IntentFilter;
import com.android.server.WatchedIntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
public class PersistentPreferredIntentResolver
extends WatchedIntentResolver<PersistentPreferredActivity, PersistentPreferredActivity>
@@ -32,7 +33,7 @@ public class PersistentPreferredIntentResolver
@Override
protected IntentFilter getIntentFilter(@NonNull PersistentPreferredActivity input) {
return input;
return input.getIntentFilter();
}
@Override
@@ -40,14 +41,40 @@ public class PersistentPreferredIntentResolver
return packageName.equals(filter.mComponent.getPackageName());
}
public PersistentPreferredIntentResolver() {
super();
mSnapshot = makeCache();
}
// Take the snapshot of F
protected PersistentPreferredActivity snapshot(PersistentPreferredActivity f) {
return (f == null) ? null : f.snapshot();
}
// Copy constructor used only to create a snapshot.
private PersistentPreferredIntentResolver(PersistentPreferredIntentResolver f) {
copyFrom(f);
mSnapshot = new SnapshotCache.Sealed();
}
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
final SnapshotCache<PersistentPreferredIntentResolver> mSnapshot;
private SnapshotCache makeCache() {
return new SnapshotCache<PersistentPreferredIntentResolver>(this, this) {
@Override
public PersistentPreferredIntentResolver createSnapshot() {
return new PersistentPreferredIntentResolver(mSource);
}};
}
/**
* Return a snapshot of the current object. The snapshot is a read-only copy suitable
* for read-only methods.
* @return A snapshot of the current object.
*/
public PersistentPreferredIntentResolver snapshot() {
PersistentPreferredIntentResolver result = new PersistentPreferredIntentResolver();
result.copyFrom(this);
return result;
return mSnapshot.snapshot();
}
}

View File

@@ -23,32 +23,62 @@ import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.server.utils.SnapshotCache;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.PrintWriter;
class PreferredActivity extends IntentFilter implements PreferredComponent.Callbacks {
class PreferredActivity extends WatchedIntentFilter implements PreferredComponent.Callbacks {
private static final String TAG = "PreferredActivity";
private static final boolean DEBUG_FILTERS = false;
final PreferredComponent mPref;
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
final SnapshotCache<PreferredActivity> mSnapshot;
private SnapshotCache makeCache() {
return new SnapshotCache<PreferredActivity>(this, this) {
@Override
public PreferredActivity createSnapshot() {
PreferredActivity s = new PreferredActivity(mSource);
s.seal();
return s;
}};
}
PreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity,
boolean always) {
super(filter);
mPref = new PreferredComponent(this, match, set, activity, always);
mSnapshot = makeCache();
}
PreferredActivity(WatchedIntentFilter filter, int match, ComponentName[] set,
ComponentName activity, boolean always) {
this(filter.mFilter, match, set, activity, always);
}
// Copy constructor used only to create a snapshot
private PreferredActivity(PreferredActivity f) {
super(f);
mPref = f.mPref;
mSnapshot = new SnapshotCache.Sealed();
}
PreferredActivity(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
mPref = new PreferredComponent(this, parser);
mSnapshot = makeCache();
}
public void writeToXml(TypedXmlSerializer serializer, boolean full) throws IOException {
mPref.writeToXml(serializer, full);
serializer.startTag(null, "filter");
super.writeToXml(serializer);
mFilter.writeToXml(serializer);
serializer.endTag(null, "filter");
}
@@ -58,7 +88,7 @@ class PreferredActivity extends IntentFilter implements PreferredComponent.Callb
if (DEBUG_FILTERS) {
Log.i(TAG, "Starting to parse filter...");
}
readFromXml(parser);
mFilter.readFromXml(parser);
if (DEBUG_FILTERS) {
Log.i(TAG, "Finished filter: depth=" + parser.getDepth() + " tag="
+ parser.getName());
@@ -71,9 +101,17 @@ class PreferredActivity extends IntentFilter implements PreferredComponent.Callb
return true;
}
public void dumpPref(PrintWriter out, String prefix, PreferredActivity filter) {
mPref.dump(out, prefix, filter);
}
@Override
public String toString() {
return "PreferredActivity{0x" + Integer.toHexString(System.identityHashCode(this))
+ " " + mPref.mComponent.flattenToShortString() + "}";
}
public PreferredActivity snapshot() {
return mSnapshot.snapshot();
}
}

View File

@@ -21,6 +21,7 @@ import android.content.IntentFilter;
import com.android.server.WatchedIntentResolver;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -46,7 +47,7 @@ public class PreferredIntentResolver
@Override
protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) {
return input;
return input.getIntentFilter();
}
public boolean shouldAddPreferredActivity(PreferredActivity pa) {
@@ -69,14 +70,40 @@ public class PreferredIntentResolver
return true;
}
public PreferredIntentResolver() {
super();
mSnapshot = makeCache();
}
// Take the snapshot of F
protected PreferredActivity snapshot(PreferredActivity f) {
return (f == null) ? null : f.snapshot();
}
// Copy constructor used only to create a snapshot.
private PreferredIntentResolver(PreferredIntentResolver f) {
copyFrom(f);
mSnapshot = new SnapshotCache.Sealed();
}
// The cache for snapshots, so they are not rebuilt if the base object has not
// changed.
final SnapshotCache<PreferredIntentResolver> mSnapshot;
private SnapshotCache makeCache() {
return new SnapshotCache<PreferredIntentResolver>(this, this) {
@Override
public PreferredIntentResolver createSnapshot() {
return new PreferredIntentResolver(mSource);
}};
}
/**
* Return a snapshot of the current object. The snapshot is a read-only copy suitable
* for read-only methods.
* @return A snapshot of the current object.
*/
public PreferredIntentResolver snapshot() {
PreferredIntentResolver result = new PreferredIntentResolver();
result.copyFrom(this);
return result;
return mSnapshot.snapshot();
}
}

View File

@@ -3026,8 +3026,9 @@ public final class Settings implements Watchable, Snappable {
= ps.pkg.getPreferredActivityFilters();
for (int i=0; i<intents.size(); i++) {
Pair<String, ParsedIntentInfo> pair = intents.get(i);
applyDefaultPreferredActivityLPw(pmInternal, pair.second, new ComponentName(
ps.name, pair.first), userId);
applyDefaultPreferredActivityLPw(pmInternal,
new WatchedIntentFilter(pair.second),
new ComponentName(ps.name, pair.first), userId);
}
}
}
@@ -3097,7 +3098,7 @@ public final class Settings implements Watchable, Snappable {
}
static void removeFilters(@NonNull PreferredIntentResolver pir,
@NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
@NonNull WatchedIntentFilter filter, @NonNull List<PreferredActivity> existing) {
if (PackageManagerService.DEBUG_PREFERRED) {
Slog.i(TAG, existing.size() + " preferred matches for:");
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
@@ -3112,8 +3113,8 @@ public final class Settings implements Watchable, Snappable {
}
}
private void applyDefaultPreferredActivityLPw(
PackageManagerInternal pmInternal, IntentFilter tmpPa, ComponentName cn, int userId) {
private void applyDefaultPreferredActivityLPw(PackageManagerInternal pmInternal,
WatchedIntentFilter tmpPa, ComponentName cn, int userId) {
// The initial preferences only specify the target activity
// component and intent-filter, not the set of matches. So we
// now need to query for the matches to build the correct
@@ -3284,7 +3285,7 @@ public final class Settings implements Watchable, Snappable {
haveNonSys = null;
}
if (haveAct && haveNonSys == null) {
IntentFilter filter = new IntentFilter();
WatchedIntentFilter filter = new WatchedIntentFilter();
if (intent.getAction() != null) {
filter.addAction(intent.getAction());
}

View File

@@ -56,6 +56,15 @@ public abstract class SnapshotCache<T> extends Watcher{
watchable.registerObserver(this);
}
/**
* A private constructor that sets fields to null and mSealed to true. This supports
* the Sealed subclass.
*/
public SnapshotCache() {
mSource = null;
mSealed = true;
}
/**
* Notify the object that the source object has changed. If the local object is sealed then
* IllegalStateException is thrown. Otherwise, the cache is cleared.
@@ -93,4 +102,25 @@ public abstract class SnapshotCache<T> extends Watcher{
* @return A snapshot
*/
public abstract T createSnapshot();
/**
* A snapshot cache suitable for sealed snapshots. Attempting to retrieve the
* snapshot will throw an UnsupportedOperationException.
* @param <T> the type of object being cached. This is needed for compilation only. It
* has no effect on execution.
*/
public static class Sealed<T> extends SnapshotCache<T> {
/**
* Create a sealed SnapshotCache that cannot be used to create new snapshots.
*/
public Sealed() {
}
/**
* Provide a concrete implementation of createSnapshot() that throws
* UnsupportedOperationException.
*/
public T createSnapshot() {
throw new UnsupportedOperationException("cannot snapshot a sealed snaphot");
}
}
}

View File

@@ -18,6 +18,7 @@ package com.android.server.pm;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.IntentFilter;
import androidx.test.filters.SmallTest;
@@ -83,4 +84,80 @@ public class WatchedIntentHandlingTest {
watcher.verifyNoChangeReported("pulled snapshot");
}
@Test
public void testPreferredActivity() {
// Create a bunch of nondescript component names
ComponentName component = new ComponentName("Package_A", "Class_A");
ComponentName[] components = new ComponentName[10];
for (int i = 0; i < components.length; i++) {
components[i] = new ComponentName("Package_" + i, "Class_" + i);
}
IntentFilter i = new IntentFilter("TEST_ACTION");
PreferredActivity a = new PreferredActivity(i, 1, components, component, true);
final WatchableTester watcher = new WatchableTester(a, "PreferredIntentResolver");
watcher.register();
// Verify that the initial IntentFilter and the PreferredActivity are truly
// independent. This is in addition to verifying that the PreferredActivity
// properly reports its changes.
i.setPriority(i.getPriority() + 1);
watcher.verifyNoChangeReported("indepenent intent");
a.setPriority(a.getPriority() + 2);
watcher.verifyChangeReported("dependent intent");
// Verify independence of i and a
assertTrue(i.getPriority() != a.getPriority());
// Verify that snapshots created from the PreferredActivity are stable when the
// source PreferredActivity changes.
a.setPriority(3);
watcher.verifyChangeReported("initialize intent priority");
PreferredActivity s1 = a.snapshot();
watcher.verifyNoChangeReported("pulled snapshot");
// Verify snapshot cache. In the absence of changes to the PreferredActivity, the
// snapshot will not be rebuilt and will be the exact same object as before.
assertTrue(s1 == a.snapshot());
// Force a change by incrementing the priority. The next snapshot must be
// different from the first snapshot.
a.setPriority(a.getPriority() + 1);
watcher.verifyChangeReported("increment priority");
PreferredActivity s2 = a.snapshot();
watcher.verifyNoChangeReported("pulled second snapshot");
assertTrue(s1 != s2);
// Assert the two snapshots are different. s1 should have priority 3 and s2
// should have priority 4. s2 should match the current value in a.
assertTrue(a.getPriority() == s2.getPriority());
assertTrue(s1.getPriority() != s2.getPriority());
}
@Test
public void testPreferredIntentResolver() {
PreferredIntentResolver r = new PreferredIntentResolver();
final WatchableTester watcher = new WatchableTester(r, "PreferredIntentResolver");
watcher.register();
// Create a bunch of nondescript component names
ComponentName component = new ComponentName("Package_A", "Class_A");
ComponentName[] components = new ComponentName[10];
for (int i = 0; i < components.length; i++) {
components[i] = new ComponentName("Package_" + i, "Class_" + i);
}
IntentFilter i = new IntentFilter("TEST_ACTION");
PreferredActivity a1 = new PreferredActivity(i, 1, components, component, true);
r.addFilter(a1);
watcher.verifyChangeReported("addFilter");
i.setPriority(i.getPriority() + 1);
watcher.verifyNoChangeReported("indepenent intent");
a1.setPriority(a1.getPriority() + 1);
watcher.verifyChangeReported("dependent intent");
PreferredActivity s1 = a1.snapshot();
watcher.verifyNoChangeReported("pulled snapshot");
// Verify snapshot cache.
assertTrue(s1 == a1.snapshot());
a1.setPriority(a1.getPriority() + 1);
watcher.verifyChangeReported("increment priority");
PreferredActivity s2 = a1.snapshot();
watcher.verifyNoChangeReported("pulled second snapshot");
assertTrue(s1.getPriority() != s2.getPriority());
}
}

View File

@@ -916,5 +916,14 @@ public class WatcherTest {
assertTrue(s1 != s2);
assertTrue(leafA.get() == s1.get() + 1);
assertTrue(leafA.get() == s2.get());
// Test sealed snapshots
SnapshotCache<Leaf> sealed = new SnapshotCache.Sealed();
try {
Leaf x1 = sealed.snapshot();
fail(name + " sealed snapshot did not throw");
} catch (UnsupportedOperationException e) {
// This is the passing scenario - the exception is expected.
}
}
}