diff options
6 files changed, 99 insertions, 11 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index dc58f11e83fd..b367dc7b9c50 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11791,6 +11791,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return null; } + /** @hide */ + View getSelfOrParentImportantForA11y() { + if (isImportantForAccessibility()) return this; + ViewParent parent = getParentForAccessibility(); + if (parent instanceof View) return (View) parent; + return null; + } + /** * Adds the children of this View relevant for accessibility to the given list * as output. Since some Views are not important for accessibility the added @@ -15006,10 +15014,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) { ensureTransformationInfo(); if (mTransformationInfo.mAlpha != alpha) { - // Report visibility changes, which can affect children, to accessibility - if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) { - notifySubtreeAccessibilityStateChangedIfNeeded(); - } + float oldAlpha = mTransformationInfo.mAlpha; mTransformationInfo.mAlpha = alpha; if (onSetAlpha((int) (alpha * 255))) { mPrivateFlags |= PFLAG_ALPHA_SET; @@ -15021,6 +15026,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(true, false); mRenderNode.setAlpha(getFinalAlpha()); } + // Report visibility changes, which can affect children, to accessibility + if ((alpha == 0) ^ (oldAlpha == 0)) { + notifySubtreeAccessibilityStateChangedIfNeeded(); + } } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e802232bae33..f6d9a16e4246 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -8305,6 +8305,12 @@ public final class ViewRootImpl implements ViewParent, public View mSource; public long mLastEventTimeMillis; + /** + * Override for {@link AccessibilityEvent#originStackTrace} to provide the stack trace + * of the original {@link #runOrPost} call instead of one for sending the delayed event + * from a looper. + */ + public StackTraceElement[] mOrigin; @Override public void run() { @@ -8322,6 +8328,7 @@ public final class ViewRootImpl implements ViewParent, AccessibilityEvent event = AccessibilityEvent.obtain(); event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); event.setContentChangeTypes(mChangeTypes); + if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin; source.sendAccessibilityEventUnchecked(event); } else { mLastEventTimeMillis = 0; @@ -8329,6 +8336,7 @@ public final class ViewRootImpl implements ViewParent, // In any case reset to initial state. source.resetSubtreeAccessibilityStateChanged(); mChangeTypes = 0; + if (AccessibilityEvent.DEBUG_ORIGIN) mOrigin = null; } public void runOrPost(View source, int changeType) { @@ -8352,12 +8360,18 @@ public final class ViewRootImpl implements ViewParent, // If there is no common predecessor, then mSource points to // a removed view, hence in this case always prefer the source. View predecessor = getCommonPredecessor(mSource, source); + if (predecessor != null) { + predecessor = predecessor.getSelfOrParentImportantForA11y(); + } mSource = (predecessor != null) ? predecessor : source; mChangeTypes |= changeType; return; } mSource = source; mChangeTypes = changeType; + if (AccessibilityEvent.DEBUG_ORIGIN) { + mOrigin = Thread.currentThread().getStackTrace(); + } final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis; final long minEventIntevalMillis = ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index e0f74a7d6dd6..95a83da3fad5 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -388,6 +388,8 @@ import java.util.List; */ public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable { private static final boolean DEBUG = false; + /** @hide */ + public static final boolean DEBUG_ORIGIN = false; /** * Invalid selection/focus position. @@ -748,7 +750,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par private static final int MAX_POOL_SIZE = 10; private static final SynchronizedPool<AccessibilityEvent> sPool = - new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE); + new SynchronizedPool<>(MAX_POOL_SIZE); private @EventType int mEventType; private CharSequence mPackageName; @@ -758,6 +760,17 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par int mContentChangeTypes; int mWindowChangeTypes; + /** + * The stack trace describing where this event originated from on the app side. + * Only populated if {@link #DEBUG_ORIGIN} is enabled + * Can be inspected(e.g. printed) from an + * {@link android.accessibilityservice.AccessibilityService} to trace where particular events + * are being dispatched from. + * + * @hide + */ + public StackTraceElement[] originStackTrace = null; + private ArrayList<AccessibilityRecord> mRecords; /* @@ -780,6 +793,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mWindowChangeTypes = event.mWindowChangeTypes; mEventTime = event.mEventTime; mPackageName = event.mPackageName; + if (DEBUG_ORIGIN) originStackTrace = event.originStackTrace; } /** @@ -1104,7 +1118,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par */ public static AccessibilityEvent obtain() { AccessibilityEvent event = sPool.acquire(); - return (event != null) ? event : new AccessibilityEvent(); + if (event == null) event = new AccessibilityEvent(); + if (DEBUG_ORIGIN) event.originStackTrace = Thread.currentThread().getStackTrace(); + return event; } /** @@ -1142,6 +1158,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par record.recycle(); } } + if (DEBUG_ORIGIN) originStackTrace = null; } /** @@ -1164,7 +1181,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par // Read the records. final int recordCount = parcel.readInt(); if (recordCount > 0) { - mRecords = new ArrayList<AccessibilityRecord>(recordCount); + mRecords = new ArrayList<>(recordCount); for (int i = 0; i < recordCount; i++) { AccessibilityRecord record = AccessibilityRecord.obtain(); readAccessibilityRecordFromParcel(record, parcel); @@ -1172,6 +1189,17 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par mRecords.add(record); } } + + if (DEBUG_ORIGIN) { + originStackTrace = new StackTraceElement[parcel.readInt()]; + for (int i = 0; i < originStackTrace.length; i++) { + originStackTrace[i] = new StackTraceElement( + parcel.readString(), + parcel.readString(), + parcel.readString(), + parcel.readInt()); + } + } } /** @@ -1227,6 +1255,17 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par AccessibilityRecord record = mRecords.get(i); writeAccessibilityRecordToParcel(record, parcel, flags); } + + if (DEBUG_ORIGIN) { + if (originStackTrace == null) originStackTrace = Thread.currentThread().getStackTrace(); + parcel.writeInt(originStackTrace.length); + for (StackTraceElement element : originStackTrace) { + parcel.writeString(element.getClassName()); + parcel.writeString(element.getMethodName()); + parcel.writeString(element.getFileName()); + parcel.writeInt(element.getLineNumber()); + } + } } /** diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 72af203e5fab..d60c48198e38 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -326,12 +326,14 @@ public final class AccessibilityInteractionClient accessibilityWindowId, accessibilityNodeId); if (cachedInfo != null) { if (DEBUG) { - Log.i(LOG_TAG, "Node cache hit"); + Log.i(LOG_TAG, "Node cache hit for " + + idToString(accessibilityWindowId, accessibilityNodeId)); } return cachedInfo; } if (DEBUG) { - Log.i(LOG_TAG, "Node cache miss"); + Log.i(LOG_TAG, "Node cache miss for " + + idToString(accessibilityWindowId, accessibilityNodeId)); } } final int interactionId = mInteractionIdCounter.getAndIncrement(); @@ -368,6 +370,11 @@ public final class AccessibilityInteractionClient return null; } + private static String idToString(int accessibilityWindowId, long accessibilityNodeId) { + return accessibilityWindowId + "/" + + AccessibilityNodeInfo.idToString(accessibilityNodeId); + } + /** * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in * the window whose id is specified and starts from the node whose accessibility diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index dee267dfea26..cbb23f1a6c62 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -16,6 +16,8 @@ package android.view.accessibility; +import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME; + import android.Manifest; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType; @@ -44,6 +46,7 @@ import android.util.SparseArray; import android.view.IWindow; import android.view.View; import android.view.accessibility.AccessibilityEvent.EventType; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IntPair; @@ -51,8 +54,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME; - /** * System level service that serves as an event dispatch for {@link AccessibilityEvent}s, * and provides facilities for querying the accessibility state of the system. diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 4c437dd44652..03f1c1243755 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -3874,6 +3874,24 @@ public class AccessibilityNodeInfo implements Parcelable { | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null); } + /** @hide */ + public static String idToString(long accessibilityId) { + int accessibilityViewId = getAccessibilityViewId(accessibilityId); + int virtualDescendantId = getVirtualDescendantId(accessibilityId); + return virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID + ? idItemToString(accessibilityViewId) + : idItemToString(accessibilityViewId) + ":" + idItemToString(virtualDescendantId); + } + + private static String idItemToString(int item) { + switch (item) { + case ROOT_ITEM_ID: return "ROOT"; + case UNDEFINED_ITEM_ID: return "UNDEFINED"; + case AccessibilityNodeProvider.HOST_VIEW_ID: return "HOST"; + default: return "" + item; + } + } + /** * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}. * Each action has a unique id that is mandatory and optional data. |