WatchedIntentResolver now uses WatchedIntentFilter
Bug: 181964615 Convert WatchedIntentResolver to use WatchedIntentFilter instead of IntentFilter. WatchedIntentFilters are converted back to IntentFilters when they are used in APIs. The final conversion to WatchedIntentFilter in PackageManagerService happens in a follow-up commit. Test: atest * FrameworksServicesTests:WatchedIntentHandlingTest * FrameworksServicesTests:AppsFilterTest * FrameworksServicesTests:PackageInstallerSessionTest * FrameworksServicesTests:PackageManagerServiceTest * FrameworksServicesTests:PackageManagerSettingsTests * FrameworksServicesTests:ScanTests * FrameworksServicesTests:UserSystemPackageInstallerTest * PackageManagerServiceBootTest * UserLifecycleTests#startUser * UserLifecycleTests#stopUser * UserLifecycleTests#switchUser * android.appsecurity.cts.EphemeralTest * android.appsecurity.cts.InstantAppUserTest * FrameworksServicesTests:WatcherTest * CtsContentTestCases:IntentFilterTest * CtsDynamicMimeHostTestCases * FrameworksServicesTests:WatcherTest Change-Id: Iba80142b44268443221c22b5626fc53d60f2aa9f
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3484,8 +3484,8 @@ 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.getIntentFilter(),
|
||||
sourceUserId, targetUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22243,7 +22243,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 IntentFilter(pa.getIntentFilter()));
|
||||
}
|
||||
if (outActivities != null) {
|
||||
outActivities.add(pa.mPref.mComponent);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3015,8 +3015,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3101,8 +3102,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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user