Loading core/java/android/view/IWindow.aidl +26 −3 Original line number Diff line number Diff line Loading @@ -58,10 +58,33 @@ oneway interface IWindow { void dispatchGetNewSurface(); /** * Tell the window that it is either gaining or losing focus. Keep it up * to date on the current state showing navigational focus (touch mode) too. * Tell the window that it is either gaining or losing focus. * * @param hasFocus {@code true} if window has focus, {@code false} otherwise. * @param inTouchMode {@code true} if screen is in touch mode, {@code false} otherwise. * @param reportToClient {@code true} when need to report to child view with * {@link View#onWindowFocusChanged(boolean)}, {@code false} otherwise. * <p> * Note: In the previous design, there is only one window focus state tracked by * WindowManagerService. * For multi-display, the window focus state is tracked by each display independently. * <p> * It will introduce a problem if the window was already focused on one display and then * switched to another display, since the window focus state on each display is independent, * there is no global window focus state in WindowManagerService, so the window focus state of * the former display remains unchanged. * <p> * When switched back to former display, some flows that rely on the global window focus state * in view root will be missed due to the window focus state remaining unchanged. * (i.e: Showing single IME window when switching between displays.) * <p> * To solve the problem, WindowManagerService tracks the top focused display change and then * callbacks to the client via this method to make sure that the client side will request the * IME on the top focused display, and then set {@param reportToClient} as {@code false} to * ignore reporting to the application, since its focus remains unchanged on its display. * */ void windowFocusChanged(boolean hasFocus, boolean inTouchMode); void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient); void closeSystemDialogs(String reason); Loading core/java/android/view/ViewRootImpl.java +24 −10 Original line number Diff line number Diff line Loading @@ -2695,7 +2695,7 @@ public final class ViewRootImpl implements ViewParent, } } private void handleWindowFocusChanged() { private void handleWindowFocusChanged(boolean reportToClient) { final boolean hasWindowFocus; final boolean inTouchMode; synchronized (this) { Loading Loading @@ -2730,8 +2730,9 @@ public final class ViewRootImpl implements ViewParent, } catch (RemoteException ex) { } // Retry in a bit. mHandler.sendMessageDelayed(mHandler.obtainMessage( MSG_WINDOW_FOCUS_CHANGED), 500); final Message msg = mHandler.obtainMessage(MSG_WINDOW_FOCUS_CHANGED); msg.arg1 = reportToClient ? 1 : 0; mHandler.sendMessageDelayed(msg, 500); return; } } Loading @@ -2748,9 +2749,15 @@ public final class ViewRootImpl implements ViewParent, } if (mView != null) { mAttachInfo.mKeyDispatchState.reset(); // We dispatch onWindowFocusChanged to child view only when window is gaining / // losing focus. // If the focus is updated from top display change but window focus on the display // remains unchanged, will not callback onWindowFocusChanged again since it may // be redundant & can affect the state when it callbacks. if (reportToClient) { mView.dispatchWindowFocusChanged(hasWindowFocus); mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); } if (mAttachInfo.mTooltipHost != null) { mAttachInfo.mTooltipHost.hideTooltip(); } Loading Loading @@ -4340,7 +4347,7 @@ public final class ViewRootImpl implements ViewParent, } break; case MSG_WINDOW_FOCUS_CHANGED: { handleWindowFocusChanged(); handleWindowFocusChanged(msg.arg1 != 0 /* reportToClient */); } break; case MSG_DIE: doDie(); Loading Loading @@ -7263,7 +7270,7 @@ public final class ViewRootImpl implements ViewParent, } if (stage != null) { handleWindowFocusChanged(); handleWindowFocusChanged(true /* reportToClient */); stage.deliver(q); } else { finishInputEvent(q); Loading Loading @@ -7580,6 +7587,11 @@ public final class ViewRootImpl implements ViewParent, } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { windowFocusChanged(hasFocus, inTouchMode, true /* reportToClient */); } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient) { synchronized (this) { mWindowFocusChanged = true; mUpcomingWindowFocus = hasFocus; Loading @@ -7587,6 +7599,7 @@ public final class ViewRootImpl implements ViewParent, } Message msg = Message.obtain(); msg.what = MSG_WINDOW_FOCUS_CHANGED; msg.arg1 = reportToClient ? 1 : 0; mHandler.sendMessage(msg); } Loading Loading @@ -8131,10 +8144,11 @@ public final class ViewRootImpl implements ViewParent, } @Override public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.windowFocusChanged(hasFocus, inTouchMode); viewAncestor.windowFocusChanged(hasFocus, inTouchMode, reportToClient); } } Loading core/java/com/android/internal/view/BaseIWindow.java +1 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.internal.view; import android.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.Bundle; Loading Loading @@ -66,7 +65,7 @@ public class BaseIWindow extends IWindow.Stub { } @Override public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { public void windowFocusChanged(boolean hasFocus, boolean touchEnabled, boolean reportToClient) { } @Override Loading services/core/java/com/android/server/wm/DisplayContent.java +0 −2 Original line number Diff line number Diff line Loading @@ -100,7 +100,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION; import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS; import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION; import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER; Loading Loading @@ -2809,7 +2808,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mCurrentFocus == newFocus) { return false; } mService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget(); boolean imWindowChanged = false; // TODO (b/111080190): Multi-Session IME if (!focusFound) { Loading services/core/java/com/android/server/wm/RootWindowContainer.java +29 −5 Original line number Diff line number Diff line Loading @@ -122,7 +122,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { // The ID of the display which is responsible for receiving display-unspecified key and pointer // events. private int mTopFocusedDisplayId = INVALID_DISPLAY; int mTopFocusedDisplayId = INVALID_DISPLAY; // Only a seperate transaction until we seperate the apply surface changes // transaction from the global transaction. Loading Loading @@ -156,7 +156,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { boolean changed = false; int topFocusedDisplayId = INVALID_DISPLAY; for (int i = mChildren.size() - 1; i >= 0; i--) { for (int i = mChildren.size() - 1; i >= 0; --i) { final DisplayContent dc = mChildren.get(i); changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId != INVALID_DISPLAY /* focusFound */); Loading @@ -167,12 +168,35 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (topFocusedDisplayId == INVALID_DISPLAY) { topFocusedDisplayId = DEFAULT_DISPLAY; } // TODO(b/118865114): Review if need callback top focus display change to view component. // (i.e. Activity or View) // Currently we only tracked topFocusedDisplayChanged for notifying InputMethodManager via // ViewRootImpl.windowFocusChanged to refocus IME window when top display focus changed // but window focus remain the same case. // It may need to review if any use case that need to add new callback for reporting // this change. final boolean topFocusedDisplayChanged = mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL; if (mTopFocusedDisplayId != topFocusedDisplayId) { mTopFocusedDisplayId = topFocusedDisplayId; mService.mInputManager.setFocusedDisplay(topFocusedDisplayId); mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId); if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId=" + topFocusedDisplayId); + mTopFocusedDisplayId); } // Report window focus or top display focus changed through REPORT_FOCUS_CHANGE. forAllDisplays((dc) -> { final boolean windowFocusChanged = dc.mCurrentFocus != null && dc.mCurrentFocus != dc.mLastFocus; final boolean isTopFocusedDisplay = topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId; if (windowFocusChanged || isTopFocusedDisplay) { final Message msg = mService.mH.obtainMessage( WindowManagerService.H.REPORT_FOCUS_CHANGE, dc); msg.arg1 = topFocusedDisplayChanged ? 1 : 0; mService.mH.sendMessage(msg); } }); final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus; mService.mInputManager.setFocusedWindow( topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null); Loading Loading
core/java/android/view/IWindow.aidl +26 −3 Original line number Diff line number Diff line Loading @@ -58,10 +58,33 @@ oneway interface IWindow { void dispatchGetNewSurface(); /** * Tell the window that it is either gaining or losing focus. Keep it up * to date on the current state showing navigational focus (touch mode) too. * Tell the window that it is either gaining or losing focus. * * @param hasFocus {@code true} if window has focus, {@code false} otherwise. * @param inTouchMode {@code true} if screen is in touch mode, {@code false} otherwise. * @param reportToClient {@code true} when need to report to child view with * {@link View#onWindowFocusChanged(boolean)}, {@code false} otherwise. * <p> * Note: In the previous design, there is only one window focus state tracked by * WindowManagerService. * For multi-display, the window focus state is tracked by each display independently. * <p> * It will introduce a problem if the window was already focused on one display and then * switched to another display, since the window focus state on each display is independent, * there is no global window focus state in WindowManagerService, so the window focus state of * the former display remains unchanged. * <p> * When switched back to former display, some flows that rely on the global window focus state * in view root will be missed due to the window focus state remaining unchanged. * (i.e: Showing single IME window when switching between displays.) * <p> * To solve the problem, WindowManagerService tracks the top focused display change and then * callbacks to the client via this method to make sure that the client side will request the * IME on the top focused display, and then set {@param reportToClient} as {@code false} to * ignore reporting to the application, since its focus remains unchanged on its display. * */ void windowFocusChanged(boolean hasFocus, boolean inTouchMode); void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient); void closeSystemDialogs(String reason); Loading
core/java/android/view/ViewRootImpl.java +24 −10 Original line number Diff line number Diff line Loading @@ -2695,7 +2695,7 @@ public final class ViewRootImpl implements ViewParent, } } private void handleWindowFocusChanged() { private void handleWindowFocusChanged(boolean reportToClient) { final boolean hasWindowFocus; final boolean inTouchMode; synchronized (this) { Loading Loading @@ -2730,8 +2730,9 @@ public final class ViewRootImpl implements ViewParent, } catch (RemoteException ex) { } // Retry in a bit. mHandler.sendMessageDelayed(mHandler.obtainMessage( MSG_WINDOW_FOCUS_CHANGED), 500); final Message msg = mHandler.obtainMessage(MSG_WINDOW_FOCUS_CHANGED); msg.arg1 = reportToClient ? 1 : 0; mHandler.sendMessageDelayed(msg, 500); return; } } Loading @@ -2748,9 +2749,15 @@ public final class ViewRootImpl implements ViewParent, } if (mView != null) { mAttachInfo.mKeyDispatchState.reset(); // We dispatch onWindowFocusChanged to child view only when window is gaining / // losing focus. // If the focus is updated from top display change but window focus on the display // remains unchanged, will not callback onWindowFocusChanged again since it may // be redundant & can affect the state when it callbacks. if (reportToClient) { mView.dispatchWindowFocusChanged(hasWindowFocus); mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus); } if (mAttachInfo.mTooltipHost != null) { mAttachInfo.mTooltipHost.hideTooltip(); } Loading Loading @@ -4340,7 +4347,7 @@ public final class ViewRootImpl implements ViewParent, } break; case MSG_WINDOW_FOCUS_CHANGED: { handleWindowFocusChanged(); handleWindowFocusChanged(msg.arg1 != 0 /* reportToClient */); } break; case MSG_DIE: doDie(); Loading Loading @@ -7263,7 +7270,7 @@ public final class ViewRootImpl implements ViewParent, } if (stage != null) { handleWindowFocusChanged(); handleWindowFocusChanged(true /* reportToClient */); stage.deliver(q); } else { finishInputEvent(q); Loading Loading @@ -7580,6 +7587,11 @@ public final class ViewRootImpl implements ViewParent, } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { windowFocusChanged(hasFocus, inTouchMode, true /* reportToClient */); } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient) { synchronized (this) { mWindowFocusChanged = true; mUpcomingWindowFocus = hasFocus; Loading @@ -7587,6 +7599,7 @@ public final class ViewRootImpl implements ViewParent, } Message msg = Message.obtain(); msg.what = MSG_WINDOW_FOCUS_CHANGED; msg.arg1 = reportToClient ? 1 : 0; mHandler.sendMessage(msg); } Loading Loading @@ -8131,10 +8144,11 @@ public final class ViewRootImpl implements ViewParent, } @Override public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { public void windowFocusChanged(boolean hasFocus, boolean inTouchMode, boolean reportToClient) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.windowFocusChanged(hasFocus, inTouchMode); viewAncestor.windowFocusChanged(hasFocus, inTouchMode, reportToClient); } } Loading
core/java/com/android/internal/view/BaseIWindow.java +1 −2 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.internal.view; import android.annotation.UnsupportedAppUsage; import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.Bundle; Loading Loading @@ -66,7 +65,7 @@ public class BaseIWindow extends IWindow.Stub { } @Override public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) { public void windowFocusChanged(boolean hasFocus, boolean touchEnabled, boolean reportToClient) { } @Override Loading
services/core/java/com/android/server/wm/DisplayContent.java +0 −2 Original line number Diff line number Diff line Loading @@ -100,7 +100,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION; import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS; import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION; import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER; Loading Loading @@ -2809,7 +2808,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (mCurrentFocus == newFocus) { return false; } mService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget(); boolean imWindowChanged = false; // TODO (b/111080190): Multi-Session IME if (!focusFound) { Loading
services/core/java/com/android/server/wm/RootWindowContainer.java +29 −5 Original line number Diff line number Diff line Loading @@ -122,7 +122,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { // The ID of the display which is responsible for receiving display-unspecified key and pointer // events. private int mTopFocusedDisplayId = INVALID_DISPLAY; int mTopFocusedDisplayId = INVALID_DISPLAY; // Only a seperate transaction until we seperate the apply surface changes // transaction from the global transaction. Loading Loading @@ -156,7 +156,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) { boolean changed = false; int topFocusedDisplayId = INVALID_DISPLAY; for (int i = mChildren.size() - 1; i >= 0; i--) { for (int i = mChildren.size() - 1; i >= 0; --i) { final DisplayContent dc = mChildren.get(i); changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId != INVALID_DISPLAY /* focusFound */); Loading @@ -167,12 +168,35 @@ class RootWindowContainer extends WindowContainer<DisplayContent> { if (topFocusedDisplayId == INVALID_DISPLAY) { topFocusedDisplayId = DEFAULT_DISPLAY; } // TODO(b/118865114): Review if need callback top focus display change to view component. // (i.e. Activity or View) // Currently we only tracked topFocusedDisplayChanged for notifying InputMethodManager via // ViewRootImpl.windowFocusChanged to refocus IME window when top display focus changed // but window focus remain the same case. // It may need to review if any use case that need to add new callback for reporting // this change. final boolean topFocusedDisplayChanged = mTopFocusedDisplayId != topFocusedDisplayId && mode == UPDATE_FOCUS_NORMAL; if (mTopFocusedDisplayId != topFocusedDisplayId) { mTopFocusedDisplayId = topFocusedDisplayId; mService.mInputManager.setFocusedDisplay(topFocusedDisplayId); mService.mInputManager.setFocusedDisplay(mTopFocusedDisplayId); if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId=" + topFocusedDisplayId); + mTopFocusedDisplayId); } // Report window focus or top display focus changed through REPORT_FOCUS_CHANGE. forAllDisplays((dc) -> { final boolean windowFocusChanged = dc.mCurrentFocus != null && dc.mCurrentFocus != dc.mLastFocus; final boolean isTopFocusedDisplay = topFocusedDisplayChanged && dc.getDisplayId() == mTopFocusedDisplayId; if (windowFocusChanged || isTopFocusedDisplay) { final Message msg = mService.mH.obtainMessage( WindowManagerService.H.REPORT_FOCUS_CHANGE, dc); msg.arg1 = topFocusedDisplayChanged ? 1 : 0; mService.mH.sendMessage(msg); } }); final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus; mService.mInputManager.setFocusedWindow( topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null); Loading