Merge "Add and implement cache API"
This commit is contained in:
@@ -3014,6 +3014,8 @@ package android.accessibilityservice {
|
||||
|
||||
public abstract class AccessibilityService extends android.app.Service {
|
||||
ctor public AccessibilityService();
|
||||
method public boolean clearCache();
|
||||
method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
|
||||
method public final void disableSelf();
|
||||
method public final boolean dispatchGesture(@NonNull android.accessibilityservice.GestureDescription, @Nullable android.accessibilityservice.AccessibilityService.GestureResultCallback, @Nullable android.os.Handler);
|
||||
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
|
||||
@@ -3028,6 +3030,8 @@ package android.accessibilityservice {
|
||||
method @NonNull public final android.accessibilityservice.TouchInteractionController getTouchInteractionController(int);
|
||||
method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
|
||||
method @NonNull public final android.util.SparseArray<java.util.List<android.view.accessibility.AccessibilityWindowInfo>> getWindowsOnAllDisplays();
|
||||
method public boolean isCacheEnabled();
|
||||
method public boolean isNodeInCache(@NonNull android.view.accessibility.AccessibilityNodeInfo);
|
||||
method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
|
||||
method public final android.os.IBinder onBind(android.content.Intent);
|
||||
method @Deprecated protected boolean onGesture(int);
|
||||
@@ -3038,6 +3042,7 @@ package android.accessibilityservice {
|
||||
method public void onSystemActionsChanged();
|
||||
method public final boolean performGlobalAction(int);
|
||||
method public void setAccessibilityFocusAppearance(int, @ColorInt int);
|
||||
method public boolean setCacheEnabled(boolean);
|
||||
method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region);
|
||||
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
|
||||
method public void setTouchExplorationPassthroughRegion(int, @NonNull android.graphics.Region);
|
||||
|
||||
@@ -59,6 +59,7 @@ import android.view.MotionEvent;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerImpl;
|
||||
import android.view.accessibility.AccessibilityCache;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityInteractionClient;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
@@ -748,7 +749,6 @@ public abstract class AccessibilityService extends Service {
|
||||
|
||||
private FingerprintGestureController mFingerprintGestureController;
|
||||
|
||||
|
||||
/**
|
||||
* Callback for {@link android.view.accessibility.AccessibilityEvent}s.
|
||||
*
|
||||
@@ -2076,6 +2076,85 @@ public abstract class AccessibilityService extends Service {
|
||||
available);
|
||||
}
|
||||
|
||||
/** Sets the cache status.
|
||||
*
|
||||
* <p>If {@code enabled}, enable the cache and prefetching. Otherwise, disable the cache
|
||||
* and prefetching.
|
||||
* Note: By default the cache is enabled.
|
||||
* @param enabled whether to enable or disable the cache.
|
||||
* @return {@code true} if the cache and connection are not null, so the cache status is set.
|
||||
*/
|
||||
public boolean setCacheEnabled(boolean enabled) {
|
||||
AccessibilityCache cache =
|
||||
AccessibilityInteractionClient.getCache(mConnectionId);
|
||||
if (cache == null) {
|
||||
return false;
|
||||
}
|
||||
final IAccessibilityServiceConnection connection =
|
||||
AccessibilityInteractionClient.getConnection(mConnectionId);
|
||||
if (connection == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
connection.setCacheEnabled(enabled);
|
||||
cache.setEnabled(enabled);
|
||||
return true;
|
||||
} catch (RemoteException re) {
|
||||
Log.w(LOG_TAG, "Error while setting status of cache", re);
|
||||
re.rethrowFromSystemServer();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Invalidates {@code node} and its subtree in the cache.
|
||||
* @param node the node to invalidate.
|
||||
* @return {@code true} if the subtree rooted at {@code node} was invalidated.
|
||||
*/
|
||||
public boolean clearCachedSubtree(@NonNull AccessibilityNodeInfo node) {
|
||||
AccessibilityCache cache =
|
||||
AccessibilityInteractionClient.getCache(mConnectionId);
|
||||
if (cache == null) {
|
||||
return false;
|
||||
}
|
||||
return cache.clearSubTree(node);
|
||||
}
|
||||
|
||||
/** Clears the cache.
|
||||
* @return {@code true} if the cache was cleared
|
||||
*/
|
||||
public boolean clearCache() {
|
||||
AccessibilityCache cache =
|
||||
AccessibilityInteractionClient.getCache(mConnectionId);
|
||||
if (cache == null) {
|
||||
return false;
|
||||
}
|
||||
cache.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Checks if {@code node} is in the cache.
|
||||
* @param node the node to check.
|
||||
* @return {@code true} if {@code node} is in the cache.
|
||||
*/
|
||||
public boolean isNodeInCache(@NonNull AccessibilityNodeInfo node) {
|
||||
AccessibilityCache cache =
|
||||
AccessibilityInteractionClient.getCache(mConnectionId);
|
||||
if (cache == null) {
|
||||
return false;
|
||||
}
|
||||
return cache.isNodeInCache(node);
|
||||
}
|
||||
|
||||
/** Returns {@code true} if the cache is enabled. */
|
||||
public boolean isCacheEnabled() {
|
||||
AccessibilityCache cache =
|
||||
AccessibilityInteractionClient.getCache(mConnectionId);
|
||||
if (cache == null) {
|
||||
return false;
|
||||
}
|
||||
return cache.isEnabled();
|
||||
}
|
||||
|
||||
/** This is called when the system action list is changed. */
|
||||
public void onSystemActionsChanged() {
|
||||
}
|
||||
@@ -2613,11 +2692,11 @@ public abstract class AccessibilityService extends Service {
|
||||
mCallback.init(mConnectionId, windowToken);
|
||||
mCallback.onServiceConnected();
|
||||
} else {
|
||||
AccessibilityInteractionClient.getInstance(mContext)
|
||||
.clearCache(mConnectionId);
|
||||
AccessibilityInteractionClient.getInstance(mContext).removeConnection(
|
||||
mConnectionId);
|
||||
mConnectionId = AccessibilityInteractionClient.NO_ID;
|
||||
AccessibilityInteractionClient.getInstance(mContext)
|
||||
.clearCache(mConnectionId);
|
||||
mCallback.init(AccessibilityInteractionClient.NO_ID, null);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -124,6 +124,8 @@ interface IAccessibilityServiceConnection {
|
||||
|
||||
void setFocusAppearance(int strokeWidth, int color);
|
||||
|
||||
void setCacheEnabled(boolean enabled);
|
||||
|
||||
oneway void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
|
||||
int processId, long threadId, int callingUid, in Bundle serializedCallingStackInBundle);
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@ public class AccessibilityCache {
|
||||
|
||||
private static final boolean CHECK_INTEGRITY = Build.IS_ENG;
|
||||
|
||||
private boolean mEnabled = true;
|
||||
|
||||
/**
|
||||
* {@link AccessibilityEvent} types that are critical for the cache to stay up to date
|
||||
*
|
||||
@@ -98,6 +100,21 @@ public class AccessibilityCache {
|
||||
mAccessibilityNodeRefresher = nodeRefresher;
|
||||
}
|
||||
|
||||
/** Returns if the cache is enabled. */
|
||||
public boolean isEnabled() {
|
||||
synchronized (mLock) {
|
||||
return mEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets enabled status. */
|
||||
public void setEnabled(boolean enabled) {
|
||||
synchronized (mLock) {
|
||||
mEnabled = enabled;
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all {@link AccessibilityWindowInfo}s of all displays into the cache.
|
||||
* The key of SparseArray is display ID.
|
||||
@@ -110,6 +127,12 @@ public class AccessibilityCache {
|
||||
SparseArray<List<AccessibilityWindowInfo>> windowsOnAllDisplays,
|
||||
long populationTimeStamp) {
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Set windows");
|
||||
}
|
||||
@@ -148,6 +171,12 @@ public class AccessibilityCache {
|
||||
*/
|
||||
public void addWindow(AccessibilityWindowInfo window) {
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Caching window: " + window.getId() + " at display Id [ "
|
||||
+ window.getDisplayId() + " ]");
|
||||
@@ -177,6 +206,12 @@ public class AccessibilityCache {
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
AccessibilityNodeInfo nodeToRefresh = null;
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "onAccessibilityEvent(" + event + ")");
|
||||
}
|
||||
@@ -292,6 +327,12 @@ public class AccessibilityCache {
|
||||
*/
|
||||
public AccessibilityNodeInfo getNode(int windowId, long accessibilityNodeId) {
|
||||
synchronized(mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
@@ -309,6 +350,28 @@ public class AccessibilityCache {
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns {@code true} if {@code info} is in the cache. */
|
||||
public boolean isNodeInCache(AccessibilityNodeInfo info) {
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
int windowId = info.getWindowId();
|
||||
long accessibilityNodeId = info.getSourceNodeId();
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
|
||||
if (nodes == null) {
|
||||
return false;
|
||||
}
|
||||
return nodes.get(accessibilityNodeId) != null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all {@link AccessibilityWindowInfo}s of all displays from the cache.
|
||||
*
|
||||
@@ -317,6 +380,12 @@ public class AccessibilityCache {
|
||||
*/
|
||||
public SparseArray<List<AccessibilityWindowInfo>> getWindowsOnAllDisplays() {
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (!mIsAllWindowsCached) {
|
||||
return null;
|
||||
}
|
||||
@@ -373,6 +442,12 @@ public class AccessibilityCache {
|
||||
*/
|
||||
public AccessibilityWindowInfo getWindow(int windowId) {
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
final int displayCounts = mWindowCacheByDisplay.size();
|
||||
for (int i = 0; i < displayCounts; i++) {
|
||||
final SparseArray<AccessibilityWindowInfo> windowsOfDisplay =
|
||||
@@ -397,6 +472,12 @@ public class AccessibilityCache {
|
||||
*/
|
||||
public void add(AccessibilityNodeInfo info) {
|
||||
synchronized(mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (VERBOSE) {
|
||||
Log.i(LOG_TAG, "add(" + info + ")");
|
||||
}
|
||||
@@ -522,6 +603,12 @@ public class AccessibilityCache {
|
||||
*/
|
||||
public AccessibilityNodeInfo getFocus(int focusType, long initialNodeId, int windowId) {
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
int currentFocusWindowId;
|
||||
long currentFocusId;
|
||||
if (focusType == FOCUS_ACCESSIBILITY) {
|
||||
@@ -602,6 +689,23 @@ public class AccessibilityCache {
|
||||
mNodeCache.remove(windowId);
|
||||
}
|
||||
|
||||
/** Clears a subtree rooted at {@code info}. */
|
||||
public boolean clearSubTree(AccessibilityNodeInfo info) {
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!mEnabled) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Cache is disabled");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
clearSubTreeLocked(info.getWindowId(), info.getSourceNodeId());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears a subtree rooted at the node with the given id that is
|
||||
* hosted in a given window.
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view.accessibility;
|
||||
|
||||
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CLIENT;
|
||||
import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK;
|
||||
import static android.os.Build.VERSION_CODES.S;
|
||||
|
||||
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.annotation.NonNull;
|
||||
@@ -225,6 +226,9 @@ public final class AccessibilityInteractionClient
|
||||
*/
|
||||
public static void addConnection(int connectionId, IAccessibilityServiceConnection connection,
|
||||
boolean initializeCache) {
|
||||
if (connectionId == NO_ID) {
|
||||
return;
|
||||
}
|
||||
synchronized (sConnectionCache) {
|
||||
sConnectionCache.put(connectionId, connection);
|
||||
if (!initializeCache) {
|
||||
@@ -554,6 +558,10 @@ public final class AccessibilityInteractionClient
|
||||
}
|
||||
return cachedInfo;
|
||||
}
|
||||
if (!cache.isEnabled()) {
|
||||
// Skip prefetching if cache is disabled.
|
||||
prefetchFlags &= ~AccessibilityNodeInfo.FLAG_PREFETCH_MASK;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Node cache miss for "
|
||||
+ idToString(accessibilityWindowId, accessibilityNodeId));
|
||||
@@ -970,9 +978,9 @@ public final class AccessibilityInteractionClient
|
||||
/**
|
||||
* Clears the cache associated with {@code connectionId}
|
||||
* @param connectionId the connection id
|
||||
* TODO(207417185): Modify UnsupportedAppUsage
|
||||
*/
|
||||
@UnsupportedAppUsage()
|
||||
@UnsupportedAppUsage(maxTargetSdk = S, publicAlternatives =
|
||||
"{@link android.accessibilityservice.AccessibilityService#clearCache()}")
|
||||
public void clearCache(int connectionId) {
|
||||
AccessibilityCache cache = getCache(connectionId);
|
||||
if (cache == null) {
|
||||
|
||||
@@ -26,6 +26,8 @@ import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
@@ -997,6 +999,60 @@ public class AccessibilityCacheTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enable_cacheEnabled() {
|
||||
mAccessibilityCache.setEnabled(false);
|
||||
assertFalse(mAccessibilityCache.isEnabled());
|
||||
|
||||
mAccessibilityCache.setEnabled(true);
|
||||
assertTrue(mAccessibilityCache.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disable_cacheDisabled() {
|
||||
mAccessibilityCache.setEnabled(false);
|
||||
assertFalse(mAccessibilityCache.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryNode_nodeIsInCache() {
|
||||
AccessibilityNodeInfo info = new AccessibilityNodeInfo();
|
||||
mAccessibilityCache.add(info);
|
||||
|
||||
assertTrue(mAccessibilityCache.isNodeInCache(info));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearSubtreeWithNode_nodeInCacheInvalidated() {
|
||||
AccessibilityNodeInfo info = new AccessibilityNodeInfo();
|
||||
info.setSource(getMockViewWithA11yAndWindowIds(1, 1));
|
||||
mAccessibilityCache.add(info);
|
||||
|
||||
mAccessibilityCache.clearSubTree(info);
|
||||
assertFalse(mAccessibilityCache.isNodeInCache(info));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearSubtreeWithNode_subtreeInCacheInvalidated() {
|
||||
AccessibilityNodeInfo info = new AccessibilityNodeInfo();
|
||||
View parentView = getMockViewWithA11yAndWindowIds(1, 1);
|
||||
info.setSource(parentView);
|
||||
|
||||
AccessibilityNodeInfo childInfo = new AccessibilityNodeInfo();
|
||||
View childView = getMockViewWithA11yAndWindowIds(2, 1);
|
||||
childInfo.setSource(childView);
|
||||
|
||||
childInfo.setParent(parentView);
|
||||
info.addChild(childView);
|
||||
mAccessibilityCache.add(info);
|
||||
mAccessibilityCache.add(childInfo);
|
||||
|
||||
mAccessibilityCache.clearSubTree(info);
|
||||
|
||||
assertFalse(mAccessibilityCache.isNodeInCache(info));
|
||||
assertFalse(mAccessibilityCache.isNodeInCache(childInfo));
|
||||
}
|
||||
|
||||
private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
|
||||
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
|
||||
windowInfo.setId(windowId);
|
||||
|
||||
@@ -173,6 +173,8 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
|
||||
|
||||
public void setFocusAppearance(int strokeWidth, int color) {}
|
||||
|
||||
public void setCacheEnabled(boolean enabled) {}
|
||||
|
||||
public void logTrace(long timestamp, String where, String callingParams, int processId,
|
||||
long threadId, int callingUid, Bundle callingStack) {}
|
||||
|
||||
|
||||
@@ -2032,6 +2032,22 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCacheEnabled(boolean enabled) {
|
||||
if (svcConnTracingEnabled()) {
|
||||
logTraceSvcConn("setCacheEnabled", "enabled=" + enabled);
|
||||
}
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mLock) {
|
||||
mUsesAccessibilityCache = enabled;
|
||||
mSystemSupport.onClientChangeLocked(true);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
|
||||
int processId, long threadId, int callingUid, Bundle callingStack) {
|
||||
|
||||
Reference in New Issue
Block a user