Loading core/java/android/window/flags/windowing_frontend.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -172,6 +172,16 @@ flag { bug: "357141415" } flag { name: "filter_irrelevant_input_device_change" namespace: "windowing_frontend" description: "Recompute display configuration only for necessary input device changes" bug: "368461853" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "respect_non_top_visible_fixed_orientation" namespace: "windowing_frontend" Loading services/core/java/com/android/server/wm/InputManagerCallback.java +54 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; Loading @@ -28,9 +29,12 @@ import android.annotation.Nullable; import android.gui.StalledTransactionInfo; import android.os.Debug; import android.os.IBinder; import android.os.Trace; import android.util.Slog; import android.util.SparseIntArray; import android.view.Display; import android.view.InputApplicationHandle; import android.view.InputDevice; import android.view.KeyEvent; import android.view.SurfaceControl; import android.view.WindowManager; Loading Loading @@ -64,6 +68,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal // which point the ActivityManager will enable dispatching. private boolean mInputDispatchEnabled; /** * The last input devices info which may affect display configuration. This is a quick lookup * to detect interested changes without entering WM lock. */ private SparseIntArray mLastInputConfigurationSources; public InputManagerCallback(WindowManagerService service) { mService = service; } Loading Loading @@ -117,8 +127,16 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal /** Notifies that the input device configuration has changed. */ @Override public void notifyConfigurationChanged() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "notifyConfigurationChanged"); final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange() || updateLastInputConfigurationSources(); if (changed) { synchronized (mService.mGlobalLock) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "inputDeviceConfigChanged"); mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } synchronized (mInputDevicesReadyMonitor) { Loading @@ -127,6 +145,40 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal mInputDevicesReadyMonitor.notifyAll(); } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** Returns {@code true} if the change of input devices may affect display configuration. */ private boolean updateLastInputConfigurationSources() { final InputDevice[] devices = mService.mInputManager.getInputDevices(); final SparseIntArray newSources = new SparseIntArray(8); final SparseIntArray lastSources = mLastInputConfigurationSources; boolean changed = lastSources == null; for (InputDevice device : devices) { final String descriptor = device.getDescriptor(); if (descriptor == null || device.isVirtual()) { continue; } final int key = descriptor.hashCode(); // The interested attributes from DisplayContent#computeScreenConfiguration. int newSourceHash = device.getSources(); newSourceHash = newSourceHash * 31 + device.getKeyboardType(); newSourceHash = newSourceHash * 31 + device.getAssociatedDisplayId(); newSourceHash = newSourceHash * 31 + (device.isExternal() ? 1 : 0); newSourceHash = newSourceHash * 31 + (device.isEnabled() ? 1 : 0); newSources.put(key, newSourceHash); if (lastSources != null && !changed) { final int lastSource = lastSources.get(key, 0 /* valueIfKeyNotFound */); if (lastSource != newSourceHash) { changed = true; } } } if (lastSources != null && lastSources.size() != newSources.size()) { changed = true; } mLastInputConfigurationSources = newSources; return changed; } /** Notifies that the pointer location configuration has changed. */ Loading services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +43 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ import android.util.MergedConfiguration; import android.view.ContentRecordingSession; import android.view.IWindow; import android.view.InputChannel; import android.view.InputDevice; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.Surface; Loading Loading @@ -1274,6 +1275,48 @@ public class WindowManagerServiceTests extends WindowTestsBase { verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0); } @Test public void testInputDeviceNotifyConfigurationChanged() { mSetFlagsRule.enableFlags(Flags.FLAG_FILTER_IRRELEVANT_INPUT_DEVICE_CHANGE); spyOn(mDisplayContent); doReturn(false).when(mDisplayContent).sendNewConfiguration(); final InputDevice deviceA = mock(InputDevice.class); final InputDevice deviceB = mock(InputDevice.class); doReturn("deviceA").when(deviceA).getDescriptor(); doReturn("deviceB").when(deviceB).getDescriptor(); final InputDevice[] devices1 = { deviceA }; final InputDevice[] devices2 = { deviceB, deviceA }; final Runnable verifySendNewConfiguration = () -> { clearInvocations(mDisplayContent); mWm.mInputManagerCallback.notifyConfigurationChanged(); verify(mDisplayContent).sendNewConfiguration(); }; doReturn(devices1).when(mWm.mInputManager).getInputDevices(); verifySendNewConfiguration.run(); doReturn(devices2).when(mWm.mInputManager).getInputDevices(); verifySendNewConfiguration.run(); doReturn(true).when(deviceB).isEnabled(); verifySendNewConfiguration.run(); doReturn(true).when(deviceA).isExternal(); verifySendNewConfiguration.run(); doReturn(1).when(deviceA).getSources(); verifySendNewConfiguration.run(); doReturn(1).when(deviceA).getAssociatedDisplayId(); verifySendNewConfiguration.run(); doReturn(1).when(deviceA).getKeyboardType(); verifySendNewConfiguration.run(); clearInvocations(mDisplayContent); mWm.mInputManagerCallback.notifyConfigurationChanged(); verify(mDisplayContent, never()).sendNewConfiguration(); } @Test public void testReportSystemGestureExclusionChanged_invalidWindow() { final Session session = mock(Session.class); Loading Loading
core/java/android/window/flags/windowing_frontend.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -172,6 +172,16 @@ flag { bug: "357141415" } flag { name: "filter_irrelevant_input_device_change" namespace: "windowing_frontend" description: "Recompute display configuration only for necessary input device changes" bug: "368461853" metadata { purpose: PURPOSE_BUGFIX } } flag { name: "respect_non_top_visible_fixed_orientation" namespace: "windowing_frontend" Loading
services/core/java/com/android/server/wm/InputManagerCallback.java +54 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; Loading @@ -28,9 +29,12 @@ import android.annotation.Nullable; import android.gui.StalledTransactionInfo; import android.os.Debug; import android.os.IBinder; import android.os.Trace; import android.util.Slog; import android.util.SparseIntArray; import android.view.Display; import android.view.InputApplicationHandle; import android.view.InputDevice; import android.view.KeyEvent; import android.view.SurfaceControl; import android.view.WindowManager; Loading Loading @@ -64,6 +68,12 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal // which point the ActivityManager will enable dispatching. private boolean mInputDispatchEnabled; /** * The last input devices info which may affect display configuration. This is a quick lookup * to detect interested changes without entering WM lock. */ private SparseIntArray mLastInputConfigurationSources; public InputManagerCallback(WindowManagerService service) { mService = service; } Loading Loading @@ -117,8 +127,16 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal /** Notifies that the input device configuration has changed. */ @Override public void notifyConfigurationChanged() { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "notifyConfigurationChanged"); final boolean changed = !com.android.window.flags.Flags.filterIrrelevantInputDeviceChange() || updateLastInputConfigurationSources(); if (changed) { synchronized (mService.mGlobalLock) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "inputDeviceConfigChanged"); mService.mRoot.forAllDisplays(DisplayContent::sendNewConfiguration); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } synchronized (mInputDevicesReadyMonitor) { Loading @@ -127,6 +145,40 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal mInputDevicesReadyMonitor.notifyAll(); } } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** Returns {@code true} if the change of input devices may affect display configuration. */ private boolean updateLastInputConfigurationSources() { final InputDevice[] devices = mService.mInputManager.getInputDevices(); final SparseIntArray newSources = new SparseIntArray(8); final SparseIntArray lastSources = mLastInputConfigurationSources; boolean changed = lastSources == null; for (InputDevice device : devices) { final String descriptor = device.getDescriptor(); if (descriptor == null || device.isVirtual()) { continue; } final int key = descriptor.hashCode(); // The interested attributes from DisplayContent#computeScreenConfiguration. int newSourceHash = device.getSources(); newSourceHash = newSourceHash * 31 + device.getKeyboardType(); newSourceHash = newSourceHash * 31 + device.getAssociatedDisplayId(); newSourceHash = newSourceHash * 31 + (device.isExternal() ? 1 : 0); newSourceHash = newSourceHash * 31 + (device.isEnabled() ? 1 : 0); newSources.put(key, newSourceHash); if (lastSources != null && !changed) { final int lastSource = lastSources.get(key, 0 /* valueIfKeyNotFound */); if (lastSource != newSourceHash) { changed = true; } } } if (lastSources != null && lastSources.size() != newSources.size()) { changed = true; } mLastInputConfigurationSources = newSources; return changed; } /** Notifies that the pointer location configuration has changed. */ Loading
services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +43 −0 Original line number Diff line number Diff line Loading @@ -94,6 +94,7 @@ import android.util.MergedConfiguration; import android.view.ContentRecordingSession; import android.view.IWindow; import android.view.InputChannel; import android.view.InputDevice; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.Surface; Loading Loading @@ -1274,6 +1275,48 @@ public class WindowManagerServiceTests extends WindowTestsBase { verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0); } @Test public void testInputDeviceNotifyConfigurationChanged() { mSetFlagsRule.enableFlags(Flags.FLAG_FILTER_IRRELEVANT_INPUT_DEVICE_CHANGE); spyOn(mDisplayContent); doReturn(false).when(mDisplayContent).sendNewConfiguration(); final InputDevice deviceA = mock(InputDevice.class); final InputDevice deviceB = mock(InputDevice.class); doReturn("deviceA").when(deviceA).getDescriptor(); doReturn("deviceB").when(deviceB).getDescriptor(); final InputDevice[] devices1 = { deviceA }; final InputDevice[] devices2 = { deviceB, deviceA }; final Runnable verifySendNewConfiguration = () -> { clearInvocations(mDisplayContent); mWm.mInputManagerCallback.notifyConfigurationChanged(); verify(mDisplayContent).sendNewConfiguration(); }; doReturn(devices1).when(mWm.mInputManager).getInputDevices(); verifySendNewConfiguration.run(); doReturn(devices2).when(mWm.mInputManager).getInputDevices(); verifySendNewConfiguration.run(); doReturn(true).when(deviceB).isEnabled(); verifySendNewConfiguration.run(); doReturn(true).when(deviceA).isExternal(); verifySendNewConfiguration.run(); doReturn(1).when(deviceA).getSources(); verifySendNewConfiguration.run(); doReturn(1).when(deviceA).getAssociatedDisplayId(); verifySendNewConfiguration.run(); doReturn(1).when(deviceA).getKeyboardType(); verifySendNewConfiguration.run(); clearInvocations(mDisplayContent); mWm.mInputManagerCallback.notifyConfigurationChanged(); verify(mDisplayContent, never()).sendNewConfiguration(); } @Test public void testReportSystemGestureExclusionChanged_invalidWindow() { final Session session = mock(Session.class); Loading