From a1c0adb05030c378a8ca8f5cf0e24c5a35f425fa Mon Sep 17 00:00:00 2001 From: Zeyin Wu Date: Wed, 26 Oct 2022 22:31:54 +0000 Subject: Add Accessibility API for rate-limiting CONTENT_CHANGE_TYPE* events Bug: 222759137 Test: ran cts test Change-Id: Ia49209d7138bd5bd1026dc18210552b3f359664e --- core/api/current.txt | 4 ++ core/api/test-current.txt | 1 + core/java/android/view/ViewConfiguration.java | 1 + .../view/accessibility/AccessibilityNodeInfo.java | 63 ++++++++++++++++++++++ .../accessibility/AccessibilityNodeInfoTest.java | 2 +- 5 files changed, 70 insertions(+), 1 deletion(-) diff --git a/core/api/current.txt b/core/api/current.txt index a862ee007b88..d661cc3b820d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -52350,6 +52350,7 @@ package android.view.accessibility { method public android.view.accessibility.AccessibilityNodeInfo getLabeledBy(); method public int getLiveRegion(); method public int getMaxTextLength(); + method public int getMinMillisBetweenContentChanges(); method public int getMovementGranularities(); method public CharSequence getPackageName(); method @Nullable public CharSequence getPaneTitle(); @@ -52437,6 +52438,7 @@ package android.view.accessibility { method public void setLiveRegion(int); method public void setLongClickable(boolean); method public void setMaxTextLength(int); + method public void setMinMillisBetweenContentChanges(int); method public void setMovementGranularities(int); method public void setMultiLine(boolean); method public void setPackageName(CharSequence); @@ -52516,11 +52518,13 @@ package android.view.accessibility { field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2 field public static final int FOCUS_INPUT = 1; // 0x1 field public static final int MAX_NUMBER_OF_PREFETCHED_NODES = 50; // 0x32 + field public static final int MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = 100; // 0x64 field public static final int MOVEMENT_GRANULARITY_CHARACTER = 1; // 0x1 field public static final int MOVEMENT_GRANULARITY_LINE = 4; // 0x4 field public static final int MOVEMENT_GRANULARITY_PAGE = 16; // 0x10 field public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 8; // 0x8 field public static final int MOVEMENT_GRANULARITY_WORD = 2; // 0x2 + field public static final int UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = -1; // 0xffffffff } public static final class AccessibilityNodeInfo.AccessibilityAction implements android.os.Parcelable { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index f0763950ba37..60fcf18b517b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2944,6 +2944,7 @@ package android.view { method public static int getHoverTooltipHideTimeout(); method public static int getHoverTooltipShowTimeout(); method public static int getLongPressTooltipHideTimeout(); + method public static long getSendRecurringAccessibilityEventsInterval(); method public boolean isPreferKeepClearForFocusEnabled(); } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index f51d9bacc0a5..58aee61b98be 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -822,6 +822,7 @@ public class ViewConfiguration { * * @hide */ + @TestApi public static long getSendRecurringAccessibilityEventsInterval() { return SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS; } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 9dbababb4de8..88adb2e1b1f1 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -127,6 +127,16 @@ public class AccessibilityNodeInfo implements Parcelable { /** @hide */ public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID); + /** + * The default value for {@link #getMinMillisBetweenContentChanges}; + */ + public static final int UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = -1; + + /** + * The minimum value for {@link #setMinMillisBetweenContentChanges}; + */ + public static final int MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES = 100; + /** @hide */ public static final long ROOT_NODE_ID = makeNodeId(ROOT_ITEM_ID, AccessibilityNodeProvider.HOST_VIEW_ID); @@ -879,6 +889,9 @@ public class AccessibilityNodeInfo implements Parcelable { private long mTraversalBefore = UNDEFINED_NODE_ID; private long mTraversalAfter = UNDEFINED_NODE_ID; + private int mMinMillisBetweenContentChanges = + UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES; + private int mBooleanProperties; private final Rect mBoundsInParent = new Rect(); private final Rect mBoundsInScreen = new Rect(); @@ -1781,6 +1794,42 @@ public class AccessibilityNodeInfo implements Parcelable { return mMovementGranularities; } + /** + * Sets the minimum time duration between two content change events, which is used in throttling + * content change events in accessibility services. + * + *

+ * Note: + * This value should not be smaller than {@link #MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES}, + * otherwise it would be ignored by accessibility services. + *

+ * + *

+ * Example: An app can set MinMillisBetweenContentChanges as 1 min for a view which sends + * content change events to accessibility services one event per second. + * Accessibility service will throttle those content change events and only handle one event + * per minute for that view. + *

+ * + * @see AccessibilityEvent#getContentChangeTypes for all content change types. + * @param minMillisBetweenContentChanges the minimum duration between content change events. + */ + public void setMinMillisBetweenContentChanges(int minMillisBetweenContentChanges) { + enforceNotSealed(); + mMinMillisBetweenContentChanges = minMillisBetweenContentChanges + >= MINIMUM_MIN_MILLIS_BETWEEN_CONTENT_CHANGES + ? minMillisBetweenContentChanges + : UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES; + } + + /** + * Gets the minimum time duration between two content change events. This method may return + * {@link #UNDEFINED_MIN_MILLIS_BETWEEN_CONTENT_CHANGES} + */ + public int getMinMillisBetweenContentChanges() { + return mMinMillisBetweenContentChanges; + } + /** * Performs an action on the node. *

@@ -3951,6 +4000,11 @@ public class AccessibilityNodeInfo implements Parcelable { fieldIndex++; if (mTraversalAfter != DEFAULT.mTraversalAfter) nonDefaultFields |= bitAt(fieldIndex); fieldIndex++; + if (mMinMillisBetweenContentChanges + != DEFAULT.mMinMillisBetweenContentChanges) { + nonDefaultFields |= bitAt(fieldIndex); + } + fieldIndex++; if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex); fieldIndex++; if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) { @@ -4080,6 +4134,9 @@ public class AccessibilityNodeInfo implements Parcelable { if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabeledById); if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalBefore); if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalAfter); + if (isBitSet(nonDefaultFields, fieldIndex++)) { + parcel.writeInt(mMinMillisBetweenContentChanges); + } if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mConnectionId); @@ -4235,6 +4292,7 @@ public class AccessibilityNodeInfo implements Parcelable { mLabeledById = other.mLabeledById; mTraversalBefore = other.mTraversalBefore; mTraversalAfter = other.mTraversalAfter; + mMinMillisBetweenContentChanges = other.mMinMillisBetweenContentChanges; mWindowId = other.mWindowId; mConnectionId = other.mConnectionId; mUniqueId = other.mUniqueId; @@ -4338,6 +4396,9 @@ public class AccessibilityNodeInfo implements Parcelable { if (isBitSet(nonDefaultFields, fieldIndex++)) mLabeledById = parcel.readLong(); if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalBefore = parcel.readLong(); if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalAfter = parcel.readLong(); + if (isBitSet(nonDefaultFields, fieldIndex++)) { + mMinMillisBetweenContentChanges = parcel.readInt(); + } if (isBitSet(nonDefaultFields, fieldIndex++)) mConnectionId = parcel.readInt(); @@ -4686,6 +4747,8 @@ public class AccessibilityNodeInfo implements Parcelable { builder.append("; mParentNodeId: 0x").append(Long.toHexString(mParentNodeId)); builder.append("; traversalBefore: 0x").append(Long.toHexString(mTraversalBefore)); builder.append("; traversalAfter: 0x").append(Long.toHexString(mTraversalAfter)); + builder.append("; minMillisBetweenContentChanges: ") + .append(mMinMillisBetweenContentChanges); int granularities = mMovementGranularities; builder.append("; MovementGranularities: ["); diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java index 32085c1cfbeb..cc02bbb3e7d1 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java @@ -46,7 +46,7 @@ public class AccessibilityNodeInfoTest { // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest: // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo, // and assertAccessibilityNodeInfoCleared in that class. - private static final int NUM_MARSHALLED_PROPERTIES = 41; + private static final int NUM_MARSHALLED_PROPERTIES = 42; /** * The number of properties that are purposely not marshalled -- cgit v1.2.3-59-g8ed1b