From 179b1e22caa19d6b056638635cdd3ccb5c9d8307 Mon Sep 17 00:00:00 2001 From: Vishnu Nair Date: Thu, 9 Sep 2021 16:25:05 -0700 Subject: [PATCH] WM: Track focus changes in AccessibilityController Forward focus changes from input and tracks them in AccessibilityController. This will help track embedded window focus changes as well and rely on input as the source of truth for focus. Accessibility service will start querying the controller for focus changes. Bug: b/188792659 Test: presubmit Test: test talkback Change-Id: I21a6f1a7353844d2b0f59f32e802ed1f0a3d66ff Merged-In: I21a6f1a7353844d2b0f59f32e802ed1f0a3d66ff --- .../server/wm/AccessibilityController.java | 26 ++++++ .../com/android/server/wm/AnrController.java | 44 ++++------- .../com/android/server/wm/DisplayContent.java | 1 + .../server/wm/EmbeddedWindowController.java | 27 ++++++- .../com/android/server/wm/InputTarget.java | 40 ++++++++++ .../server/wm/RootWindowContainer.java | 1 + .../server/wm/WindowManagerService.java | 79 ++++++++++++------- .../com/android/server/wm/WindowState.java | 20 ++++- 8 files changed, 177 insertions(+), 61 deletions(-) create mode 100644 services/core/java/com/android/server/wm/InputTarget.java diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index e59c82cfb6d03..cf9783fb92419 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -50,6 +50,7 @@ import android.accessibilityservice.AccessibilityTrace; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Application; import android.content.Context; import android.content.pm.PackageManagerInternal; @@ -134,6 +135,8 @@ final class AccessibilityController { private SparseArray mDisplayMagnifiers = new SparseArray<>(); private SparseArray mWindowsForAccessibilityObserver = new SparseArray<>(); + private SparseArray mFocusedWindow = new SparseArray<>(); + private int mFocusedDisplay = -1; // Set to true if initializing window population complete. private boolean mAllObserversInitialized = true; @@ -603,6 +606,29 @@ final class AccessibilityController { return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null; } + void onFocusChanged(InputTarget lastTarget, InputTarget newTarget) { + if (lastTarget != null) { + mFocusedWindow.remove(lastTarget.getDisplayId()); + } + if (newTarget != null) { + int displayId = newTarget.getDisplayId(); + IBinder clientBinder = newTarget.getIWindow().asBinder(); + mFocusedWindow.put(displayId, clientBinder); + } + } + + public void onDisplayRemoved(int displayId) { + mFocusedWindow.remove(displayId); + } + + public void setFocusedDisplay(int focusedDisplayId) { + mFocusedDisplay = focusedDisplayId; + } + + @Nullable IBinder getFocusedWindowToken() { + return mFocusedWindow.get(mFocusedDisplay); + } + /** * This class encapsulates the functionality related to display magnification. */ diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java index 892db9c33dbde..c881864dff254 100644 --- a/services/core/java/com/android/server/wm/AnrController.java +++ b/services/core/java/com/android/server/wm/AnrController.java @@ -31,7 +31,6 @@ import android.util.SparseArray; import android.view.InputApplicationHandle; import com.android.server.am.ActivityManagerService; -import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow; import java.io.File; import java.util.ArrayList; @@ -81,21 +80,19 @@ class AnrController { final boolean aboveSystem; final ActivityRecord activity; synchronized (mService.mGlobalLock) { - WindowState windowState = mService.mInputToWindowMap.get(inputToken); - if (windowState != null) { - pid = windowState.mSession.mPid; - activity = windowState.mActivityRecord; - Slog.i(TAG_WM, "ANR in " + windowState.mAttrs.getTitle() + ". Reason:" + reason); - } else { - EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(inputToken); - if (embeddedWindow == null) { - Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request"); - return; - } - pid = embeddedWindow.mOwnerPid; - windowState = embeddedWindow.mHostWindowState; - activity = null; // Don't blame the host process, instead blame the embedded pid. + InputTarget target = mService.getInputTargetFromToken(inputToken); + if (target == null) { + Slog.e(TAG_WM, "Unknown token, dropping notifyConnectionUnresponsive request"); + return; } + + WindowState windowState = target.getWindowState(); + pid = target.getPid(); + // Blame the activity if the input token belongs to the window. If the target is + // embedded, then we will blame the pid instead. + activity = (windowState.mInputChannelToken == inputToken) + ? windowState.mActivityRecord : null; + Slog.i(TAG_WM, "ANR in " + target + ". Reason:" + reason); aboveSystem = isWindowAboveSystem(windowState); dumpAnrStateLocked(activity, windowState, reason); } @@ -109,19 +106,12 @@ class AnrController { void notifyWindowResponsive(IBinder inputToken) { final int pid; synchronized (mService.mGlobalLock) { - WindowState windowState = mService.mInputToWindowMap.get(inputToken); - if (windowState != null) { - pid = windowState.mSession.mPid; - } else { - // Check if the token belongs to an embedded window. - EmbeddedWindow embeddedWindow = mService.mEmbeddedWindowController.get(inputToken); - if (embeddedWindow == null) { - Slog.e(TAG_WM, - "Unknown token, dropping notifyWindowConnectionResponsive request"); - return; - } - pid = embeddedWindow.mOwnerPid; + InputTarget target = mService.getInputTargetFromToken(inputToken); + if (target == null) { + Slog.e(TAG_WM, "Unknown token, dropping notifyWindowConnectionResponsive request"); + return; } + pid = target.getPid(); } mService.mAmInternal.inputDispatchingResumed(pid); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 46a4f8a266731..ff4dd46b1f189 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3099,6 +3099,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mOverlayLayer.release(); mInputMonitor.onDisplayRemoved(); mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this); + mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId); } finally { mDisplayReady = false; } diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index b08d6e1dff9e3..fc317a1212d5b 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -127,7 +127,7 @@ class EmbeddedWindowController { } } - static class EmbeddedWindow { + static class EmbeddedWindow implements InputTarget { final IWindow mClient; @Nullable final WindowState mHostWindowState; @Nullable final ActivityRecord mHostActivityRecord; @@ -166,7 +166,8 @@ class EmbeddedWindowController { mDisplayId = displayId; } - String getName() { + @Override + public String toString() { final String hostWindowName = (mHostWindowState != null) ? mHostWindowState.getWindowTag().toString() : "Internal"; return "EmbeddedWindow{ u" + UserHandle.getUserId(mOwnerUid) + " " + hostWindowName @@ -183,7 +184,7 @@ class EmbeddedWindowController { } InputChannel openInputChannel() { - final String name = getName(); + final String name = toString(); mInputChannel = mWmService.mInputManager.createInputChannel(name); return mInputChannel; } @@ -195,5 +196,25 @@ class EmbeddedWindowController { mInputChannel = null; } } + + @Override + public WindowState getWindowState() { + return mHostWindowState; + } + + @Override + public int getDisplayId() { + return mDisplayId; + } + + @Override + public IWindow getIWindow() { + return mClient; + } + + @Override + public int getPid() { + return mOwnerPid; + } } } diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java new file mode 100644 index 0000000000000..c7d328a2b18a2 --- /dev/null +++ b/services/core/java/com/android/server/wm/InputTarget.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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 com.android.server.wm; + +import android.view.IWindow; + +/** + * Common interface between focusable objects. + * + * Both WindowState and EmbeddedWindows can receive input. This consolidates some common properties + * of both targets. + */ +interface InputTarget { + /* Get the WindowState associated with the target. */ + WindowState getWindowState(); + + /* Display id of the target. */ + int getDisplayId(); + + /* Client IWindow for the target. */ + IWindow getIWindow(); + + /* Owning pid of the target. */ + int getPid(); +} + diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 97ea41c2f2288..1a881f7a62279 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -504,6 +504,7 @@ class RootWindowContainer extends WindowContainer mTopFocusedDisplayId = topFocusedDisplayId; mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId); mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId); + mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId); ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId); } return changed; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 4cf8c97eb1326..776a65db365f6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4981,23 +4981,49 @@ public class WindowManagerService extends IWindowManager.Stub return Surface.ROTATION_0; } - void reportFocusChanged(IBinder oldToken, IBinder newToken) { - WindowState lastFocus; - WindowState newFocus; - synchronized (mGlobalLock) { - lastFocus = mInputToWindowMap.get(oldToken); - newFocus = mInputToWindowMap.get(newToken); - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastFocus, newFocus); + // Returns an input target which is mapped to the given input token. This can be a WindowState + // or an embedded window. + @Nullable InputTarget getInputTargetFromToken(IBinder inputToken) { + WindowState windowState = mInputToWindowMap.get(inputToken); + if (windowState != null) { + return windowState; } - if (newFocus != null) { - mAnrController.onFocusChanged(newFocus); - newFocus.reportFocusChangedSerialized(true); + EmbeddedWindowController.EmbeddedWindow embeddedWindow = + mEmbeddedWindowController.get(inputToken); + if (embeddedWindow != null) { + return embeddedWindow; + } + + return null; + } + + void reportFocusChanged(IBinder oldToken, IBinder newToken) { + InputTarget lastTarget; + InputTarget newTarget; + synchronized (mGlobalLock) { + lastTarget = getInputTargetFromToken(oldToken); + newTarget = getInputTargetFromToken(newToken); + if (newTarget == null && lastTarget == null) { + Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged"); + return; + } + + mAccessibilityController.onFocusChanged(lastTarget, newTarget); + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget); + } + + // Call WindowState focus change observers + WindowState newFocusedWindow = newTarget != null ? newTarget.getWindowState() : null; + if (newFocusedWindow != null && newFocusedWindow.mInputChannelToken == newToken) { + mAnrController.onFocusChanged(newFocusedWindow); + newFocusedWindow.reportFocusChangedSerialized(true); notifyFocusChanged(); } - if (lastFocus != null) { - lastFocus.reportFocusChangedSerialized(false); + WindowState lastFocusedWindow = lastTarget != null ? lastTarget.getWindowState() : null; + if (lastFocusedWindow != null && lastFocusedWindow.mInputChannelToken == oldToken) { + lastFocusedWindow.reportFocusChangedSerialized(false); } } @@ -7522,11 +7548,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public IBinder getFocusedWindowToken() { synchronized (mGlobalLock) { - WindowState windowState = getFocusedWindowLocked(); - if (windowState != null) { - return windowState.mClient.asBinder(); - } - return null; + return mAccessibilityController.getFocusedWindowToken(); } } @@ -8267,7 +8289,7 @@ public class WindowManagerService extends IWindowManager.Stub clientChannel = win.openInputChannel(); mEmbeddedWindowController.add(clientChannel.getToken(), win); applicationHandle = win.getApplicationHandle(); - name = win.getName(); + name = win.toString(); } updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, @@ -8333,7 +8355,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.e(TAG, "Couldn't find window for provided channelToken."); return; } - name = win.getName(); + name = win.toString(); applicationHandle = win.getApplicationHandle(); } @@ -8542,10 +8564,9 @@ public class WindowManagerService extends IWindowManager.Stub SurfaceControl.Transaction t = mTransactionFactory.get(); final int displayId = embeddedWindow.mDisplayId; if (grantFocus) { - t.setFocusedWindow(inputToken, embeddedWindow.getName(), displayId).apply(); + t.setFocusedWindow(inputToken, embeddedWindow.toString(), displayId).apply(); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, - "Focus request " + embeddedWindow.getName(), - "reason=grantEmbeddedWindowFocus(true)"); + "Focus request " + embeddedWindow, "reason=grantEmbeddedWindowFocus(true)"); } else { // Search for a new focus target DisplayContent displayContent = mRoot.getDisplayContent(displayId); @@ -8554,18 +8575,18 @@ public class WindowManagerService extends IWindowManager.Stub if (newFocusTarget == null) { ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus remove request for " + "win=%s dropped since no candidate was found", - embeddedWindow.getName()); + embeddedWindow); return; } t.requestFocusTransfer(newFocusTarget.mInputChannelToken, newFocusTarget.getName(), - inputToken, embeddedWindow.getName(), + inputToken, embeddedWindow.toString(), displayId).apply(); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Transfer focus request " + newFocusTarget, "reason=grantEmbeddedWindowFocus(false)"); } ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", - embeddedWindow.getName(), grantFocus); + embeddedWindow, grantFocus); } } @@ -8594,24 +8615,24 @@ public class WindowManagerService extends IWindowManager.Stub } SurfaceControl.Transaction t = mTransactionFactory.get(); if (grantFocus) { - t.requestFocusTransfer(targetInputToken, embeddedWindow.getName(), + t.requestFocusTransfer(targetInputToken, embeddedWindow.toString(), hostWindow.mInputChannel.getToken(), hostWindow.getName(), hostWindow.getDisplayId()).apply(); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, - "Transfer focus request " + embeddedWindow.getName(), + "Transfer focus request " + embeddedWindow, "reason=grantEmbeddedWindowFocus(true)"); } else { t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(), targetInputToken, - embeddedWindow.getName(), + embeddedWindow.toString(), hostWindow.getDisplayId()).apply(); EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Transfer focus request " + hostWindow, "reason=grantEmbeddedWindowFocus(false)"); } ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s", - embeddedWindow.getName(), grantFocus); + embeddedWindow, grantFocus); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ad4734f0526c1..d29b2aecf7931 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -273,7 +273,7 @@ import java.util.function.Predicate; /** A window in the window manager. */ class WindowState extends WindowContainer implements WindowManagerPolicy.WindowState, - InsetsControlTarget { + InsetsControlTarget, InputTarget { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM; // The minimal size of a window within the usable area of the freeform root task. @@ -1727,7 +1727,8 @@ class WindowState extends WindowContainer implements WindowManagerP return state; } - int getDisplayId() { + @Override + public int getDisplayId() { final DisplayContent displayContent = getDisplayContent(); if (displayContent == null) { return Display.INVALID_DISPLAY; @@ -1735,6 +1736,21 @@ class WindowState extends WindowContainer implements WindowManagerP return displayContent.getDisplayId(); } + @Override + public WindowState getWindowState() { + return this; + } + + @Override + public IWindow getIWindow() { + return mClient; + } + + @Override + public int getPid() { + return mSession.mPid; + } + Task getTask() { return mActivityRecord != null ? mActivityRecord.getTask() : null; }