diff options
| author | 2024-09-26 18:25:58 +0000 | |
|---|---|---|
| committer | 2024-09-26 18:25:58 +0000 | |
| commit | eeac1b7c83e65e362d01dbc40bfbf0f857846170 (patch) | |
| tree | 88419b9004f56d97486f1bb17784f31f26d14539 | |
| parent | d01202d1064b8363f9fff4920c0e9c4d5840f1f7 (diff) | |
| parent | 3c0b145f400d6f3af19d2f74692d26e59bd27bdf (diff) | |
Merge "Change native input manager interactiveness to per-display" into main
10 files changed, 183 insertions, 22 deletions
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index a2d24f643bfb..73b5d947c0fe 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -432,6 +432,11 @@ public abstract class DisplayManagerInternal { public abstract IntArray getDisplayGroupIds(); /** + * Get all available display ids. + */ + public abstract IntArray getDisplayIds(); + + /** * Called upon presentation started/ended on the display. * @param displayId the id of the display where presentation started. * @param isShown whether presentation is shown. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index ae33b83b49dc..88907e35854f 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -5293,6 +5293,17 @@ public final class DisplayManagerService extends SystemService { } @Override + public IntArray getDisplayIds() { + IntArray displayIds = new IntArray(); + synchronized (mSyncRoot) { + mLogicalDisplayMapper.forEachLocked((logicalDisplay -> { + displayIds.add(logicalDisplay.getDisplayIdLocked()); + }), /* includeDisabled= */ false); + } + return displayIds; + } + + @Override public DisplayManagerInternal.DisplayOffloadSession registerDisplayOffloader( int displayId, @NonNull DisplayManagerInternal.DisplayOffloader displayOffloader) { if (!mFlags.isDisplayOffloadEnabled()) { diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index 92812670057a..99f7f12567b4 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -23,6 +23,7 @@ import android.graphics.PointF; import android.hardware.display.DisplayViewport; import android.hardware.input.KeyGestureEvent; import android.os.IBinder; +import android.util.SparseBooleanArray; import android.view.InputChannel; import android.view.inputmethod.InputMethodSubtype; @@ -45,9 +46,11 @@ public abstract class InputManagerInternal { /** * Called by the power manager to tell the input manager whether it should start - * watching for wake events. + * watching for wake events on given displays. + * + * @param displayInteractivities Map of display ids to their current interactive state. */ - public abstract void setInteractive(boolean interactive); + public abstract void setDisplayInteractivities(SparseBooleanArray displayInteractivities); /** * Toggles Caps Lock state for input device with specific id. diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 909c47bc9359..f04557665477 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -96,6 +96,7 @@ import android.os.vibrator.VibrationEffectSegment; import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; @@ -3337,10 +3338,22 @@ public class InputManagerService extends IInputManager.Stub } @Override - public void setInteractive(boolean interactive) { - mNative.setInteractive(interactive); - mBatteryController.onInteractiveChanged(interactive); - mKeyboardBacklightController.onInteractiveChanged(interactive); + public void setDisplayInteractivities(SparseBooleanArray displayInteractivities) { + boolean globallyInteractive = false; + ArraySet<Integer> nonInteractiveDisplays = new ArraySet<>(); + for (int i = 0; i < displayInteractivities.size(); i++) { + final int displayId = displayInteractivities.keyAt(i); + final boolean displayInteractive = displayInteractivities.get(displayId); + if (displayInteractive) { + globallyInteractive = true; + } else { + nonInteractiveDisplays.add(displayId); + } + } + mNative.setNonInteractiveDisplays( + nonInteractiveDisplays.stream().mapToInt(Integer::intValue).toArray()); + mBatteryController.onInteractiveChanged(globallyInteractive); + mKeyboardBacklightController.onInteractiveChanged(globallyInteractive); } // TODO(b/358569822): Remove this method from InputManagerInternal after key gesture diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index d17e256e34fc..4404d63e02fc 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -141,7 +141,7 @@ interface NativeInputManagerService { void setShowTouches(boolean enabled); - void setInteractive(boolean interactive); + void setNonInteractiveDisplays(int[] displayIds); void reloadCalibration(); @@ -409,7 +409,7 @@ interface NativeInputManagerService { public native void setShowTouches(boolean enabled); @Override - public native void setInteractive(boolean interactive); + public native void setNonInteractiveDisplays(int[] displayIds); @Override public native void reloadCalibration(); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 303828f94e8a..0cdf537b3455 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -53,6 +53,7 @@ import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.WindowManagerPolicyConstants; import com.android.internal.annotations.VisibleForTesting; @@ -512,8 +513,17 @@ public class Notifier { } // Start input as soon as we start waking up or going to sleep. - mInputManagerInternal.setInteractive(interactive); mInputMethodManagerInternal.setInteractive(interactive); + if (!mFlags.isPerDisplayWakeByTouchEnabled()) { + // Since wakefulness is a global property in original logic, all displays should + // be set to the same interactive state, matching system's global wakefulness + SparseBooleanArray displayInteractivities = new SparseBooleanArray(); + int[] displayIds = mDisplayManagerInternal.getDisplayIds().toArray(); + for (int displayId : displayIds) { + displayInteractivities.put(displayId, interactive); + } + mInputManagerInternal.setDisplayInteractivities(displayInteractivities); + } // Notify battery stats. try { diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java index c6ef89dcff69..fd60e06c0762 100644 --- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java +++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java @@ -42,6 +42,11 @@ public class PowerManagerFlags { Flags::improveWakelockLatency ); + private final FlagState mPerDisplayWakeByTouch = new FlagState( + Flags.FLAG_PER_DISPLAY_WAKE_BY_TOUCH, + Flags::perDisplayWakeByTouch + ); + /** Returns whether early-screen-timeout-detector is enabled on not. */ public boolean isEarlyScreenTimeoutDetectorEnabled() { return mEarlyScreenTimeoutDetectorFlagState.isEnabled(); @@ -55,6 +60,13 @@ public class PowerManagerFlags { } /** + * @return Whether per-display wake by touch is enabled or not. + */ + public boolean isPerDisplayWakeByTouchEnabled() { + return mPerDisplayWakeByTouch.isEnabled(); + } + + /** * dumps all flagstates * @param pw printWriter */ @@ -62,6 +74,7 @@ public class PowerManagerFlags { pw.println("PowerManagerFlags:"); pw.println(" " + mEarlyScreenTimeoutDetectorFlagState); pw.println(" " + mImproveWakelockLatency); + pw.println(" " + mPerDisplayWakeByTouch); } private static class FlagState { diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig index 3581b2fad1df..9cf3bb6df3db 100644 --- a/services/core/java/com/android/server/power/feature/power_flags.aconfig +++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig @@ -17,4 +17,12 @@ flag { description: "Feature flag for tracking the optimizations to improve the latency of acquiring and releasing a wakelock." bug: "339590565" is_fixed_read_only: true -}
\ No newline at end of file +} + +flag { + name: "per_display_wake_by_touch" + namespace: "power" + description: "Feature flag to enable per-display wake by touch" + bug: "343295183" + is_fixed_read_only: true +} diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 5cd117b512d4..efca90217e83 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -56,6 +56,7 @@ #include <nativehelper/ScopedPrimitiveArray.h> #include <nativehelper/ScopedUtfChars.h> #include <server_configurable_flags/get_flags.h> +#include <ui/LogicalDisplayId.h> #include <ui/Region.h> #include <utils/Log.h> #include <utils/Looper.h> @@ -64,6 +65,7 @@ #include <atomic> #include <cinttypes> +#include <map> #include <vector> #include "android_hardware_display_DisplayViewport.h" @@ -343,7 +345,7 @@ public: void setTouchpadRightClickZoneEnabled(bool enabled); void setInputDeviceEnabled(uint32_t deviceId, bool enabled); void setShowTouches(bool enabled); - void setInteractive(bool interactive); + void setNonInteractiveDisplays(const std::set<ui::LogicalDisplayId>& displayIds); void reloadCalibration(); void reloadPointerIcons(); void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled); @@ -508,9 +510,11 @@ private: // Keycodes to be remapped. std::map<int32_t /* fromKeyCode */, int32_t /* toKeyCode */> keyRemapping{}; + + // Displays which are non-interactive. + std::set<ui::LogicalDisplayId> nonInteractiveDisplays; } mLocked GUARDED_BY(mLock); - std::atomic<bool> mInteractive; void updateInactivityTimeoutLocked(); void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags); void ensureSpriteControllerLocked(); @@ -524,12 +528,13 @@ private: void forEachPointerControllerLocked(std::function<void(PointerController&)> apply) REQUIRES(mLock); PointerIcon loadPointerIcon(JNIEnv* env, ui::LogicalDisplayId displayId, PointerIconStyle type); + bool isDisplayInteractive(ui::LogicalDisplayId displayId); static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); } }; NativeInputManager::NativeInputManager(jobject serviceObj, const sp<Looper>& looper) - : mLooper(looper), mInteractive(true) { + : mLooper(looper) { JNIEnv* env = jniEnv(); mServiceObj = env->NewGlobalRef(serviceObj); @@ -547,9 +552,13 @@ NativeInputManager::~NativeInputManager() { void NativeInputManager::dump(std::string& dump) { dump += "Input Manager State:\n"; - dump += StringPrintf(INDENT "Interactive: %s\n", toString(mInteractive.load())); { // acquire lock std::scoped_lock _l(mLock); + auto logicalDisplayIdToString = [](const ui::LogicalDisplayId& displayId) { + return std::to_string(displayId.val()); + }; + dump += StringPrintf(INDENT "Display not interactive: %s\n", + dumpSet(mLocked.nonInteractiveDisplays, streamableToString).c_str()); dump += StringPrintf(INDENT "System UI Lights Out: %s\n", toString(mLocked.systemUiLightsOut)); dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed); @@ -1476,8 +1485,10 @@ void NativeInputManager::requestPointerCapture(const sp<IBinder>& windowToken, b mInputManager->getDispatcher().requestPointerCapture(windowToken, enabled); } -void NativeInputManager::setInteractive(bool interactive) { - mInteractive = interactive; +void NativeInputManager::setNonInteractiveDisplays( + const std::set<ui::LogicalDisplayId>& displayIds) { + std::scoped_lock _l(mLock); + mLocked.nonInteractiveDisplays = displayIds; } void NativeInputManager::reloadCalibration() { @@ -1606,7 +1617,7 @@ void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent& keyEvent, // - Ignore untrusted events and pass them along. // - Ask the window manager what to do with normal events and trusted injected events. // - For normal events wake and brighten the screen if currently off or dim. - const bool interactive = mInteractive.load(); + const bool interactive = isDisplayInteractive(keyEvent.getDisplayId()); if (interactive) { policyFlags |= POLICY_FLAG_INTERACTIVE; } @@ -1644,7 +1655,7 @@ void NativeInputManager::interceptMotionBeforeQueueing(ui::LogicalDisplayId disp // - No special filtering for injected events required at this time. // - Filter normal events based on screen state. // - For normal events brighten (but do not wake) the screen if currently dim. - const bool interactive = mInteractive.load(); + const bool interactive = isDisplayInteractive(displayId); if (interactive) { policyFlags |= POLICY_FLAG_INTERACTIVE; } @@ -1683,6 +1694,24 @@ void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, } } +bool NativeInputManager::isDisplayInteractive(ui::LogicalDisplayId displayId) { + // If an input event doesn't have an associated id, use the default display id + if (displayId == ui::LogicalDisplayId::INVALID) { + displayId = ui::LogicalDisplayId::DEFAULT; + } + + { // acquire lock + std::scoped_lock _l(mLock); + + auto it = mLocked.nonInteractiveDisplays.find(displayId); + if (it != mLocked.nonInteractiveDisplays.end()) { + return false; + } + } // release lock + + return true; +} + nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent, uint32_t policyFlags) { @@ -2372,10 +2401,17 @@ static void nativeSetShowTouches(JNIEnv* env, jobject nativeImplObj, jboolean en im->setShowTouches(enabled); } -static void nativeSetInteractive(JNIEnv* env, jobject nativeImplObj, jboolean interactive) { +static void nativeSetNonInteractiveDisplays(JNIEnv* env, jobject nativeImplObj, + jintArray displayIds) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); - im->setInteractive(interactive); + const std::vector displayIdsVec = getIntArray(env, displayIds); + std::set<ui::LogicalDisplayId> logicalDisplayIds; + for (int displayId : displayIdsVec) { + logicalDisplayIds.emplace(ui::LogicalDisplayId{displayId}); + } + + im->setNonInteractiveDisplays(logicalDisplayIds); } static void nativeReloadCalibration(JNIEnv* env, jobject nativeImplObj) { @@ -3021,7 +3057,7 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*)nativeSetShouldNotifyTouchpadHardwareState}, {"setTouchpadRightClickZoneEnabled", "(Z)V", (void*)nativeSetTouchpadRightClickZoneEnabled}, {"setShowTouches", "(Z)V", (void*)nativeSetShowTouches}, - {"setInteractive", "(Z)V", (void*)nativeSetInteractive}, + {"setNonInteractiveDisplays", "([I)V", (void*)nativeSetNonInteractiveDisplays}, {"reloadCalibration", "()V", (void*)nativeReloadCalibration}, {"vibrate", "(I[J[III)V", (void*)nativeVibrate}, {"vibrateCombined", "(I[JLandroid/util/SparseArray;II)V", (void*)nativeVibrateCombined}, diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index fc4d8d871fd5..07029268661e 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -16,6 +16,9 @@ package com.android.server.power; +import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; +import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -31,11 +34,13 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.content.Context; import android.content.res.Resources; import android.hardware.SensorManager; import android.hardware.display.AmbientDisplayConfiguration; +import android.hardware.display.DisplayManagerInternal; import android.os.BatteryStats; import android.os.Handler; import android.os.IWakeLockCallback; @@ -48,11 +53,18 @@ import android.os.WorkSource; import android.os.test.TestLooper; import android.provider.Settings; import android.testing.TestableContext; +import android.util.IntArray; +import android.util.SparseBooleanArray; +import android.view.Display; +import android.view.DisplayAddress; +import android.view.DisplayInfo; import androidx.test.InstrumentationRegistry; import com.android.internal.app.IBatteryStats; import com.android.server.LocalServices; +import com.android.server.input.InputManagerInternal; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.policy.WindowManagerPolicy; import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.feature.PowerManagerFlags; @@ -71,6 +83,8 @@ import java.util.concurrent.Executor; public class NotifierTest { private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; private static final int USER_ID = 0; + private static final int DISPLAY_PORT = 0xFF; + private static final long DISPLAY_MODEL = 0xEEEEEEEEL; @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock; @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; @@ -81,10 +95,16 @@ public class NotifierTest { @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; @Mock private Vibrator mVibrator; @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + @Mock private InputManagerInternal mInputManagerInternal; + @Mock private InputMethodManagerInternal mInputMethodManagerInternal; + @Mock private DisplayManagerInternal mDisplayManagerInternal; + @Mock private ActivityManagerInternal mActivityManagerInternal; @Mock private WakeLockLog mWakeLockLog; @Mock private IBatteryStats mBatteryStats; + @Mock private WindowManagerPolicy mPolicy; + @Mock private PowerManagerFlags mPowerManagerFlags; @Mock private AppOpsManager mAppOpsManager; @@ -96,6 +116,8 @@ public class NotifierTest { private FakeExecutor mTestExecutor = new FakeExecutor(); private Notifier mNotifier; + private DisplayInfo mDefaultDisplayInfo = new DisplayInfo(); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -103,11 +125,25 @@ public class NotifierTest { LocalServices.removeServiceForTest(StatusBarManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal); + LocalServices.removeServiceForTest(InputManagerInternal.class); + LocalServices.addService(InputManagerInternal.class, mInputManagerInternal); + LocalServices.removeServiceForTest(InputMethodManagerInternal.class); + LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal); + + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal); + + mDefaultDisplayInfo.address = DisplayAddress.fromPortAndModel(DISPLAY_PORT, DISPLAY_MODEL); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternal); + mContextSpy = spy(new TestableContext(InstrumentationRegistry.getContext())); mResourcesSpy = spy(mContextSpy.getResources()); when(mContextSpy.getResources()).thenReturn(mResourcesSpy); when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); when(mContextSpy.getSystemService(Vibrator.class)).thenReturn(mVibrator); + when(mDisplayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn( + mDefaultDisplayInfo); mService = new PowerManagerService(mContextSpy, mInjector); } @@ -232,6 +268,32 @@ public class NotifierTest { } @Test + public void testOnGlobalWakefulnessChangeStarted() throws Exception { + createNotifier(); + // GIVEN system is currently non-interactive + when(mPowerManagerFlags.isPerDisplayWakeByTouchEnabled()).thenReturn(false); + final int displayId1 = 101; + final int displayId2 = 102; + final int[] displayIds = new int[]{displayId1, displayId2}; + when(mDisplayManagerInternal.getDisplayIds()).thenReturn(IntArray.wrap(displayIds)); + mNotifier.onGlobalWakefulnessChangeStarted(WAKEFULNESS_ASLEEP, + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, /* eventTime= */ 1000); + mTestLooper.dispatchAll(); + + // WHEN a global wakefulness change to interactive starts + mNotifier.onGlobalWakefulnessChangeStarted(WAKEFULNESS_AWAKE, + PowerManager.WAKE_REASON_TAP, /* eventTime= */ 2000); + mTestLooper.dispatchAll(); + + // THEN input is notified of all displays being interactive + final SparseBooleanArray expectedDisplayInteractivities = new SparseBooleanArray(); + expectedDisplayInteractivities.put(displayId1, true); + expectedDisplayInteractivities.put(displayId2, true); + verify(mInputManagerInternal).setDisplayInteractivities(expectedDisplayInteractivities); + verify(mInputMethodManagerInternal).setInteractive(/* interactive= */ true); + } + + @Test public void testOnWakeLockListener_RemoteException_NoRethrow() throws RemoteException { when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); createNotifier(); @@ -551,7 +613,7 @@ public class NotifierTest { mContextSpy, mBatteryStats, mInjector.createSuspendBlocker(mService, "testBlocker"), - null, + mPolicy, null, null, mTestExecutor, mPowerManagerFlags, injector); |