Merge changes I366d5f63,I76d9c952 into sc-dev
* changes: Create a WatchedIntentFilter Create a snapshot cache utility
This commit is contained in:
@@ -57,6 +57,7 @@ import com.android.server.pm.parsing.PackageInfoUtils;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.permission.PermissionManagerServiceInternal;
|
||||
import com.android.server.utils.Snappable;
|
||||
import com.android.server.utils.SnapshotCache;
|
||||
import com.android.server.utils.Watchable;
|
||||
import com.android.server.utils.WatchableImpl;
|
||||
import com.android.server.utils.Watched;
|
||||
@@ -146,7 +147,7 @@ class InstantAppRegistry implements Watchable, Snappable {
|
||||
/**
|
||||
* The cached snapshot
|
||||
*/
|
||||
private volatile InstantAppRegistry mSnapshot = null;
|
||||
private final SnapshotCache<InstantAppRegistry> mSnapshot;
|
||||
|
||||
/**
|
||||
* Watchable machinery
|
||||
@@ -162,7 +163,6 @@ class InstantAppRegistry implements Watchable, Snappable {
|
||||
return mWatchable.isRegisteredObserver(observer);
|
||||
}
|
||||
public void dispatchChange(@Nullable Watchable what) {
|
||||
mSnapshot = null;
|
||||
mWatchable.dispatchChange(what);
|
||||
}
|
||||
/**
|
||||
@@ -180,6 +180,16 @@ class InstantAppRegistry implements Watchable, Snappable {
|
||||
}
|
||||
};
|
||||
|
||||
private SnapshotCache<InstantAppRegistry> makeCache() {
|
||||
return new SnapshotCache<InstantAppRegistry>(this, this) {
|
||||
@Override
|
||||
public InstantAppRegistry createSnapshot() {
|
||||
InstantAppRegistry s = new InstantAppRegistry(mSource);
|
||||
s.mWatchable.seal();
|
||||
return s;
|
||||
}};
|
||||
}
|
||||
|
||||
public InstantAppRegistry(PackageManagerService service,
|
||||
PermissionManagerServiceInternal permissionManager) {
|
||||
mService = service;
|
||||
@@ -194,6 +204,8 @@ class InstantAppRegistry implements Watchable, Snappable {
|
||||
mInstantGrants.registerObserver(mObserver);
|
||||
mInstalledInstantAppUids.registerObserver(mObserver);
|
||||
Watchable.verifyWatchedAttributes(this, mObserver);
|
||||
|
||||
mSnapshot = makeCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,20 +223,15 @@ class InstantAppRegistry implements Watchable, Snappable {
|
||||
mInstalledInstantAppUids = new WatchedSparseArray<WatchedSparseBooleanArray>(
|
||||
r.mInstalledInstantAppUids);
|
||||
|
||||
// Do not register any observers. This is a clone
|
||||
// Do not register any observers. This is a snapshot.
|
||||
mSnapshot = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a snapshot: the value is the cached snapshot if available.
|
||||
*/
|
||||
public InstantAppRegistry snapshot() {
|
||||
InstantAppRegistry s = mSnapshot;
|
||||
if (s == null) {
|
||||
s = new InstantAppRegistry(this);
|
||||
s.mWatchable.seal();
|
||||
mSnapshot = s;
|
||||
}
|
||||
return s;
|
||||
return mSnapshot.snapshot();
|
||||
}
|
||||
|
||||
@GuardedBy("mService.mLock")
|
||||
|
||||
@@ -117,6 +117,7 @@ import com.android.server.pm.verify.domain.DomainVerificationLegacySettings;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationPersistence;
|
||||
import com.android.server.utils.Snappable;
|
||||
import com.android.server.utils.SnapshotCache;
|
||||
import com.android.server.utils.TimingsTraceAndSlog;
|
||||
import com.android.server.utils.Watchable;
|
||||
import com.android.server.utils.WatchableImpl;
|
||||
@@ -164,11 +165,6 @@ import java.util.UUID;
|
||||
public final class Settings implements Watchable, Snappable {
|
||||
private static final String TAG = "PackageSettings";
|
||||
|
||||
/**
|
||||
* Cached snapshot
|
||||
*/
|
||||
private volatile Settings mSnapshot = null;
|
||||
|
||||
/**
|
||||
* Watchable machinery
|
||||
*/
|
||||
@@ -212,7 +208,6 @@ public final class Settings implements Watchable, Snappable {
|
||||
* @param what The {@link Watchable} that generated the event.
|
||||
*/
|
||||
public void dispatchChange(@Nullable Watchable what) {
|
||||
mSnapshot = null;
|
||||
mWatchable.dispatchChange(what);
|
||||
}
|
||||
/**
|
||||
@@ -523,6 +518,19 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
};
|
||||
|
||||
private final SnapshotCache<Settings> mSnapshot;
|
||||
|
||||
// Create a snapshot cache
|
||||
private SnapshotCache<Settings> makeCache() {
|
||||
return new SnapshotCache<Settings>(this, this) {
|
||||
@Override
|
||||
public Settings createSnapshot() {
|
||||
Settings s = new Settings(mSource);
|
||||
s.mWatchable.seal();
|
||||
return s;
|
||||
}};
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
|
||||
public Settings(Map<String, PackageSetting> pkgSettings) {
|
||||
mLock = new PackageManagerTracedLock();
|
||||
@@ -557,6 +565,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
mDefaultBrowserApp.registerObserver(mObserver);
|
||||
|
||||
Watchable.verifyWatchedAttributes(this, mObserver);
|
||||
|
||||
mSnapshot = makeCache();
|
||||
}
|
||||
|
||||
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
|
||||
@@ -608,6 +618,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
mDefaultBrowserApp.registerObserver(mObserver);
|
||||
|
||||
Watchable.verifyWatchedAttributes(this, mObserver);
|
||||
|
||||
mSnapshot = makeCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -661,22 +673,15 @@ public final class Settings implements Watchable, Snappable {
|
||||
mPermissions = r.mPermissions;
|
||||
mPermissionDataProvider = r.mPermissionDataProvider;
|
||||
|
||||
// Do not register any Watchables
|
||||
// Do not register any Watchables and do not create a snapshot cache.
|
||||
mSnapshot = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a snapshot. If the cached snapshot is null, build a new one. The logic in
|
||||
* the function ensures that this function returns a valid snapshot even if a race
|
||||
* condition causes the cached snapshot to be cleared asynchronously to this method.
|
||||
* Return a snapshot.
|
||||
*/
|
||||
public Settings snapshot() {
|
||||
Settings s = mSnapshot;
|
||||
if (s == null) {
|
||||
s = new Settings(this);
|
||||
s.mWatchable.seal();
|
||||
mSnapshot = s;
|
||||
}
|
||||
return s;
|
||||
return mSnapshot.snapshot();
|
||||
}
|
||||
|
||||
private void invalidatePackageCache() {
|
||||
|
||||
@@ -0,0 +1,715 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.PatternMatcher;
|
||||
import android.util.Printer;
|
||||
|
||||
import com.android.server.utils.Snappable;
|
||||
import com.android.server.utils.WatchableImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A watched variant of {@link IntentFilter}. The class consists of a
|
||||
* {@link IntentFilter} attribute and methods that are identical to those in IntentFilter,
|
||||
* forwarding to the attribute.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class WatchedIntentFilter
|
||||
extends WatchableImpl
|
||||
implements Snappable<WatchedIntentFilter> {
|
||||
|
||||
// Watch for modifications made through an {@link Iterator}.
|
||||
private class WatchedIterator<E> implements Iterator<E> {
|
||||
private final Iterator<E> mIterator;
|
||||
WatchedIterator(@NonNull Iterator<E> i) {
|
||||
mIterator = i;
|
||||
}
|
||||
public boolean hasNext() {
|
||||
return mIterator.hasNext();
|
||||
}
|
||||
public E next() {
|
||||
return mIterator.next();
|
||||
}
|
||||
public void remove() {
|
||||
mIterator.remove();
|
||||
WatchedIntentFilter.this.onChanged();
|
||||
}
|
||||
public void forEachRemaining(Consumer<? super E> action) {
|
||||
mIterator.forEachRemaining(action);
|
||||
WatchedIntentFilter.this.onChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// A convenience function to wrap an iterator result, but only if it is not null.
|
||||
private <E> Iterator<E> maybeWatch(Iterator<E> i) {
|
||||
return i == null ? i : new WatchedIterator<>(i);
|
||||
}
|
||||
|
||||
// The wrapped {@link IntentFilter}
|
||||
protected IntentFilter mFilter;
|
||||
|
||||
private void onChanged() {
|
||||
dispatchChange(this);
|
||||
}
|
||||
|
||||
protected WatchedIntentFilter() {
|
||||
mFilter = new IntentFilter();
|
||||
}
|
||||
|
||||
// Convert an {@link IntentFilter} to a {@link WatchedIntentFilter}
|
||||
protected WatchedIntentFilter(IntentFilter f) {
|
||||
mFilter = new IntentFilter(f);
|
||||
}
|
||||
|
||||
// The copy constructor is used to create a snapshot of the object.
|
||||
protected WatchedIntentFilter(WatchedIntentFilter f) {
|
||||
this(f.getIntentFilter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WatchedIntentFilter based on an action
|
||||
* @see IntentFilter#IntentFilter(String)
|
||||
*/
|
||||
public WatchedIntentFilter(String action) {
|
||||
mFilter = new IntentFilter(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WatchedIntentFilter based on an action and a data type.
|
||||
* @see IntentFilter#IntentFilter(String, String)
|
||||
*/
|
||||
public WatchedIntentFilter(String action, String dataType)
|
||||
throws IntentFilter.MalformedMimeTypeException {
|
||||
mFilter = new IntentFilter(action, dataType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a clone of the filter represented by this object.
|
||||
*/
|
||||
public WatchedIntentFilter cloneFilter() {
|
||||
return new WatchedIntentFilter(mFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link IntentFilter} represented by this object.
|
||||
*/
|
||||
public IntentFilter getIntentFilter() {
|
||||
return mFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#setPriority(int)
|
||||
*/
|
||||
public final void setPriority(int priority) {
|
||||
mFilter.setPriority(priority);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getPriority()
|
||||
*/
|
||||
public final int getPriority() {
|
||||
return mFilter.getPriority();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#setOrder(int)
|
||||
*/
|
||||
public final void setOrder(int order) {
|
||||
mFilter.setOrder(order);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getOrder()
|
||||
*/
|
||||
public final int getOrder() {
|
||||
return mFilter.getOrder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getAutoVerify()
|
||||
*/
|
||||
public final boolean getAutoVerify() {
|
||||
return mFilter.getAutoVerify();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#handleAllWebDataURI()
|
||||
*/
|
||||
public final boolean handleAllWebDataURI() {
|
||||
return mFilter.handleAllWebDataURI();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#handlesWebUris(boolean)
|
||||
*/
|
||||
public final boolean handlesWebUris(boolean onlyWebSchemes) {
|
||||
return mFilter.handlesWebUris(onlyWebSchemes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#needsVerification()
|
||||
*/
|
||||
public final boolean needsVerification() {
|
||||
return mFilter.needsVerification();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#setVerified(boolean)
|
||||
*/
|
||||
public void setVerified(boolean verified) {
|
||||
mFilter.setVerified(verified);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#setVisibilityToInstantApp(int)
|
||||
*/
|
||||
public void setVisibilityToInstantApp(int visibility) {
|
||||
mFilter.setVisibilityToInstantApp(visibility);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getVisibilityToInstantApp()
|
||||
*/
|
||||
public int getVisibilityToInstantApp() {
|
||||
return mFilter.getVisibilityToInstantApp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#isVisibleToInstantApp()
|
||||
*/
|
||||
public boolean isVisibleToInstantApp() {
|
||||
return mFilter.isVisibleToInstantApp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#isExplicitlyVisibleToInstantApp()
|
||||
*/
|
||||
public boolean isExplicitlyVisibleToInstantApp() {
|
||||
return mFilter.isExplicitlyVisibleToInstantApp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#isImplicitlyVisibleToInstantApp()
|
||||
*/
|
||||
public boolean isImplicitlyVisibleToInstantApp() {
|
||||
return mFilter.isImplicitlyVisibleToInstantApp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addAction(String)
|
||||
*/
|
||||
public final void addAction(String action) {
|
||||
mFilter.addAction(action);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countActions()
|
||||
*/
|
||||
public final int countActions() {
|
||||
return mFilter.countActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getAction(int)
|
||||
*/
|
||||
public final String getAction(int index) {
|
||||
return mFilter.getAction(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasAction(String)
|
||||
*/
|
||||
public final boolean hasAction(String action) {
|
||||
return mFilter.hasAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#matchAction(String)
|
||||
*/
|
||||
public final boolean matchAction(String action) {
|
||||
return mFilter.matchAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#actionsIterator()
|
||||
*/
|
||||
public final Iterator<String> actionsIterator() {
|
||||
return maybeWatch(mFilter.actionsIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataType(String)
|
||||
*/
|
||||
public final void addDataType(String type)
|
||||
throws IntentFilter.MalformedMimeTypeException {
|
||||
mFilter.addDataType(type);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDynamicDataType(String)
|
||||
*/
|
||||
public final void addDynamicDataType(String type)
|
||||
throws IntentFilter.MalformedMimeTypeException {
|
||||
mFilter.addDynamicDataType(type);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#clearDynamicDataTypes()
|
||||
*/
|
||||
public final void clearDynamicDataTypes() {
|
||||
mFilter.clearDynamicDataTypes();
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countStaticDataTypes()
|
||||
*/
|
||||
public int countStaticDataTypes() {
|
||||
return mFilter.countStaticDataTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasDataType(String)
|
||||
*/
|
||||
public final boolean hasDataType(String type) {
|
||||
return mFilter.hasDataType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasExactDynamicDataType(String)
|
||||
*/
|
||||
public final boolean hasExactDynamicDataType(String type) {
|
||||
return mFilter.hasExactDynamicDataType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasExactStaticDataType(String)
|
||||
*/
|
||||
public final boolean hasExactStaticDataType(String type) {
|
||||
return mFilter.hasExactStaticDataType(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countDataTypes()
|
||||
*/
|
||||
public final int countDataTypes() {
|
||||
return mFilter.countDataTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getDataType(int)
|
||||
*/
|
||||
public final String getDataType(int index) {
|
||||
return mFilter.getDataType(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#typesIterator()
|
||||
*/
|
||||
public final Iterator<String> typesIterator() {
|
||||
return maybeWatch(mFilter.typesIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @see IntentFilter#dataTypes()
|
||||
*/
|
||||
public final List<String> dataTypes() {
|
||||
return mFilter.dataTypes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addMimeGroup(String)
|
||||
*/
|
||||
public final void addMimeGroup(String name) {
|
||||
mFilter.addMimeGroup(name);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasMimeGroup(String)
|
||||
*/
|
||||
public final boolean hasMimeGroup(String name) {
|
||||
return mFilter.hasMimeGroup(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getMimeGroup(int)
|
||||
*/
|
||||
public final String getMimeGroup(int index) {
|
||||
return mFilter.getMimeGroup(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countMimeGroups()
|
||||
*/
|
||||
public final int countMimeGroups() {
|
||||
return mFilter.countMimeGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentAction@mimeGroupsIterator()
|
||||
*/
|
||||
public final Iterator<String> mimeGroupsIterator() {
|
||||
return maybeWatch(mFilter.mimeGroupsIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataScheme(String)
|
||||
*/
|
||||
public final void addDataScheme(String scheme) {
|
||||
mFilter.addDataScheme(scheme);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countDataSchemes()
|
||||
*/
|
||||
public final int countDataSchemes() {
|
||||
return mFilter.countDataSchemes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getDataScheme(int)
|
||||
*/
|
||||
public final String getDataScheme(int index) {
|
||||
return mFilter.getDataScheme(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasDataScheme(String)
|
||||
*/
|
||||
public final boolean hasDataScheme(String scheme) {
|
||||
return mFilter.hasDataScheme(scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#schemesIterator()
|
||||
*/
|
||||
public final Iterator<String> schemesIterator() {
|
||||
return maybeWatch(mFilter.schemesIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataSchemeSpecificPart(String, int)
|
||||
*/
|
||||
public final void addDataSchemeSpecificPart(String ssp, int type) {
|
||||
mFilter.addDataSchemeSpecificPart(ssp, type);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataSchemeSpecificPart(PatternMatcher)
|
||||
*/
|
||||
public final void addDataSchemeSpecificPart(PatternMatcher ssp) {
|
||||
mFilter.addDataSchemeSpecificPart(ssp);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countDataSchemeSpecificParts()
|
||||
*/
|
||||
public final int countDataSchemeSpecificParts() {
|
||||
return mFilter.countDataSchemeSpecificParts();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getDataSchemeSpecificPart(int)
|
||||
*/
|
||||
public final PatternMatcher getDataSchemeSpecificPart(int index) {
|
||||
return mFilter.getDataSchemeSpecificPart(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasDataSchemeSpecificPart(String)
|
||||
*/
|
||||
public final boolean hasDataSchemeSpecificPart(String data) {
|
||||
return mFilter.hasDataSchemeSpecificPart(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#schemeSpecificPartsIterator()
|
||||
*/
|
||||
public final Iterator<PatternMatcher> schemeSpecificPartsIterator() {
|
||||
return maybeWatch(mFilter.schemeSpecificPartsIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataAuthority(String, String)
|
||||
*/
|
||||
public final void addDataAuthority(String host, String port) {
|
||||
mFilter.addDataAuthority(host, port);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataAuthority(IntentFilter.AuthorityEntry)
|
||||
*/
|
||||
public final void addDataAuthority(IntentFilter.AuthorityEntry ent) {
|
||||
mFilter.addDataAuthority(ent);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countDataAuthorities()
|
||||
*/
|
||||
public final int countDataAuthorities() {
|
||||
return mFilter.countDataAuthorities();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getDataAuthority(int)
|
||||
*/
|
||||
public final IntentFilter.AuthorityEntry getDataAuthority(int index) {
|
||||
return mFilter.getDataAuthority(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasDataAuthority(Uri)
|
||||
*/
|
||||
public final boolean hasDataAuthority(Uri data) {
|
||||
return mFilter.hasDataAuthority(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#authoritiesIterator()
|
||||
*/
|
||||
public final Iterator<IntentFilter.AuthorityEntry> authoritiesIterator() {
|
||||
return maybeWatch(mFilter.authoritiesIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataPath(String, int)
|
||||
*/
|
||||
public final void addDataPath(String path, int type) {
|
||||
mFilter.addDataPath(path, type);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addDataPath(PatternMatcher)
|
||||
*/
|
||||
public final void addDataPath(PatternMatcher path) {
|
||||
mFilter.addDataPath(path);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countDataPaths()
|
||||
*/
|
||||
public final int countDataPaths() {
|
||||
return mFilter.countDataPaths();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getDataPath(int)
|
||||
*/
|
||||
public final PatternMatcher getDataPath(int index) {
|
||||
return mFilter.getDataPath(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasDataPath(String)
|
||||
*/
|
||||
public final boolean hasDataPath(String data) {
|
||||
return mFilter.hasDataPath(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#pathsIterator()
|
||||
*/
|
||||
public final Iterator<PatternMatcher> pathsIterator() {
|
||||
return maybeWatch(mFilter.pathsIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#matchDataAuthority(Uri)
|
||||
*/
|
||||
public final int matchDataAuthority(Uri data) {
|
||||
return mFilter.matchDataAuthority(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#matchDataAuthority(Uri, boolean)
|
||||
*/
|
||||
public final int matchDataAuthority(Uri data, boolean wildcardSupported) {
|
||||
return mFilter.matchDataAuthority(data, wildcardSupported);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#matchData(String, String, Uri)
|
||||
*/
|
||||
public final int matchData(String type, String scheme, Uri data) {
|
||||
return mFilter.matchData(type, scheme, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#addCategory(String)
|
||||
*/
|
||||
public final void addCategory(String category) {
|
||||
mFilter.addCategory(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#countCategories()
|
||||
*/
|
||||
public final int countCategories() {
|
||||
return mFilter.countCategories();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getCategory(int)
|
||||
*/
|
||||
public final String getCategory(int index) {
|
||||
return mFilter.getCategory(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#hasCategory(String)
|
||||
*/
|
||||
public final boolean hasCategory(String category) {
|
||||
return mFilter.hasCategory(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#categoriesIterator()
|
||||
*/
|
||||
public final Iterator<String> categoriesIterator() {
|
||||
return maybeWatch(mFilter.categoriesIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#matchCategories(Set<String>)
|
||||
*/
|
||||
public final String matchCategories(Set<String> categories) {
|
||||
return mFilter.matchCategories(categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#match(ContentResolver, Intent, boolean, String)
|
||||
*/
|
||||
public final int match(ContentResolver resolver, Intent intent,
|
||||
boolean resolve, String logTag) {
|
||||
return mFilter.match(resolver, intent,
|
||||
resolve, logTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#match(String, String, String, Uri, Set<String>, String)
|
||||
*/
|
||||
public final int match(String action, String type, String scheme,
|
||||
Uri data, Set<String> categories, String logTag) {
|
||||
return mFilter.match(action, type, scheme,
|
||||
data, categories, logTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#match(String, String, String, Uri, Set<String>, String, boolean,
|
||||
Collection<String> ignoreActions)
|
||||
*/
|
||||
public final int match(String action, String type, String scheme,
|
||||
Uri data, Set<String> categories, String logTag, boolean supportWildcards,
|
||||
Collection<String> ignoreActions) {
|
||||
return mFilter.match(action, type, scheme,
|
||||
data, categories, logTag, supportWildcards,
|
||||
ignoreActions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#dump(Printer, String)
|
||||
*/
|
||||
public void dump(Printer du, String prefix) {
|
||||
mFilter.dump(du, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#describeContents()
|
||||
*/
|
||||
public final int describeContents() {
|
||||
return mFilter.describeContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#debugCheck()
|
||||
*/
|
||||
public boolean debugCheck() {
|
||||
return mFilter.debugCheck();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getHostsList()
|
||||
*/
|
||||
public ArrayList<String> getHostsList() {
|
||||
return mFilter.getHostsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IntentFilter#getHosts()
|
||||
*/
|
||||
public String[] getHosts() {
|
||||
return mFilter.getHosts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list of {@link IntentFilter} into a list of {@link WatchedIntentFilter}
|
||||
*/
|
||||
public static List<WatchedIntentFilter> toWatchedIntentFilterList(List<IntentFilter> inList) {
|
||||
ArrayList<WatchedIntentFilter> outList = new ArrayList<>();
|
||||
for (int i = 0; i < inList.size(); i++) {
|
||||
outList.add(new WatchedIntentFilter(inList.get(i)));
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a list of {@link IntentFilter} into a list of {@link WatchedIntentFilter}
|
||||
*/
|
||||
public static List<IntentFilter> toIntentFilterList(List<WatchedIntentFilter> inList) {
|
||||
ArrayList<IntentFilter> outList = new ArrayList<>();
|
||||
for (int i = 0; i < inList.size(); i++) {
|
||||
outList.add(inList.get(i).getIntentFilter());
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a snapshot by cloning the object.
|
||||
*/
|
||||
public WatchedIntentFilter snapshot() {
|
||||
return new WatchedIntentFilter(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.utils;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A class that caches snapshots. Instances are instantiated on a {@link Watchable}; when the
|
||||
* {@link Watchable} reports a change, the cache is cleared. The snapshot() method fetches the
|
||||
* cache if it is valid, or rebuilds the cache if it has been cleared.
|
||||
*
|
||||
* The class is abstract; clients must implement the createSnapshot() method.
|
||||
*
|
||||
* @param <T> The type returned by the snapshot() method.
|
||||
*/
|
||||
public abstract class SnapshotCache<T> extends Watcher{
|
||||
|
||||
/**
|
||||
* Global snapshot cache enable flag. Set to false for testing or debugging.
|
||||
*/
|
||||
private static final boolean ENABLED = true;
|
||||
|
||||
// The source object from which snapshots are created. This may be null if createSnapshot()
|
||||
// does not require it.
|
||||
protected final T mSource;
|
||||
|
||||
// The cached snapshot
|
||||
private T mSnapshot = null;
|
||||
|
||||
// True if the snapshot is sealed and may not be modified.
|
||||
private boolean mSealed = false;
|
||||
|
||||
/**
|
||||
* Create a cache with a source object for rebuilding snapshots and a
|
||||
* {@link Watchable} that notifies when the cache is invalid.
|
||||
* @param source Source data for rebuilding snapshots.
|
||||
* @param watchable The object that notifies when the cache is invalid.
|
||||
*/
|
||||
public SnapshotCache(@Nullable T source, @NonNull Watchable watchable) {
|
||||
mSource = source;
|
||||
watchable.registerObserver(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the object that the source object has changed. If the local object is sealed then
|
||||
* IllegalStateException is thrown. Otherwise, the cache is cleared.
|
||||
*/
|
||||
public void onChange(@Nullable Watchable what) {
|
||||
if (mSealed) {
|
||||
throw new IllegalStateException("attempt to change a sealed object");
|
||||
}
|
||||
mSnapshot = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seal the cache. Attempts to modify the cache will generate an exception.
|
||||
*/
|
||||
public void seal() {
|
||||
mSealed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a snapshot. This uses the cache if it is non-null. Otherwise it creates a
|
||||
* new snapshot and saves it in the cache.
|
||||
* @return A snapshot as returned by createSnapshot() and possibly cached.
|
||||
*/
|
||||
public T snapshot() {
|
||||
T s = mSnapshot;
|
||||
if (s == null || !ENABLED) {
|
||||
s = createSnapshot();
|
||||
mSnapshot = s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single, uncached snapshot. Clients must implement this per local rules.
|
||||
* @return A snapshot
|
||||
*/
|
||||
public abstract T createSnapshot();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.IntentFilter;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.server.utils.WatchableTester;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@SmallTest
|
||||
public class WatchedIntentHandlingTest {
|
||||
|
||||
@Test
|
||||
public void testWatchedIntentFilter() {
|
||||
IntentFilter i = new IntentFilter("TEST_ACTION");
|
||||
WatchedIntentFilter f = new WatchedIntentFilter(i);
|
||||
final WatchableTester watcher =
|
||||
new WatchableTester(f, "WatchedIntentFilter");
|
||||
watcher.register();
|
||||
int wantPriority = 3;
|
||||
f.setPriority(wantPriority);
|
||||
watcher.verifyChangeReported("setPriority");
|
||||
f.getPriority();
|
||||
watcher.verifyNoChangeReported("getPriority");
|
||||
assertTrue(f.getPriority() == wantPriority);
|
||||
f.setPriority(f.getPriority() + 1);
|
||||
watcher.verifyChangeReported("setPriority");
|
||||
assertTrue(f.getPriority() == wantPriority + 1);
|
||||
|
||||
i.setPriority(wantPriority + 3);
|
||||
watcher.verifyNoChangeReported("indendent intent");
|
||||
assertTrue(f.getPriority() == wantPriority + 1);
|
||||
|
||||
f.addAction("action-1");
|
||||
f.addAction("action-2");
|
||||
f.addAction("action-3");
|
||||
f.addAction("action-4");
|
||||
watcher.verifyChangeReported("addAction");
|
||||
int actionCount = f.countActions();
|
||||
|
||||
Iterator<String> actions = f.actionsIterator();
|
||||
watcher.verifyNoChangeReported("actionsIterator 1");
|
||||
int count = 0;
|
||||
while (actions.hasNext()) {
|
||||
assertTrue(f.hasAction(actions.next()));
|
||||
count++;
|
||||
}
|
||||
watcher.verifyNoChangeReported("actionsIterator 2");
|
||||
assertTrue(count == actionCount);
|
||||
|
||||
actions = f.actionsIterator();
|
||||
watcher.verifyNoChangeReported("actionsIterator 1");
|
||||
while (actions.hasNext()) {
|
||||
if (actions.next().equals("action-3")) {
|
||||
actions.remove();
|
||||
watcher.verifyChangeReported("remove action");
|
||||
}
|
||||
}
|
||||
assertTrue(f.countActions() == actionCount - 1);
|
||||
|
||||
WatchedIntentFilter s1 = f.snapshot();
|
||||
watcher.verifyNoChangeReported("pulled snapshot");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -896,4 +896,25 @@ public class WatcherTest {
|
||||
leafD.tick();
|
||||
tester.verify(3, "tick leafD");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshotCache() {
|
||||
final String name = "SnapshotCache";
|
||||
WatchableTester tester;
|
||||
|
||||
Leaf leafA = new Leaf();
|
||||
SnapshotCache<Leaf> cache = new SnapshotCache<>(leafA, leafA) {
|
||||
@Override
|
||||
public Leaf createSnapshot() {
|
||||
return mSource.snapshot();
|
||||
}};
|
||||
|
||||
Leaf s1 = cache.snapshot();
|
||||
assertTrue(s1 == cache.snapshot());
|
||||
leafA.tick();
|
||||
Leaf s2 = cache.snapshot();
|
||||
assertTrue(s1 != s2);
|
||||
assertTrue(leafA.get() == s1.get() + 1);
|
||||
assertTrue(leafA.get() == s2.get());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user