From 5c302db303da26c1a7e089503deaf70d9edc529a Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Fri, 30 Apr 2021 06:21:54 -0700 Subject: [PATCH 1/3] 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 --- .../com/android/server/IntentResolver.java | 36 ++++++--- .../android/server/WatchedIntentResolver.java | 30 +++++++- .../server/pm/CrossProfileIntentFilter.java | 44 ++++++++++- .../server/pm/CrossProfileIntentResolver.java | 34 +++++++- .../server/pm/PackageManagerService.java | 6 +- .../pm/PersistentPreferredActivity.java | 44 ++++++++++- .../pm/PersistentPreferredIntentResolver.java | 35 ++++++++- .../android/server/pm/PreferredActivity.java | 44 ++++++++++- .../server/pm/PreferredIntentResolver.java | 35 ++++++++- .../java/com/android/server/pm/Settings.java | 9 ++- .../android/server/utils/SnapshotCache.java | 30 ++++++++ .../server/pm/WatchedIntentHandlingTest.java | 77 +++++++++++++++++++ .../com/android/server/utils/WatcherTest.java | 9 +++ 13 files changed, 390 insertions(+), 43 deletions(-) diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 9067028668895..047aae772f44f 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -839,33 +839,45 @@ public abstract class IntentResolver { } }; + // 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 void copyInto(ArrayMap l, ArrayMap r) { + protected void copyInto(ArrayMap l, ArrayMap 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 l, ArraySet 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 a copy of . The presumption is that 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); } diff --git a/services/core/java/com/android/server/WatchedIntentResolver.java b/services/core/java/com/android/server/WatchedIntentResolver.java index e514f3c6fd59e..0831c36d2bf6a 100644 --- a/services/core/java/com/android/server/WatchedIntentResolver.java +++ b/services/core/java/com/android/server/WatchedIntentResolver.java @@ -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 The resolver type. * {@hide} */ -public abstract class WatchedIntentResolver +public abstract class WatchedIntentResolver extends IntentResolver - implements Watchable { + implements Watchable, Snappable { /** * Watchable machinery @@ -78,6 +81,13 @@ public abstract class WatchedIntentResolver 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 @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 super.sortResults(results); onChanged(); } + + /** + * @see IntentResolver#findFilters(IntentFilter) + */ + public ArrayList findFilters(WatchedIntentFilter matching) { + return super.findFilters(matching.getIntentFilter()); + } + + // Make a copy of . The presumption is that is empty but all + // arrays are cleared out explicitly, just to be sure. + protected void copyFrom(WatchedIntentResolver orig) { + super.copyFrom(orig); + } } diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java index 7c19d90e4eded..f5910fa3143c7 100644 --- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java @@ -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 mSnapshot; + + private SnapshotCache makeCache() { + return new SnapshotCache(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(); + } } diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java index 791a1057d112a..7927538603582 100644 --- a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java @@ -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 mSnapshot; + + private SnapshotCache makeCache() { + return new SnapshotCache(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(); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9a3fe82f2d0fd..3d507b99a1b53 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -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); diff --git a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java index a015456f1485a..ad3950c906ef5 100644 --- a/services/core/java/com/android/server/pm/PersistentPreferredActivity.java +++ b/services/core/java/com/android/server/pm/PersistentPreferredActivity.java @@ -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 mSnapshot; + + private SnapshotCache makeCache() { + return new SnapshotCache(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(); + } } diff --git a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java index 2b11a42978354..bfddaea0dd756 100644 --- a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java @@ -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 @@ -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 mSnapshot; + + private SnapshotCache makeCache() { + return new SnapshotCache(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(); } } diff --git a/services/core/java/com/android/server/pm/PreferredActivity.java b/services/core/java/com/android/server/pm/PreferredActivity.java index 4e1dcb27111df..5bc915f2c1c4e 100644 --- a/services/core/java/com/android/server/pm/PreferredActivity.java +++ b/services/core/java/com/android/server/pm/PreferredActivity.java @@ -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 mSnapshot; + + private SnapshotCache makeCache() { + return new SnapshotCache(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(); + } } diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java index 10a6b3f69fde2..0aca6eec25fcc 100644 --- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java @@ -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 mSnapshot; + + private SnapshotCache makeCache() { + return new SnapshotCache(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(); } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b6d4a5b88f8a8..d23bb7295fbf8 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3015,8 +3015,9 @@ public final class Settings implements Watchable, Snappable { = ps.pkg.getPreferredActivityFilters(); for (int i=0; i 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 diff --git a/services/core/java/com/android/server/utils/SnapshotCache.java b/services/core/java/com/android/server/utils/SnapshotCache.java index f0fb8b25b402a..b4b8835ac0269 100644 --- a/services/core/java/com/android/server/utils/SnapshotCache.java +++ b/services/core/java/com/android/server/utils/SnapshotCache.java @@ -56,6 +56,15 @@ public abstract class SnapshotCache 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 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 the type of object being cached. This is needed for compilation only. It + * has no effect on execution. + */ + public static class Sealed extends SnapshotCache { + /** + * 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"); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java index 153938cbbbf1a..b2c300255aef3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/WatchedIntentHandlingTest.java @@ -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()); + } } diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index f255c67cf47d3..9679e58c4e7d2 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -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 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. + } } } From 16c00cb28966c186d8345d1b57b46ea4423aed22 Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Fri, 30 Apr 2021 06:21:58 -0700 Subject: [PATCH 2/3] Thread WatchedIntentFilter in PackageManagerService Bug: 181964615 Replace most occurrences of IntentFilter with WatchedIntentFilter, so that any changes to the objects will invalidate the PM snapshot. 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 Change-Id: Ibc931daba594038929bafeff9cc2515d770ff60c --- .../pm/DefaultCrossProfileIntentFilter.java | 6 +- .../server/pm/PackageManagerService.java | 90 ++++++++++++++----- .../java/com/android/server/pm/Settings.java | 4 +- 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java index a32e107d9e730..c58128a3c9b18 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFilter.java @@ -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; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 3d507b99a1b53..95a2ae6c13f50 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1984,8 +1984,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 matchingFilters, Intent intent, String resolvedType, int flags, int sourceUserId, boolean matchInCurrentProfile); @@ -2888,8 +2888,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 @@ -3484,7 +3484,7 @@ 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.getIntentFilter(), + return createForwardingResolveInfoUnchecked(filter, sourceUserId, targetUserId); } } @@ -3492,7 +3492,7 @@ public class PackageManagerService extends IPackageManager.Stub 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(); @@ -3522,7 +3522,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; } @@ -9472,6 +9472,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; } @@ -9492,7 +9501,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); } @@ -10210,12 +10219,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 queryIntentActivityOptions(ComponentName caller, Intent[] specifics, String[] specificTypes, Intent intent, @@ -16491,7 +16494,7 @@ public class PackageManagerService extends IPackageManager.Stub return new ParceledListSlice(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); } @@ -21978,11 +21981,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 @@ -22048,6 +22054,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."); @@ -22120,7 +22135,7 @@ public class PackageManagerService extends IPackageManager.Stub } } } - addPreferredActivityInternal(filter, match, set, activity, true, userId, + addPreferredActivity(filter, match, set, activity, true, userId, "Replacing preferred", false); } @@ -22227,6 +22242,22 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int getPreferredActivities(List outFilters, List outActivities, String packageName) { + List 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 outFilters, + List outActivities, String packageName) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return 0; } @@ -22243,7 +22274,7 @@ public class PackageManagerService extends IPackageManager.Stub || (pa.mPref.mComponent.getPackageName().equals(packageName) && pa.mPref.mAlways)) { if (outFilters != null) { - outFilters.add(new IntentFilter(pa.getIntentFilter())); + outFilters.add(new WatchedIntentFilter(pa.getIntentFilter())); } if (outActivities != null) { outActivities.add(pa.mPref.mComponent); @@ -22259,6 +22290,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( @@ -22479,6 +22518,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(); @@ -22608,8 +22656,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; diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index d23bb7295fbf8..a625ad445853b 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -3087,7 +3087,7 @@ public final class Settings implements Watchable, Snappable { } static void removeFilters(@NonNull PreferredIntentResolver pir, - @NonNull IntentFilter filter, @NonNull List existing) { + @NonNull WatchedIntentFilter filter, @NonNull List existing) { if (PackageManagerService.DEBUG_PREFERRED) { Slog.i(TAG, existing.size() + " preferred matches for:"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); @@ -3274,7 +3274,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()); } From b489de6a4f5d8040cbec107805b3e6c3ebcd21fb Mon Sep 17 00:00:00 2001 From: Lee Shombert Date: Fri, 30 Apr 2021 06:22:02 -0700 Subject: [PATCH 3/3] Watch ComponentResolver Bug: 181964615 Make ComponentResolver a watched object, where changes are notified to PackageManagerService. At the same time, make it snappable, since it has state that is changed by PMS. commit. Test: atest * 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 * CtsRoleTestCases Change-Id: Iccf02e4d32170ca91cf0976603a9bfc0b0627303 --- .../android/server/pm/ComponentResolver.java | 230 ++++++++++++++---- .../server/pm/PackageManagerService.java | 8 +- 2 files changed, 183 insertions(+), 55 deletions(-) diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java index 1d556fec31ea6..dde9f822ac767 100644 --- a/services/core/java/com/android/server/pm/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/ComponentResolver.java @@ -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. * */ - 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 mProvidersByAuthority = new ArrayMap<>(); + private final ArrayMap 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(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 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 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> protectedFilters = - mProtectedFilters; - mProtectedFilters = null; + if (mProtectedFilters == null || mProtectedFilters.size() == 0) { + return; + } + final List> 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 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 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, R> extends IntentResolver { - private ArrayMap mMimeGroupToFilter = new ArrayMap<>(); + private final ArrayMap mMimeGroupToFilter = new ArrayMap<>(); private boolean mIsUpdatingMimeGroup = false; + // Default constructor + MimeGroupsAwareIntentResolver() { + } + + // Copy constructor used in creating snapshots + MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver 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, 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 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> 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 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 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 getResolveList(AndroidPackage pkg) { return pkg.getReceivers(); @@ -1584,6 +1679,17 @@ public class ComponentResolver { private static final class ProviderIntentResolver extends MimeGroupsAwareIntentResolver, 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 queryIntent(Intent intent, String resolvedType, boolean defaultOnly, int userId) { @@ -1829,6 +1935,17 @@ public class ComponentResolver { private static final class ServiceIntentResolver extends MimeGroupsAwareIntentResolver, 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 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; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 95a2ae6c13f50..2b2205abbe74c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1481,7 +1481,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 mKeepUninstalledPackages; @@ -1854,6 +1856,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) { @@ -1879,6 +1882,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; @@ -1895,6 +1899,7 @@ public class PackageManagerService extends IPackageManager.Stub androidApplication = mAndroidApplication; appPredictionServicePackage = mAppPredictionServicePackage; appsFilter = mAppsFilter; + componentResolver = mComponentResolver; } else { throw new IllegalArgumentException(); } @@ -2142,6 +2147,7 @@ public class PackageManagerService extends IPackageManager.Stub mInstantAppRegistry = args.instantAppRegistry; mLocalAndroidApplication = args.androidApplication; mAppsFilter = args.appsFilter; + mComponentResolver = args.componentResolver; mAppPredictionServicePackage = args.appPredictionServicePackage; @@ -2152,7 +2158,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; @@ -6159,6 +6164,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));