Merge changes Iccf02e4d,Ibc931dab,Iba80142b into sc-dev
* changes: Watch ComponentResolver Thread WatchedIntentFilter in PackageManagerService WatchedIntentResolver now uses WatchedIntentFilter
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,9 @@ import com.android.server.IntentResolver;
|
||||
import com.android.server.pm.parsing.PackageInfoUtils;
|
||||
import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.utils.Snappable;
|
||||
import com.android.server.utils.SnapshotCache;
|
||||
import com.android.server.utils.WatchableImpl;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
@@ -72,12 +75,19 @@ import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/** Resolves all Android component types [activities, services, providers and receivers]. */
|
||||
public class ComponentResolver {
|
||||
public class ComponentResolver
|
||||
extends WatchableImpl
|
||||
implements Snappable {
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String TAG = "PackageManager";
|
||||
private static final boolean DEBUG_FILTERS = false;
|
||||
private static final boolean DEBUG_SHOW_INFO = false;
|
||||
|
||||
// Convenience function to report that this object has changed.
|
||||
private void onChanged() {
|
||||
dispatchChange(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of all protected actions [i.e. those actions for which a high priority
|
||||
* intent filter is disallowed].
|
||||
@@ -158,27 +168,27 @@ public class ComponentResolver {
|
||||
* would be able to hold its lock while checking the package setting state.</li>
|
||||
* </ol>
|
||||
*/
|
||||
private final Object mLock;
|
||||
private final PackageManagerTracedLock mLock;
|
||||
|
||||
/** All available activities, for your resolving pleasure. */
|
||||
@GuardedBy("mLock")
|
||||
private final ActivityIntentResolver mActivities = new ActivityIntentResolver();
|
||||
private final ActivityIntentResolver mActivities;
|
||||
|
||||
/** All available providers, for your resolving pleasure. */
|
||||
@GuardedBy("mLock")
|
||||
private final ProviderIntentResolver mProviders = new ProviderIntentResolver();
|
||||
private final ProviderIntentResolver mProviders;
|
||||
|
||||
/** All available receivers, for your resolving pleasure. */
|
||||
@GuardedBy("mLock")
|
||||
private final ActivityIntentResolver mReceivers = new ReceiverIntentResolver();
|
||||
private final ReceiverIntentResolver mReceivers;
|
||||
|
||||
/** All available services, for your resolving pleasure. */
|
||||
@GuardedBy("mLock")
|
||||
private final ServiceIntentResolver mServices = new ServiceIntentResolver();
|
||||
private final ServiceIntentResolver mServices;
|
||||
|
||||
/** Mapping from provider authority [first directory in content URI codePath) to provider. */
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<String, ParsedProvider> mProvidersByAuthority = new ArrayMap<>();
|
||||
private final ArrayMap<String, ParsedProvider> mProvidersByAuthority;
|
||||
|
||||
/** Whether or not processing protected filters should be deferred. */
|
||||
private boolean mDeferProtectedFilters = true;
|
||||
@@ -200,12 +210,57 @@ public class ComponentResolver {
|
||||
|
||||
ComponentResolver(UserManagerService userManager,
|
||||
PackageManagerInternal packageManagerInternal,
|
||||
Object lock) {
|
||||
PackageManagerTracedLock lock) {
|
||||
sPackageManagerInternal = packageManagerInternal;
|
||||
sUserManager = userManager;
|
||||
mLock = lock;
|
||||
|
||||
mActivities = new ActivityIntentResolver();
|
||||
mProviders = new ProviderIntentResolver();
|
||||
mReceivers = new ReceiverIntentResolver();
|
||||
mServices = new ServiceIntentResolver();
|
||||
mProvidersByAuthority = new ArrayMap<>();
|
||||
mDeferProtectedFilters = true;
|
||||
|
||||
mSnapshot = new SnapshotCache<ComponentResolver>(this, this) {
|
||||
@Override
|
||||
public ComponentResolver createSnapshot() {
|
||||
return new ComponentResolver(mSource);
|
||||
}};
|
||||
}
|
||||
|
||||
// Copy constructor used in creating snapshots.
|
||||
private ComponentResolver(ComponentResolver orig) {
|
||||
// Do not set the static variables that are set in the default constructor. Do
|
||||
// create a new object for the lock. The snapshot is read-only, so a lock is not
|
||||
// strictly required. However, the current code is simpler if the lock exists,
|
||||
// but does not contend with any outside class.
|
||||
// TODO: make the snapshot lock-free
|
||||
mLock = new PackageManagerTracedLock();
|
||||
|
||||
mActivities = new ActivityIntentResolver(orig.mActivities);
|
||||
mProviders = new ProviderIntentResolver(orig.mProviders);
|
||||
mReceivers = new ReceiverIntentResolver(orig.mReceivers);
|
||||
mServices = new ServiceIntentResolver(orig.mServices);
|
||||
mProvidersByAuthority = new ArrayMap<>(orig.mProvidersByAuthority);
|
||||
mDeferProtectedFilters = orig.mDeferProtectedFilters;
|
||||
mProtectedFilters = (mProtectedFilters == null)
|
||||
? null
|
||||
: new ArrayList<>(orig.mProtectedFilters);
|
||||
|
||||
mSnapshot = null;
|
||||
}
|
||||
|
||||
final SnapshotCache<ComponentResolver> mSnapshot;
|
||||
|
||||
/**
|
||||
* Create a snapshot.
|
||||
*/
|
||||
public ComponentResolver snapshot() {
|
||||
return mSnapshot.snapshot();
|
||||
}
|
||||
|
||||
|
||||
/** Returns the given activity */
|
||||
@Nullable
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
|
||||
@@ -474,6 +529,7 @@ public class ComponentResolver {
|
||||
addReceiversLocked(pkg, chatty);
|
||||
addProvidersLocked(pkg, chatty);
|
||||
addServicesLocked(pkg, chatty);
|
||||
onChanged();
|
||||
}
|
||||
// expect single setupwizard package
|
||||
final String setupWizardPackage = ArrayUtils.firstOrNull(
|
||||
@@ -489,6 +545,7 @@ public class ComponentResolver {
|
||||
final List<ParsedActivity> systemActivities =
|
||||
disabledPkg != null ? disabledPkg.getActivities() : null;
|
||||
adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,6 +553,7 @@ public class ComponentResolver {
|
||||
void removeAllComponents(AndroidPackage pkg, boolean chatty) {
|
||||
synchronized (mLock) {
|
||||
removeAllComponentsLocked(pkg, chatty);
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,51 +562,54 @@ public class ComponentResolver {
|
||||
* all of the filters defined on the /system partition and know the special components.
|
||||
*/
|
||||
void fixProtectedFilterPriorities() {
|
||||
if (!mDeferProtectedFilters) {
|
||||
return;
|
||||
}
|
||||
mDeferProtectedFilters = false;
|
||||
synchronized (mLock) {
|
||||
if (!mDeferProtectedFilters) {
|
||||
return;
|
||||
}
|
||||
mDeferProtectedFilters = false;
|
||||
|
||||
if (mProtectedFilters == null || mProtectedFilters.size() == 0) {
|
||||
return;
|
||||
}
|
||||
final List<Pair<ParsedMainComponent, ParsedIntentInfo>> protectedFilters =
|
||||
mProtectedFilters;
|
||||
mProtectedFilters = null;
|
||||
if (mProtectedFilters == null || mProtectedFilters.size() == 0) {
|
||||
return;
|
||||
}
|
||||
final List<Pair<ParsedMainComponent, ParsedIntentInfo>> protectedFilters =
|
||||
mProtectedFilters;
|
||||
mProtectedFilters = null;
|
||||
|
||||
// expect single setupwizard package
|
||||
final String setupWizardPackage = ArrayUtils.firstOrNull(
|
||||
// expect single setupwizard package
|
||||
final String setupWizardPackage = ArrayUtils.firstOrNull(
|
||||
sPackageManagerInternal.getKnownPackageNames(
|
||||
PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
|
||||
PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
|
||||
|
||||
if (DEBUG_FILTERS && setupWizardPackage == null) {
|
||||
Slog.i(TAG, "No setup wizard;"
|
||||
+ " All protected intents capped to priority 0");
|
||||
}
|
||||
for (int i = protectedFilters.size() - 1; i >= 0; --i) {
|
||||
final Pair<ParsedMainComponent, ParsedIntentInfo> pair = protectedFilters.get(i);
|
||||
ParsedMainComponent component = pair.first;
|
||||
ParsedIntentInfo filter = pair.second;
|
||||
String packageName = component.getPackageName();
|
||||
String className = component.getClassName();
|
||||
if (packageName.equals(setupWizardPackage)) {
|
||||
if (DEBUG_FILTERS && setupWizardPackage == null) {
|
||||
Slog.i(TAG, "No setup wizard;"
|
||||
+ " All protected intents capped to priority 0");
|
||||
}
|
||||
for (int i = protectedFilters.size() - 1; i >= 0; --i) {
|
||||
final Pair<ParsedMainComponent, ParsedIntentInfo> pair = protectedFilters.get(i);
|
||||
ParsedMainComponent component = pair.first;
|
||||
ParsedIntentInfo filter = pair.second;
|
||||
String packageName = component.getPackageName();
|
||||
String className = component.getClassName();
|
||||
if (packageName.equals(setupWizardPackage)) {
|
||||
if (DEBUG_FILTERS) {
|
||||
Slog.i(TAG, "Found setup wizard;"
|
||||
+ " allow priority " + filter.getPriority() + ";"
|
||||
+ " package: " + packageName
|
||||
+ " activity: " + className
|
||||
+ " priority: " + filter.getPriority());
|
||||
}
|
||||
// skip setup wizard; allow it to keep the high priority filter
|
||||
continue;
|
||||
}
|
||||
if (DEBUG_FILTERS) {
|
||||
Slog.i(TAG, "Found setup wizard;"
|
||||
+ " allow priority " + filter.getPriority() + ";"
|
||||
Slog.i(TAG, "Protected action; cap priority to 0;"
|
||||
+ " package: " + packageName
|
||||
+ " activity: " + className
|
||||
+ " priority: " + filter.getPriority());
|
||||
+ " origPrio: " + filter.getPriority());
|
||||
}
|
||||
// skip setup wizard; allow it to keep the high priority filter
|
||||
continue;
|
||||
filter.setPriority(0);
|
||||
}
|
||||
if (DEBUG_FILTERS) {
|
||||
Slog.i(TAG, "Protected action; cap priority to 0;"
|
||||
+ " package: " + packageName
|
||||
+ " activity: " + className
|
||||
+ " origPrio: " + filter.getPriority());
|
||||
}
|
||||
filter.setPriority(0);
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1181,9 +1242,20 @@ public class ComponentResolver {
|
||||
private abstract static class MimeGroupsAwareIntentResolver<F extends Pair<?
|
||||
extends ParsedComponent, ParsedIntentInfo>, R>
|
||||
extends IntentResolver<F, R> {
|
||||
private ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
|
||||
private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>();
|
||||
private boolean mIsUpdatingMimeGroup = false;
|
||||
|
||||
// Default constructor
|
||||
MimeGroupsAwareIntentResolver() {
|
||||
}
|
||||
|
||||
// Copy constructor used in creating snapshots
|
||||
MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig) {
|
||||
copyFrom(orig);
|
||||
copyInto(mMimeGroupToFilter, orig.mMimeGroupToFilter);
|
||||
mIsUpdatingMimeGroup = orig.mIsUpdatingMimeGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFilter(F f) {
|
||||
IntentFilter intentFilter = getIntentFilter(f);
|
||||
@@ -1282,6 +1354,17 @@ public class ComponentResolver {
|
||||
private static class ActivityIntentResolver
|
||||
extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
|
||||
|
||||
// Default constructor
|
||||
ActivityIntentResolver() {
|
||||
}
|
||||
|
||||
// Copy constructor used in creating snapshots
|
||||
ActivityIntentResolver(ActivityIntentResolver orig) {
|
||||
super(orig);
|
||||
mActivities.putAll(orig.mActivities);
|
||||
mFlags = orig.mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
|
||||
boolean defaultOnly, int userId) {
|
||||
@@ -1330,7 +1413,7 @@ public class ComponentResolver {
|
||||
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
|
||||
}
|
||||
|
||||
private void addActivity(ParsedActivity a, String type,
|
||||
protected void addActivity(ParsedActivity a, String type,
|
||||
List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) {
|
||||
mActivities.put(a.getComponentName(), a);
|
||||
if (DEBUG_SHOW_INFO) {
|
||||
@@ -1354,7 +1437,7 @@ public class ComponentResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeActivity(ParsedActivity a, String type) {
|
||||
protected void removeActivity(ParsedActivity a, String type) {
|
||||
mActivities.remove(a.getComponentName());
|
||||
if (DEBUG_SHOW_INFO) {
|
||||
Log.v(TAG, " " + type + ":");
|
||||
@@ -1567,8 +1650,11 @@ public class ComponentResolver {
|
||||
return pkg.getActivities();
|
||||
}
|
||||
|
||||
// Keys are String (activity class name), values are Activity.
|
||||
private final ArrayMap<ComponentName, ParsedActivity> mActivities =
|
||||
// Keys are String (activity class name), values are Activity. This attribute is
|
||||
// protected because it is accessed directly from ComponentResolver. That works
|
||||
// even if the attribute is private, but fails for subclasses of
|
||||
// ActivityIntentResolver.
|
||||
protected final ArrayMap<ComponentName, ParsedActivity> mActivities =
|
||||
new ArrayMap<>();
|
||||
private int mFlags;
|
||||
}
|
||||
@@ -1576,6 +1662,15 @@ public class ComponentResolver {
|
||||
// Both receivers and activities share a class, but point to different get methods
|
||||
private static final class ReceiverIntentResolver extends ActivityIntentResolver {
|
||||
|
||||
// Default constructor
|
||||
ReceiverIntentResolver() {
|
||||
}
|
||||
|
||||
// Copy constructor used in creating snapshots
|
||||
ReceiverIntentResolver(ReceiverIntentResolver orig) {
|
||||
super(orig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ParsedActivity> getResolveList(AndroidPackage pkg) {
|
||||
return pkg.getReceivers();
|
||||
@@ -1584,6 +1679,17 @@ public class ComponentResolver {
|
||||
|
||||
private static final class ProviderIntentResolver
|
||||
extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> {
|
||||
// Default constructor
|
||||
ProviderIntentResolver() {
|
||||
}
|
||||
|
||||
// Copy constructor used in creating snapshots
|
||||
ProviderIntentResolver(ProviderIntentResolver orig) {
|
||||
super(orig);
|
||||
mProviders.putAll(orig.mProviders);
|
||||
mFlags = orig.mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
|
||||
boolean defaultOnly, int userId) {
|
||||
@@ -1829,6 +1935,17 @@ public class ComponentResolver {
|
||||
|
||||
private static final class ServiceIntentResolver
|
||||
extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> {
|
||||
// Default constructor
|
||||
ServiceIntentResolver() {
|
||||
}
|
||||
|
||||
// Copy constructor used in creating snapshots
|
||||
ServiceIntentResolver(ServiceIntentResolver orig) {
|
||||
copyFrom(orig);
|
||||
mServices.putAll(orig.mServices);
|
||||
mFlags = orig.mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
|
||||
boolean defaultOnly, int userId) {
|
||||
@@ -2213,11 +2330,16 @@ public class ComponentResolver {
|
||||
* @return true if any intent filters were changed due to this update
|
||||
*/
|
||||
boolean updateMimeGroup(String packageName, String group) {
|
||||
boolean hasChanges = mActivities.updateMimeGroup(packageName, group);
|
||||
hasChanges |= mProviders.updateMimeGroup(packageName, group);
|
||||
hasChanges |= mReceivers.updateMimeGroup(packageName, group);
|
||||
hasChanges |= mServices.updateMimeGroup(packageName, group);
|
||||
|
||||
boolean hasChanges = false;
|
||||
synchronized (mLock) {
|
||||
hasChanges |= mActivities.updateMimeGroup(packageName, group);
|
||||
hasChanges |= mProviders.updateMimeGroup(packageName, group);
|
||||
hasChanges |= mReceivers.updateMimeGroup(packageName, group);
|
||||
hasChanges |= mServices.updateMimeGroup(packageName, group);
|
||||
if (hasChanges) {
|
||||
onChanged();
|
||||
}
|
||||
}
|
||||
return hasChanges;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1482,7 +1482,9 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
// Internal interface for permission manager
|
||||
private final PermissionManagerServiceInternal mPermissionManager;
|
||||
|
||||
@Watched
|
||||
private final ComponentResolver mComponentResolver;
|
||||
|
||||
// List of packages names to keep cached, even if they are uninstalled for all users
|
||||
private List<String> mKeepUninstalledPackages;
|
||||
|
||||
@@ -1826,6 +1828,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
public final ApplicationInfo androidApplication;
|
||||
public final String appPredictionServicePackage;
|
||||
public final AppsFilter appsFilter;
|
||||
public final ComponentResolver componentResolver;
|
||||
public final PackageManagerService service;
|
||||
|
||||
Snapshot(int type) {
|
||||
@@ -1851,6 +1854,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
: new ApplicationInfo(mAndroidApplication);
|
||||
appPredictionServicePackage = mAppPredictionServicePackage;
|
||||
appsFilter = mAppsFilter.snapshot();
|
||||
componentResolver = mComponentResolver.snapshot();
|
||||
} else if (type == Snapshot.LIVE) {
|
||||
settings = mSettings;
|
||||
isolatedOwners = mIsolatedOwners;
|
||||
@@ -1867,6 +1871,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
androidApplication = mAndroidApplication;
|
||||
appPredictionServicePackage = mAppPredictionServicePackage;
|
||||
appsFilter = mAppsFilter;
|
||||
componentResolver = mComponentResolver;
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
@@ -1956,8 +1961,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
int callingUid);
|
||||
ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, Intent intent,
|
||||
String resolvedType, int flags, int sourceUserId);
|
||||
ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter, int sourceUserId,
|
||||
int targetUserId);
|
||||
ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
|
||||
int sourceUserId, int targetUserId);
|
||||
ResolveInfo queryCrossProfileIntents(List<CrossProfileIntentFilter> matchingFilters,
|
||||
Intent intent, String resolvedType, int flags, int sourceUserId,
|
||||
boolean matchInCurrentProfile);
|
||||
@@ -2114,6 +2119,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mInstantAppRegistry = args.instantAppRegistry;
|
||||
mLocalAndroidApplication = args.androidApplication;
|
||||
mAppsFilter = args.appsFilter;
|
||||
mComponentResolver = args.componentResolver;
|
||||
|
||||
mAppPredictionServicePackage = args.appPredictionServicePackage;
|
||||
|
||||
@@ -2124,7 +2130,6 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mContext = args.service.mContext;
|
||||
mInjector = args.service.mInjector;
|
||||
mApexManager = args.service.mApexManager;
|
||||
mComponentResolver = args.service.mComponentResolver;
|
||||
mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
|
||||
mDefaultAppProvider = args.service.mDefaultAppProvider;
|
||||
mDomainVerificationManager = args.service.mDomainVerificationManager;
|
||||
@@ -2860,8 +2865,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
if (result == null) {
|
||||
result = new CrossProfileDomainInfo();
|
||||
result.resolveInfo = createForwardingResolveInfoUnchecked(new IntentFilter(),
|
||||
sourceUserId, parentUserId);
|
||||
result.resolveInfo = createForwardingResolveInfoUnchecked(
|
||||
new WatchedIntentFilter(), sourceUserId, parentUserId);
|
||||
}
|
||||
|
||||
result.highestApprovalLevel = Math.max(mDomainVerificationManager
|
||||
@@ -3469,15 +3474,15 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
for (int i = resultTargetUser.size() - 1; i >= 0; i--) {
|
||||
if ((resultTargetUser.get(i).activityInfo.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_SUSPENDED) == 0) {
|
||||
return createForwardingResolveInfoUnchecked(filter, sourceUserId,
|
||||
targetUserId);
|
||||
return createForwardingResolveInfoUnchecked(filter,
|
||||
sourceUserId, targetUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
|
||||
public ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
|
||||
int sourceUserId, int targetUserId) {
|
||||
ResolveInfo forwardingResolveInfo = new ResolveInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
@@ -3507,7 +3512,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
forwardingResolveInfo.preferredOrder = 0;
|
||||
forwardingResolveInfo.match = 0;
|
||||
forwardingResolveInfo.isDefault = true;
|
||||
forwardingResolveInfo.filter = filter;
|
||||
forwardingResolveInfo.filter = new IntentFilter(filter.getIntentFilter());
|
||||
forwardingResolveInfo.targetUserId = targetUserId;
|
||||
return forwardingResolveInfo;
|
||||
}
|
||||
@@ -6144,6 +6149,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mInstantAppRegistry.registerObserver(mWatcher);
|
||||
mSettings.registerObserver(mWatcher);
|
||||
mIsolatedOwners.registerObserver(mWatcher);
|
||||
mComponentResolver.registerObserver(mWatcher);
|
||||
// If neither "build" attribute is true then this may be a mockito test, and verification
|
||||
// can fail as a false positive.
|
||||
Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
|
||||
@@ -9457,6 +9463,15 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
|
||||
IntentFilter filter, int match, ComponentName activity) {
|
||||
setLastChosenActivity(intent, resolvedType, flags,
|
||||
new WatchedIntentFilter(filter), match, activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant that takes a {@link WatchedIntentFilter}
|
||||
*/
|
||||
public void setLastChosenActivity(Intent intent, String resolvedType, int flags,
|
||||
WatchedIntentFilter filter, int match, ComponentName activity) {
|
||||
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
|
||||
return;
|
||||
}
|
||||
@@ -9477,7 +9492,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
findPreferredActivityNotLocked(
|
||||
intent, resolvedType, flags, query, 0, false, true, false, userId);
|
||||
// Add the new activity as the last chosen for this filter
|
||||
addPreferredActivityInternal(filter, match, null, activity, false, userId,
|
||||
addPreferredActivity(filter, match, null, activity, false, userId,
|
||||
"Setting last chosen", false);
|
||||
}
|
||||
|
||||
@@ -10195,12 +10210,6 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
resolvedType, flags, sourceUserId);
|
||||
}
|
||||
|
||||
private ResolveInfo createForwardingResolveInfoUnchecked(IntentFilter filter,
|
||||
int sourceUserId, int targetUserId) {
|
||||
return liveComputer().createForwardingResolveInfoUnchecked(filter,
|
||||
sourceUserId, targetUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ParceledListSlice<ResolveInfo> queryIntentActivityOptions(ComponentName caller,
|
||||
Intent[] specifics, String[] specificTypes, Intent intent,
|
||||
@@ -16477,7 +16486,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return new ParceledListSlice<IntentFilter>(result) {
|
||||
@Override
|
||||
protected void writeElement(IntentFilter parcelable, Parcel dest, int callFlags) {
|
||||
// IntentFilter has final Parcelable methods, so redirect to the subclass
|
||||
// WatchedIntentFilter has final Parcelable methods, so redirect to the subclass
|
||||
((ParsedIntentInfo) parcelable).writeIntentInfoToParcel(dest,
|
||||
callFlags);
|
||||
}
|
||||
@@ -21964,11 +21973,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void addPreferredActivity(IntentFilter filter, int match,
|
||||
ComponentName[] set, ComponentName activity, int userId, boolean removeExisting) {
|
||||
addPreferredActivityInternal(filter, match, set, activity, true, userId,
|
||||
addPreferredActivity(new WatchedIntentFilter(filter), match, set, activity, true, userId,
|
||||
"Adding preferred", removeExisting);
|
||||
}
|
||||
|
||||
private void addPreferredActivityInternal(IntentFilter filter, int match,
|
||||
/**
|
||||
* Variant that takes a {@link WatchedIntentFilter}
|
||||
*/
|
||||
public void addPreferredActivity(WatchedIntentFilter filter, int match,
|
||||
ComponentName[] set, ComponentName activity, boolean always, int userId,
|
||||
String opname, boolean removeExisting) {
|
||||
// writer
|
||||
@@ -22034,6 +22046,15 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void replacePreferredActivity(IntentFilter filter, int match,
|
||||
ComponentName[] set, ComponentName activity, int userId) {
|
||||
replacePreferredActivity(new WatchedIntentFilter(filter), match,
|
||||
set, activity, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant that takes a {@link WatchedIntentFilter}
|
||||
*/
|
||||
public void replacePreferredActivity(WatchedIntentFilter filter, int match,
|
||||
ComponentName[] set, ComponentName activity, int userId) {
|
||||
if (filter.countActions() != 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"replacePreferredActivity expects filter to have only 1 action.");
|
||||
@@ -22106,7 +22127,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
}
|
||||
addPreferredActivityInternal(filter, match, set, activity, true, userId,
|
||||
addPreferredActivity(filter, match, set, activity, true, userId,
|
||||
"Replacing preferred", false);
|
||||
}
|
||||
|
||||
@@ -22213,6 +22234,22 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public int getPreferredActivities(List<IntentFilter> outFilters,
|
||||
List<ComponentName> outActivities, String packageName) {
|
||||
List<WatchedIntentFilter> temp =
|
||||
WatchedIntentFilter.toWatchedIntentFilterList(outFilters);
|
||||
final int result = getPreferredActivitiesInternal(
|
||||
temp, outActivities, packageName);
|
||||
outFilters.clear();
|
||||
for (int i = 0; i < temp.size(); i++) {
|
||||
outFilters.add(temp.get(i).getIntentFilter());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant that takes a {@link WatchedIntentFilter}
|
||||
*/
|
||||
public int getPreferredActivitiesInternal(List<WatchedIntentFilter> outFilters,
|
||||
List<ComponentName> outActivities, String packageName) {
|
||||
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
|
||||
return 0;
|
||||
}
|
||||
@@ -22229,7 +22266,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|| (pa.mPref.mComponent.getPackageName().equals(packageName)
|
||||
&& pa.mPref.mAlways)) {
|
||||
if (outFilters != null) {
|
||||
outFilters.add(new IntentFilter(pa));
|
||||
outFilters.add(new WatchedIntentFilter(pa.getIntentFilter()));
|
||||
}
|
||||
if (outActivities != null) {
|
||||
outActivities.add(pa.mPref.mComponent);
|
||||
@@ -22245,6 +22282,14 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity,
|
||||
int userId) {
|
||||
addPersistentPreferredActivity(new WatchedIntentFilter(filter), activity, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant that takes a {@link WatchedIntentFilter}
|
||||
*/
|
||||
public void addPersistentPreferredActivity(WatchedIntentFilter filter, ComponentName activity,
|
||||
int userId) {
|
||||
int callingUid = Binder.getCallingUid();
|
||||
if (callingUid != Process.SYSTEM_UID) {
|
||||
throw new SecurityException(
|
||||
@@ -22488,6 +22533,15 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
|
||||
int sourceUserId, int targetUserId, int flags) {
|
||||
addCrossProfileIntentFilter(new WatchedIntentFilter(intentFilter), ownerPackage,
|
||||
sourceUserId, targetUserId, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant that takes a {@link WatchedIntentFilter}
|
||||
*/
|
||||
public void addCrossProfileIntentFilter(WatchedIntentFilter intentFilter, String ownerPackage,
|
||||
int sourceUserId, int targetUserId, int flags) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
|
||||
int callingUid = Binder.getCallingUid();
|
||||
@@ -22617,8 +22671,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return liveComputer().getHomeIntent();
|
||||
}
|
||||
|
||||
private IntentFilter getHomeFilter() {
|
||||
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
|
||||
private WatchedIntentFilter getHomeFilter() {
|
||||
WatchedIntentFilter filter = new WatchedIntentFilter(Intent.ACTION_MAIN);
|
||||
filter.addCategory(Intent.CATEGORY_HOME);
|
||||
filter.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
return filter;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3026,8 +3026,9 @@ public final class Settings implements Watchable, Snappable {
|
||||
= ps.pkg.getPreferredActivityFilters();
|
||||
for (int i=0; i<intents.size(); i++) {
|
||||
Pair<String, ParsedIntentInfo> pair = intents.get(i);
|
||||
applyDefaultPreferredActivityLPw(pmInternal, pair.second, new ComponentName(
|
||||
ps.name, pair.first), userId);
|
||||
applyDefaultPreferredActivityLPw(pmInternal,
|
||||
new WatchedIntentFilter(pair.second),
|
||||
new ComponentName(ps.name, pair.first), userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3097,7 +3098,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
static void removeFilters(@NonNull PreferredIntentResolver pir,
|
||||
@NonNull IntentFilter filter, @NonNull List<PreferredActivity> existing) {
|
||||
@NonNull WatchedIntentFilter filter, @NonNull List<PreferredActivity> existing) {
|
||||
if (PackageManagerService.DEBUG_PREFERRED) {
|
||||
Slog.i(TAG, existing.size() + " preferred matches for:");
|
||||
filter.dump(new LogPrinter(Log.INFO, TAG), " ");
|
||||
@@ -3112,8 +3113,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDefaultPreferredActivityLPw(
|
||||
PackageManagerInternal pmInternal, IntentFilter tmpPa, ComponentName cn, int userId) {
|
||||
private void applyDefaultPreferredActivityLPw(PackageManagerInternal pmInternal,
|
||||
WatchedIntentFilter tmpPa, ComponentName cn, int userId) {
|
||||
// The initial preferences only specify the target activity
|
||||
// component and intent-filter, not the set of matches. So we
|
||||
// now need to query for the matches to build the correct
|
||||
@@ -3284,7 +3285,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
haveNonSys = null;
|
||||
}
|
||||
if (haveAct && haveNonSys == null) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
WatchedIntentFilter filter = new WatchedIntentFilter();
|
||||
if (intent.getAction() != null) {
|
||||
filter.addAction(intent.getAction());
|
||||
}
|
||||
|
||||
@@ -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