Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 714eee2e authored by Riddle Hsu's avatar Riddle Hsu
Browse files

Reduce unnecessary calculation from input device change

The notifyConfigurationChanged can be called for any attributes
change of InputDevice. And the change may be unrelated to display
configuration. E.g. motion range change of touch panel.

So before asking window manager to recompute entire display
configuration, check if attributes which can affect display are
changed. That reduces the execution time of the method from
5ms to <0.1ms when turning on/off screen, and it reduces lock
contention between other important operations.

Bug: 368461853
Flag: com.android.window.flags.filter_irrelevant_input_device_change
Test: atest WindowManagerServiceTests# \
            testInputDeviceNotifyConfigurationChanged
Change-Id: Ifce6778d8180865b7597a391b5fddd0009a04162
parent d8aa9a2a
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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"
+54 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
    }
@@ -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) {
@@ -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. */
+43 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);