summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Phil Weaver <pweaver@google.com> 2017-12-22 09:44:28 -0800
committer Phil Weaver <pweaver@google.com> 2018-01-03 08:51:18 -0800
commitbb2f28a776cff0af0fea0130992a3537a43c57ed (patch)
treeeb8213b6f7fd5929ab415c0ba320d2e8f6277485
parentc80114c855c0f8055940ffb6b4a7b867d5742a40 (diff)
Make accessibility window events more granular
TYPE_WINDOWS_CHANGED events have meant that "something" changes with the system windows, so a single event would be fired for a single small window change or a bunch of window changes. Services were required to cache the windows and compare the old ones to the new ones to find out what changed. Since AccessibilityEvents are intended to communicate changes to the UI, this CL sends one TYPE_WINDOWS_CHANGED for each window that has changed, and provides a way to get the source of the changing window. It also adds a windowChangeType field, which contains flags to indicate what exactly has changed for the window. Bug: 62231686 Test: Run all a11y unit and cts tests Change-Id: I6c48e74da26be5ea485ac114a37f3c404a74940e
-rw-r--r--api/current.txt12
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java165
-rw-r--r--core/java/android/view/accessibility/AccessibilityWindowInfo.java59
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java68
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java154
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java8
6 files changed, 412 insertions, 54 deletions
diff --git a/api/current.txt b/api/current.txt
index 892584661084..232214b0f054 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -47776,6 +47776,7 @@ package android.view.accessibility {
method public java.lang.CharSequence getPackageName();
method public android.view.accessibility.AccessibilityRecord getRecord(int);
method public int getRecordCount();
+ method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
method public static android.view.accessibility.AccessibilityEvent obtain(int);
method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
@@ -47820,6 +47821,17 @@ package android.view.accessibility {
field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
+ field public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 128; // 0x80
+ field public static final int WINDOWS_CHANGE_ACTIVE = 32; // 0x20
+ field public static final int WINDOWS_CHANGE_ADDED = 1; // 0x1
+ field public static final int WINDOWS_CHANGE_BOUNDS = 8; // 0x8
+ field public static final int WINDOWS_CHANGE_CHILDREN = 512; // 0x200
+ field public static final int WINDOWS_CHANGE_FOCUSED = 64; // 0x40
+ field public static final int WINDOWS_CHANGE_LAYER = 16; // 0x10
+ field public static final int WINDOWS_CHANGE_PARENT = 256; // 0x100
+ field public static final int WINDOWS_CHANGE_PIP = 1024; // 0x400
+ field public static final int WINDOWS_CHANGE_REMOVED = 2; // 0x2
+ field public static final int WINDOWS_CHANGE_TITLE = 4; // 0x4
}
public abstract interface AccessibilityEventSource {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 1d19a9f5969a..98715395f976 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -350,21 +350,22 @@ import java.util.List;
* view.</br>
* </p>
* <p>
- * <b>Windows changed</b> - represents the event of changes in the windows shown on
+ * <b>Windows changed</b> - represents a change in the windows shown on
* the screen such as a window appeared, a window disappeared, a window size changed,
- * a window layer changed, etc.</br>
+ * a window layer changed, etc. These events should only come from the system, which is responsible
+ * for managing windows. For regions of the user interface that are presented as windows but are
+ * controlled by an app's process, use {@link #TYPE_WINDOW_STATE_CHANGED}.</br>
* <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br>
* <em>Properties:</em></br>
* <ul>
* <li>{@link #getEventType()} - The type of the event.</li>
* <li>{@link #getEventTime()} - The event time.</li>
+ * <li>{@link #getWindowChanges()}</li> - The specific change to the source window
* </ul>
* <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window
- * source of the event via {@link AccessibilityEvent#getSource()} to get the source
- * node on which then call {@link AccessibilityNodeInfo#getWindow()
- * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can
- * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows()
- * android.accessibilityservice.AccessibilityService.getWindows()}.
+ * source of the event by looking through the list returned by
+ * {@link android.accessibilityservice.AccessibilityService#getWindows()} for the window whose ID
+ * matches {@link #getWindowId()}.
* </p>
* <p>
* <b>NOTIFICATION TYPES</b></br>
@@ -712,6 +713,88 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
*/
public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window was added.
+ */
+ public static final int WINDOWS_CHANGE_ADDED = 0x00000001;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * A window was removed.
+ */
+ public static final int WINDOWS_CHANGE_REMOVED = 0x00000002;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's title changed.
+ */
+ public static final int WINDOWS_CHANGE_TITLE = 0x00000004;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's bounds changed.
+ */
+ public static final int WINDOWS_CHANGE_BOUNDS = 0x00000008;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's layer changed.
+ */
+ public static final int WINDOWS_CHANGE_LAYER = 0x00000010;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's {@link AccessibilityWindowInfo#isActive()} changed.
+ */
+ public static final int WINDOWS_CHANGE_ACTIVE = 0x00000020;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's {@link AccessibilityWindowInfo#isFocused()} changed.
+ */
+ public static final int WINDOWS_CHANGE_FOCUSED = 0x00000040;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's {@link AccessibilityWindowInfo#isAccessibilityFocused()} changed.
+ */
+ public static final int WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED = 0x00000080;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's parent changed.
+ */
+ public static final int WINDOWS_CHANGE_PARENT = 0x00000100;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window's children changed.
+ */
+ public static final int WINDOWS_CHANGE_CHILDREN = 0x00000200;
+
+ /**
+ * Change type for {@link #TYPE_WINDOWS_CHANGED} event:
+ * The window either entered or exited picture-in-picture mode.
+ */
+ public static final int WINDOWS_CHANGE_PIP = 0x00000400;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "WINDOWS_CHANGE_" }, value = {
+ WINDOWS_CHANGE_ADDED,
+ WINDOWS_CHANGE_REMOVED,
+ WINDOWS_CHANGE_TITLE,
+ WINDOWS_CHANGE_BOUNDS,
+ WINDOWS_CHANGE_LAYER,
+ WINDOWS_CHANGE_ACTIVE,
+ WINDOWS_CHANGE_FOCUSED,
+ WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED,
+ WINDOWS_CHANGE_PARENT,
+ WINDOWS_CHANGE_CHILDREN,
+ WINDOWS_CHANGE_PIP
+ })
+ public @interface WindowsChangeTypes {}
/** @hide */
@IntDef(flag = true, prefix = { "TYPE_" }, value = {
@@ -782,6 +865,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
int mMovementGranularity;
int mAction;
int mContentChangeTypes;
+ int mWindowChangeTypes;
private ArrayList<AccessibilityRecord> mRecords;
@@ -802,6 +886,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mMovementGranularity = event.mMovementGranularity;
mAction = event.mAction;
mContentChangeTypes = event.mContentChangeTypes;
+ mWindowChangeTypes = event.mWindowChangeTypes;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
}
@@ -919,6 +1004,43 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
+ * Get the bit mask of change types signaled by a {@link #TYPE_WINDOWS_CHANGED} event. A
+ * single event may represent multiple change types.
+ *
+ * @return The bit mask of change types.
+ */
+ @WindowsChangeTypes
+ public int getWindowChanges() {
+ return mWindowChangeTypes;
+ }
+
+ /** @hide */
+ public void setWindowChanges(@WindowsChangeTypes int changes) {
+ mWindowChangeTypes = changes;
+ }
+
+ private static String windowChangeTypesToString(@WindowsChangeTypes int types) {
+ return BitUtils.flagsToString(types, AccessibilityEvent::singleWindowChangeTypeToString);
+ }
+
+ private static String singleWindowChangeTypeToString(int type) {
+ switch (type) {
+ case WINDOWS_CHANGE_ADDED: return "WINDOWS_CHANGE_ADDED";
+ case WINDOWS_CHANGE_REMOVED: return "WINDOWS_CHANGE_REMOVED";
+ case WINDOWS_CHANGE_TITLE: return "WINDOWS_CHANGE_TITLE";
+ case WINDOWS_CHANGE_BOUNDS: return "WINDOWS_CHANGE_BOUNDS";
+ case WINDOWS_CHANGE_LAYER: return "WINDOWS_CHANGE_LAYER";
+ case WINDOWS_CHANGE_ACTIVE: return "WINDOWS_CHANGE_ACTIVE";
+ case WINDOWS_CHANGE_FOCUSED: return "WINDOWS_CHANGE_FOCUSED";
+ case WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED:
+ return "WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED";
+ case WINDOWS_CHANGE_PARENT: return "WINDOWS_CHANGE_PARENT";
+ case WINDOWS_CHANGE_CHILDREN: return "WINDOWS_CHANGE_CHILDREN";
+ default: return Integer.toHexString(type);
+ }
+ }
+
+ /**
* Sets the event type.
*
* @param eventType The event type.
@@ -1025,6 +1147,26 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
}
/**
+ * Convenience method to obtain a {@link #TYPE_WINDOWS_CHANGED} event for a specific window and
+ * change set.
+ *
+ * @param windowId The ID of the window that changed
+ * @param windowChangeTypes The changes to populate
+ * @return An instance of a TYPE_WINDOWS_CHANGED, populated with the requested fields and with
+ * importantForAccessibility set to {@code true}.
+ *
+ * @hide
+ */
+ public static AccessibilityEvent obtainWindowsChangedEvent(
+ int windowId, int windowChangeTypes) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain(TYPE_WINDOWS_CHANGED);
+ event.setWindowId(windowId);
+ event.setWindowChanges(windowChangeTypes);
+ event.setImportantForAccessibility(true);
+ return event;
+ }
+
+ /**
* Returns a cached instance if such is available or a new one is
* instantiated with its type property set.
*
@@ -1099,6 +1241,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mMovementGranularity = 0;
mAction = 0;
mContentChangeTypes = 0;
+ mWindowChangeTypes = 0;
mPackageName = null;
mEventTime = 0;
if (mRecords != null) {
@@ -1120,6 +1263,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
mMovementGranularity = parcel.readInt();
mAction = parcel.readInt();
mContentChangeTypes = parcel.readInt();
+ mWindowChangeTypes = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
mConnectionId = parcel.readInt();
@@ -1178,6 +1322,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
parcel.writeInt(mMovementGranularity);
parcel.writeInt(mAction);
parcel.writeInt(mContentChangeTypes);
+ parcel.writeInt(mWindowChangeTypes);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
parcel.writeInt(mConnectionId);
@@ -1238,11 +1383,13 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
builder.append("; PackageName: ").append(mPackageName);
builder.append("; MovementGranularity: ").append(mMovementGranularity);
builder.append("; Action: ").append(mAction);
+ builder.append("; ContentChangeTypes: ").append(
+ contentChangeTypesToString(mContentChangeTypes));
+ builder.append("; WindowChangeTypes: ").append(
+ windowChangeTypesToString(mWindowChangeTypes));
builder.append(super.toString());
if (DEBUG) {
builder.append("\n");
- builder.append("; ContentChangeTypes: ").append(
- contentChangeTypesToString(mContentChangeTypes));
builder.append("; sourceWindowId: ").append(mSourceWindowId);
builder.append("; mSourceNodeId: ").append(mSourceNodeId);
for (int i = 0; i < getRecordCount(); i++) {
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index ef1a3f3bcc8f..c1c9174c0f9f 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -21,9 +21,12 @@ import android.annotation.TestApi;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.LongArray;
import android.util.Pools.SynchronizedPool;
+import android.view.accessibility.AccessibilityEvent.WindowsChangeTypes;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -575,7 +578,7 @@ public final class AccessibilityWindowInfo implements Parcelable {
StringBuilder builder = new StringBuilder();
builder.append("AccessibilityWindowInfo[");
builder.append("title=").append(mTitle);
- builder.append("id=").append(mId);
+ builder.append(", id=").append(mId);
builder.append(", type=").append(typeToString(mType));
builder.append(", layer=").append(mLayer);
builder.append(", bounds=").append(mBoundsInScreen);
@@ -713,6 +716,60 @@ public final class AccessibilityWindowInfo implements Parcelable {
return false;
}
+ /**
+ * Reports how this window differs from a possibly different state of the same window. The
+ * argument must have the same id and type as neither of those properties may change.
+ *
+ * @param other The new state.
+ * @return A set of flags showing how the window has changes, or 0 if the two states are the
+ * same.
+ *
+ * @hide
+ */
+ @WindowsChangeTypes
+ public int differenceFrom(AccessibilityWindowInfo other) {
+ if (other.mId != mId) {
+ throw new IllegalArgumentException("Not same window.");
+ }
+ if (other.mType != mType) {
+ throw new IllegalArgumentException("Not same type.");
+ }
+ int changes = 0;
+ if (!TextUtils.equals(mTitle, other.mTitle)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE;
+ }
+
+ if (!mBoundsInScreen.equals(other.mBoundsInScreen)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS;
+ }
+ if (mLayer != other.mLayer) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_LAYER;
+ }
+ if (getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)
+ != other.getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_ACTIVE;
+ }
+ if (getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)
+ != other.getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
+ }
+ if (getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)
+ != other.getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
+ }
+ if (getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)
+ != other.getBooleanProperty(BOOLEAN_PROPERTY_PICTURE_IN_PICTURE)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_PIP;
+ }
+ if (mParentId != other.mParentId) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_PARENT;
+ }
+ if (!Objects.equals(mChildIds, other.mChildIds)) {
+ changes |= AccessibilityEvent.WINDOWS_CHANGE_CHILDREN;
+ }
+ return changes;
+ }
+
public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
new Creator<AccessibilityWindowInfo>() {
@Override
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
new file mode 100644
index 000000000000..fed197ed2505
--- /dev/null
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 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 android.view.accessibility;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * AccessibilityEvent is public, so CTS covers it pretty well. Verifying hidden methods here.
+ */
+@RunWith(AndroidJUnit4.class)
+public class AccessibilityEventTest {
+ @Test
+ public void testImportantForAccessibiity_getSetWorkAcrossParceling() {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setImportantForAccessibility(true);
+ assertTrue(copyEventViaParcel(event).isImportantForAccessibility());
+
+ event.setImportantForAccessibility(false);
+ assertFalse(copyEventViaParcel(event).isImportantForAccessibility());
+ }
+
+ @Test
+ public void testSouceNodeId_getSetWorkAcrossParceling() {
+ final long sourceNodeId = 0x1234567890ABCDEFL;
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setSourceNodeId(sourceNodeId);
+ assertEquals(sourceNodeId, copyEventViaParcel(event).getSourceNodeId());
+ }
+
+ @Test
+ public void testWindowChanges_getSetWorkAcrossParceling() {
+ final int windowChanges = AccessibilityEvent.WINDOWS_CHANGE_TITLE
+ | AccessibilityEvent.WINDOWS_CHANGE_ACTIVE
+ | AccessibilityEvent.WINDOWS_CHANGE_FOCUSED;
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setWindowChanges(windowChanges);
+ assertEquals(windowChanges, copyEventViaParcel(event).getWindowChanges());
+ }
+
+ private AccessibilityEvent copyEventViaParcel(AccessibilityEvent event) {
+ Parcel parcel = Parcel.obtain();
+ event.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return AccessibilityEvent.CREATOR.createFromParcel(parcel);
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 50b0be1a11d2..ba8ce59803f7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
@@ -762,7 +763,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mPictureInPictureActionReplacingConnection = wrapper;
wrapper.linkToDeath();
}
- mSecurityPolicy.notifyWindowsChanged();
}
}
@@ -2283,6 +2283,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ private void sendAccessibilityEventLocked(AccessibilityEvent event, int userId) {
+ // Resync to avoid calling out with the lock held
+ event.setEventTime(SystemClock.uptimeMillis());
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SEND_ACCESSIBILITY_EVENT, userId, 0 /* unused */, event)
+ .sendToTarget();
+ }
+
/**
* AIDL-exposed method. System only.
* Inform accessibility that a fingerprint gesture was performed
@@ -2419,6 +2427,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14;
public static final int MSG_INIT_SERVICE = 15;
+ public static final int MSG_SEND_ACCESSIBILITY_EVENT = 16;
public MainHandler(Looper looper) {
super(looper);
@@ -2519,6 +2528,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
(AccessibilityServiceConnection) msg.obj;
service.initializeService();
} break;
+
+ case MSG_SEND_ACCESSIBILITY_EVENT: {
+ final AccessibilityEvent event = (AccessibilityEvent) msg.obj;
+ final int userId = msg.arg1;
+ sendAccessibilityEvent(event, userId);
+ }
}
}
@@ -2533,7 +2548,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.getText().add(message);
- sendAccessibilityEvent(event, mCurrentUserId);
+ sendAccessibilityEventLocked(event, mCurrentUserId);
}
}
}
@@ -2961,21 +2976,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public class SecurityPolicy {
public static final int INVALID_WINDOW_ID = -1;
- private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
- AccessibilityEvent.TYPE_VIEW_CLICKED
- | AccessibilityEvent.TYPE_VIEW_FOCUSED
- | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
- | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
- | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
- | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
- | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
- | AccessibilityEvent.TYPE_VIEW_SELECTED
- | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
- | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
- | AccessibilityEvent.TYPE_VIEW_SCROLLED
- | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
- | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
- | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
+ private static final int KEEP_SOURCE_EVENT_TYPES = AccessibilityEvent.TYPE_VIEW_CLICKED
+ | AccessibilityEvent.TYPE_VIEW_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER
+ | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+ | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+ | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+ | AccessibilityEvent.TYPE_WINDOWS_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_SELECTED
+ | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
+ | AccessibilityEvent.TYPE_VIEW_SCROLLED
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
+ | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+ | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
// In Z order
public List<AccessibilityWindowInfo> mWindows;
@@ -3137,10 +3152,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mWindows = new ArrayList<>();
}
- final int oldWindowCount = mWindows.size();
- for (int i = oldWindowCount - 1; i >= 0; i--) {
- mWindows.remove(i).recycle();
- }
+ List<AccessibilityWindowInfo> oldWindowList = new ArrayList<>(mWindows);
+ SparseArray<AccessibilityWindowInfo> oldWindowsById = mA11yWindowInfoById.clone();
+
+ mWindows.clear();
mA11yWindowInfoById.clear();
for (int i = 0; i < mWindowInfoById.size(); i++) {
@@ -3202,7 +3217,49 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
- notifyWindowsChanged();
+ sendEventsForChangedWindowsLocked(oldWindowList, oldWindowsById);
+
+ final int oldWindowCount = oldWindowList.size();
+ for (int i = oldWindowCount - 1; i >= 0; i--) {
+ oldWindowList.remove(i).recycle();
+ }
+ }
+
+ private void sendEventsForChangedWindowsLocked(List<AccessibilityWindowInfo> oldWindows,
+ SparseArray<AccessibilityWindowInfo> oldWindowsById) {
+ List<AccessibilityEvent> events = new ArrayList<>();
+ // Send events for all removed windows
+ final int oldWindowsCount = oldWindows.size();
+ for (int i = 0; i < oldWindowsCount; i++) {
+ final AccessibilityWindowInfo window = oldWindows.get(i);
+ if (mA11yWindowInfoById.get(window.getId()) == null) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ window.getId(), AccessibilityEvent.WINDOWS_CHANGE_REMOVED));
+ }
+ }
+
+ // Look for other changes
+ int oldWindowIndex = 0;
+ final int newWindowCount = mWindows.size();
+ for (int i = 0; i < newWindowCount; i++) {
+ final AccessibilityWindowInfo newWindow = mWindows.get(i);
+ final AccessibilityWindowInfo oldWindow = oldWindowsById.get(newWindow.getId());
+ if (oldWindow == null) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ newWindow.getId(), AccessibilityEvent.WINDOWS_CHANGE_ADDED));
+ } else {
+ int changes = newWindow.differenceFrom(oldWindow);
+ if (changes != 0) {
+ events.add(AccessibilityEvent.obtainWindowsChangedEvent(
+ newWindow.getId(), changes));
+ }
+ }
+ }
+
+ final int numEvents = events.size();
+ for (int i = 0; i < numEvents; i++) {
+ sendAccessibilityEventLocked(events.get(i), mCurrentUserId);
+ }
}
public boolean computePartialInteractiveRegionForWindowLocked(int windowId,
@@ -3243,7 +3300,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
public void updateEventSourceLocked(AccessibilityEvent event) {
- if ((event.getEventType() & RETRIEVAL_ALLOWING_EVENT_TYPES) == 0) {
+ if ((event.getEventType() & KEEP_SOURCE_EVENT_TYPES) == 0) {
event.setSource((View) null);
}
}
@@ -3357,46 +3414,55 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private void setActiveWindowLocked(int windowId) {
if (mActiveWindowId != windowId) {
+ sendAccessibilityEventLocked(
+ AccessibilityEvent.obtainWindowsChangedEvent(
+ mActiveWindowId, AccessibilityEvent.WINDOWS_CHANGE_ACTIVE),
+ mCurrentUserId);
+
mActiveWindowId = windowId;
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
- window.setActive(window.getId() == windowId);
+ if (window.getId() == windowId) {
+ window.setActive(true);
+ sendAccessibilityEventLocked(
+ AccessibilityEvent.obtainWindowsChangedEvent(windowId,
+ AccessibilityEvent.WINDOWS_CHANGE_ACTIVE),
+ mCurrentUserId);
+ } else {
+ window.setActive(false);
+ }
}
}
- notifyWindowsChanged();
}
}
private void setAccessibilityFocusedWindowLocked(int windowId) {
if (mAccessibilityFocusedWindowId != windowId) {
+ sendAccessibilityEventLocked(
+ AccessibilityEvent.obtainWindowsChangedEvent(
+ mAccessibilityFocusedWindowId,
+ WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED),
+ mCurrentUserId);
+
mAccessibilityFocusedWindowId = windowId;
if (mWindows != null) {
final int windowCount = mWindows.size();
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
- window.setAccessibilityFocused(window.getId() == windowId);
+ if (window.getId() == windowId) {
+ window.setAccessibilityFocused(true);
+ sendAccessibilityEventLocked(
+ AccessibilityEvent.obtainWindowsChangedEvent(
+ windowId, WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED),
+ mCurrentUserId);
+
+ } else {
+ window.setAccessibilityFocused(false);
+ }
}
}
-
- notifyWindowsChanged();
- }
- }
-
- public void notifyWindowsChanged() {
- if (mWindowsForAccessibilityCallback == null) {
- return;
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- // Let the client know the windows changed.
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEvent.TYPE_WINDOWS_CHANGED);
- event.setEventTime(SystemClock.uptimeMillis());
- sendAccessibilityEvent(event, mCurrentUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 863922f60aae..705dc5e0abc6 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -142,6 +142,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.util.Slog;
@@ -3676,6 +3677,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
windowInfo.activityToken = mAppToken.appToken.asBinder();
}
windowInfo.title = mAttrs.accessibilityTitle;
+ // Panel windows have no public way to set the a11y title directly. Use the
+ // regular title as a fallback.
+ if (TextUtils.isEmpty(windowInfo.title)
+ && (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
+ && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)) {
+ windowInfo.title = mAttrs.getTitle();
+ }
windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
windowInfo.focused = isFocused();
Task task = getTask();