Ensure all events from a showing window are dispatched.

Accessibility services may opt-in to introspect the interactive
windows on the screen. If window introspection is enabled there
is a case where some events from a showing window are received
before the updated window state from the window manager. Now the
window manager sends over the windows before notifying the app
for the focus change.

bug:18625996

Change-Id: Ic481e01efbe12dc92f090f799feeb236672fc7b3
This commit is contained in:
Svetoslav
2014-12-04 12:50:11 -08:00
committed by Svetoslav Ganov
parent bcaa315d48
commit 3a0d878ab5
4 changed files with 47 additions and 68 deletions

View File

@@ -364,7 +364,7 @@ public abstract class Window {
/**
* This hook is called whenever the window focus changes. See
* {@link View#onWindowFocusChanged(boolean)
* View.onWindowFocusChanged(boolean)} for more information.
* View.onWindowFocusChangedNotLocked(boolean)} for more information.
*
* @param hasFocus Whether the window now has focus.
*/

View File

@@ -3314,8 +3314,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public int mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
public long mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
public AccessibilityEvent mShowingFocusedWindowEvent;
private boolean mTouchInteractionInProgress;
private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
@@ -3324,19 +3322,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// All events that are for changes in a global window
// state should *always* be dispatched.
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
if (mWindowsForAccessibilityCallback != null) {
// OK, this is fun. Sometimes the focused window is notified
// it has focus before being shown. Historically this event
// means that the window is focused and can be introspected.
// But we still have not gotten the window state from the
// window manager, so delay the notification until then.
AccessibilityWindowInfo window = findWindowById(event.getWindowId());
if (window == null) {
mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event);
return false;
}
}
// $fall-through$
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
case AccessibilityEvent.TYPE_ANNOUNCEMENT:
// All events generated by the user touching the
@@ -3428,18 +3413,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
notifyWindowsChanged();
// If we are delaying a window state change event as the window
// source was showing when it was fired, now is the time to send.
if (mShowingFocusedWindowEvent != null) {
final int windowId = mShowingFocusedWindowEvent.getWindowId();
AccessibilityWindowInfo window = findWindowById(windowId);
if (window != null) {
// Sending does the recycle.
sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId);
}
mShowingFocusedWindowEvent = null;
}
}
public boolean computePartialInteractiveRegionForWindowLocked(int windowId,

View File

@@ -159,11 +159,15 @@ final class AccessibilityController {
}
}
public void onWindowFocusChangedLocked() {
public void onWindowFocusChangedNotLocked() {
// Not relevant for the display magnifier.
if (mWindowsForAccessibilityObserver != null) {
mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
WindowsForAccessibilityObserver observer = null;
synchronized (mWindowManagerService) {
observer = mWindowsForAccessibilityObserver;
}
if (observer != null) {
observer.performComputeChangedWindowsNotLocked();
}
}
@@ -937,14 +941,13 @@ final class AccessibilityController {
computeChangedWindows();
}
public void performComputeChangedWindowsNotLocked() {
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows();
}
public void scheduleComputeChangedWindowsLocked() {
// If focus changed, compute changed windows immediately as the focused window
// is used by the accessibility manager service to determine the active window.
if (mWindowManagerService.mCurrentFocus != null
&& mWindowManagerService.mCurrentFocus != mWindowManagerService.mLastFocus) {
mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
computeChangedWindows();
} else if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
mRecurringAccessibilityEventsIntervalMillis);
}
@@ -955,6 +958,9 @@ final class AccessibilityController {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
boolean windowsChanged = false;
List<WindowInfo> windows = new ArrayList<WindowInfo>();
synchronized (mWindowManagerService.mWindowMap) {
// Do not send the windows if there is no current focus as
// the window manager is still looking for where to put it.
@@ -975,8 +981,6 @@ final class AccessibilityController {
SparseArray<WindowState> visibleWindows = mTempWindowStates;
populateVisibleWindowsOnScreenLocked(visibleWindows);
List<WindowInfo> windows = new ArrayList<WindowInfo>();
Set<IBinder> addedWindows = mTempBinderSet;
addedWindows.clear();
@@ -1074,7 +1078,6 @@ final class AccessibilityController {
addedWindows.clear();
// We computed the windows and if they changed notify the client.
boolean windowsChanged = false;
if (mOldWindows.size() != windows.size()) {
// Different size means something changed.
windowsChanged = true;
@@ -1096,22 +1099,24 @@ final class AccessibilityController {
}
if (windowsChanged) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows changed:" + windows);
}
// Remember the old windows to detect changes.
cacheWindows(windows);
// Announce the change.
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
windows).sendToTarget();
} else {
if (DEBUG) {
Log.i(LOG_TAG, "No windows changed.");
}
// Recycle the nodes as we do not need them.
clearAndRecycleWindows(windows);
}
}
// Now we do not hold the lock, so send the windows over.
if (windowsChanged) {
if (DEBUG) {
Log.i(LOG_TAG, "Windows changed:" + windows);
}
mCallback.onWindowsForAccessibilityChanged(windows);
} else {
if (DEBUG) {
Log.i(LOG_TAG, "No windows changed.");
}
}
// Recycle the windows as we do not need them.
clearAndRecycleWindows(windows);
}
private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
@@ -1217,7 +1222,7 @@ final class AccessibilityController {
return false;
}
private void clearAndRecycleWindows(List<WindowInfo> windows) {
private static void clearAndRecycleWindows(List<WindowInfo> windows) {
final int windowCount = windows.size();
for (int i = windowCount - 1; i >= 0; i--) {
windows.remove(i).recycle();
@@ -1254,7 +1259,6 @@ final class AccessibilityController {
private class MyHandler extends Handler {
public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 2;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -1267,12 +1271,6 @@ final class AccessibilityController {
case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
computeChangedWindows();
} break;
case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
List<WindowInfo> windows = (List<WindowInfo>) message.obj;
mCallback.onWindowsForAccessibilityChanged(windows);
clearAndRecycleWindows(windows);
} break;
}
}
}

View File

@@ -7610,7 +7610,15 @@ public class WindowManagerService extends IWindowManager.Stub
WindowState lastFocus;
WindowState newFocus;
AccessibilityController accessibilityController = null;
synchronized(mWindowMap) {
// TODO(multidisplay): Accessibility supported only of default desiplay.
if (mAccessibilityController != null && getDefaultDisplayContentLocked()
.getDisplayId() == Display.DEFAULT_DISPLAY) {
accessibilityController = mAccessibilityController;
}
lastFocus = mLastFocus;
newFocus = mCurrentFocus;
if (lastFocus == newFocus) {
@@ -7628,6 +7636,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
// First notify the accessibility manager for the change so it has
// the windows before the newly focused one starts firing eventgs.
if (accessibilityController != null) {
accessibilityController.onWindowFocusChangedNotLocked();
}
//System.out.println("Changing focus from " + lastFocus
// + " to " + newFocus);
if (newFocus != null) {
@@ -10402,12 +10416,6 @@ public class WindowManagerService extends IWindowManager.Stub
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
// TODO(multidisplay): Accessibilty supported only of default desiplay.
if (mAccessibilityController != null
&& displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
mAccessibilityController.onWindowFocusChangedLocked();
}
int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
if (imWindowChanged && oldFocus != mInputMethodWindow) {