summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-09-17 00:11:38 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-09-17 00:11:38 +0000
commitac01554bd8a2ac0d17d706c687b8d5a751626dbb (patch)
tree08190dbe124c89e28cb5463f17e29dc687a6873e
parent0867e667eb139cf874c087d87df4651f4fbde046 (diff)
parent0c81597eab61c0138606531c0522f65d156424ca (diff)
Merge "Shift block shortcuts before KeyGestureController" into main
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java23
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java96
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java5
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt123
5 files changed, 136 insertions, 126 deletions
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 44d675305ac0..edc35e553244 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,6 +16,9 @@
package com.android.server.input;
+import static android.Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+import static android.content.PermissionChecker.PID_UNKNOWN;
import static android.provider.DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT;
import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -37,6 +40,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.PermissionChecker;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.graphics.PixelFormat;
@@ -121,6 +125,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.DisplayThread;
@@ -130,6 +135,7 @@ import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.input.debug.FocusEventDebugView;
import com.android.server.input.debug.TouchpadDebugViewController;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowManagerInternal;
import libcore.io.IoUtils;
@@ -177,6 +183,7 @@ public class InputManagerService extends IInputManager.Stub
private final InputManagerHandler mHandler;
private DisplayManagerInternal mDisplayManagerInternal;
+ private WindowManagerInternal mWindowManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
private final File mDoubleTouchGestureEnableFile;
@@ -548,6 +555,7 @@ public class InputManagerService extends IInputManager.Stub
}
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mSettingsObserver.registerAndUpdate();
@@ -2471,6 +2479,11 @@ public class InputManagerService extends IInputManager.Stub
long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
final long keyNotConsumed = 0;
long value = keyNotConsumed;
+ // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
+ if ((event.isMetaPressed() || KeyEvent.isMetaKey(event.getKeyCode()))
+ && shouldInterceptShortcuts(focus)) {
+ return keyNotConsumed;
+ }
if (useKeyGestureEventHandler()) {
value = mKeyGestureController.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
@@ -2481,6 +2494,16 @@ public class InputManagerService extends IInputManager.Stub
return value;
}
+ private boolean shouldInterceptShortcuts(IBinder focusedToken) {
+ KeyInterceptionInfo info =
+ mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+ boolean hasInterceptWindowFlag = info != null && (info.layoutParamsPrivateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS) != 0;
+ return hasInterceptWindowFlag && PermissionChecker.checkPermissionForDataDelivery(mContext,
+ OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW, PID_UNKNOWN, info.windowOwnerUid,
+ null, null, null) == PERMISSION_GRANTED;
+ }
+
// Native callback.
@SuppressWarnings("unused")
private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 98091b1e617f..ef37464704ae 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3350,12 +3350,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- // TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
- if ((event.isMetaPressed() || KeyEvent.isMetaKey(keyCode))
- && shouldInterceptShortcuts(focusedToken)) {
- return keyNotConsumed;
- }
-
Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
if (consumedKeys == null) {
consumedKeys = new HashSet<>();
@@ -4064,15 +4058,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
- private boolean shouldInterceptShortcuts(IBinder focusedToken) {
- KeyInterceptionInfo info =
- mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
- boolean hasInterceptWindowFlag = (info.layoutParamsPrivateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS) != 0;
- return hasInterceptWindowFlag && mButtonOverridePermissionChecker.canAppOverrideSystemKey(
- mContext, info.windowOwnerUid);
- }
-
/**
* In this function, we check whether a system key should be sent to the application. We also
* detect the key gesture on this key, even if the key will be sent to the app. The gesture
diff --git a/services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java b/services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java
deleted file mode 100644
index b979335233e3..000000000000
--- a/services/tests/wmtests/src/com/android/server/policy/MetaKeyEventsInterceptionTests.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2024 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.policy;
-
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.view.KeyEvent;
-import android.view.WindowManager;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.policy.KeyInterceptionInfo;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Testing {@link PhoneWindowManager} functionality of letting app intercepting key events
- * containing META.
- */
-@SmallTest
-public class MetaKeyEventsInterceptionTests extends ShortcutKeyTestBase {
-
- private static final List<KeyEvent> META_KEY_EVENTS = Arrays.asList(
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_LEFT),
- new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_RIGHT),
- new KeyEvent(/* downTime= */ 0, /* eventTime= */
- 0, /* action= */ 0, /* code= */ 0, /* repeat= */ 0,
- /* metaState= */ KeyEvent.META_META_ON));
-
- @Before
- public void setUp() {
- setUpPhoneWindowManager();
- }
-
- @Test
- public void doesntInterceptMetaKeyEvents_whenWindowAskedForIt() {
- mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(/* granted= */ true);
- setWindowKeyInterceptionWithPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS);
-
- META_KEY_EVENTS.forEach(keyEvent -> {
- assertKeyInterceptionResult(keyEvent, /* intercepted= */ false);
- });
- }
-
- @Test
- public void interceptsMetaKeyEvents_whenWindowDoesntHaveFlagSet() {
- mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(/* granted= */ true);
- setWindowKeyInterceptionWithPrivateFlags(0);
-
- META_KEY_EVENTS.forEach(keyEvent -> {
- assertKeyInterceptionResult(keyEvent, /* intercepted= */ true);
- });
- }
-
- @Test
- public void interceptsMetaKeyEvents_whenWindowDoesntHavePermission() {
- mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(/* granted= */ false);
- setWindowKeyInterceptionWithPrivateFlags(PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS);
-
- META_KEY_EVENTS.forEach(keyEvent -> {
- assertKeyInterceptionResult(keyEvent, /* intercepted= */ true);
- });
- }
-
- private void setWindowKeyInterceptionWithPrivateFlags(int privateFlags) {
- KeyInterceptionInfo info = new KeyInterceptionInfo(
- WindowManager.LayoutParams.TYPE_APPLICATION, privateFlags, "title", 0);
- mPhoneWindowManager.overrideWindowKeyInterceptionInfo(info);
- }
-
- private void assertKeyInterceptionResult(KeyEvent keyEvent, boolean intercepted) {
- long result = mPhoneWindowManager.interceptKeyBeforeDispatching(keyEvent);
- int expected = intercepted ? -1 : 0;
- assertThat(result).isEqualTo(expected);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 0b55e2b9313e..98401b33840f 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -52,7 +52,6 @@ import static org.mockito.Mockito.after;
import static org.mockito.Mockito.description;
import static org.mockito.Mockito.mockingDetails;
import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import android.app.ActivityManagerInternal;
@@ -634,10 +633,6 @@ class TestPhoneWindowManager {
.when(mButtonOverridePermissionChecker).canAppOverrideSystemKey(any(), anyInt());
}
- void overrideWindowKeyInterceptionInfo(KeyInterceptionInfo info) {
- when(mWindowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info);
- }
-
void overrideKeyEventPolicyFlags(int flags) {
mKeyEventPolicyFlags = flags;
}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 8829f74f5092..c2e71f8269da 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -17,8 +17,10 @@
package com.android.server.input
+import android.Manifest
import android.content.Context
import android.content.ContextWrapper
+import android.content.PermissionChecker
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayViewport
import android.hardware.display.VirtualDisplay
@@ -28,19 +30,26 @@ import android.os.InputEventInjectionSync
import android.os.SystemClock
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.provider.Settings
import android.view.View.OnKeyListener
import android.view.InputDevice
import android.view.KeyEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
+import android.view.WindowManager
import android.test.mock.MockContentResolver
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.internal.policy.KeyInterceptionInfo
import com.android.internal.util.test.FakeSettingsProvider
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.LocalServices
+import com.android.server.wm.WindowManagerInternal
import com.google.common.truth.Truth.assertThat
-import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
@@ -49,15 +58,15 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyFloat
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when`
import org.mockito.stubbing.OngoingStubbing
/**
@@ -69,14 +78,24 @@ import org.mockito.stubbing.OngoingStubbing
@Presubmit
class InputManagerServiceTests {
- @get:Rule
- val mockitoRule = MockitoJUnit.rule()!!
+ companion object {
+ val ACTION_KEY_EVENTS = listOf(
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_LEFT),
+ KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_META_RIGHT),
+ KeyEvent( /* downTime= */0, /* eventTime= */0, /* action= */0, /* code= */0,
+ /* repeat= */0, KeyEvent.META_META_ON
+ )
+ )
+ }
- @get:Rule
- val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
+ @JvmField
+ @Rule
+ val extendedMockitoRule =
+ ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java)
+ .mockStatic(PermissionChecker::class.java).build()!!
@get:Rule
- val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()!!
+ val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
@Mock
private lateinit var native: NativeInputManagerService
@@ -85,6 +104,9 @@ class InputManagerServiceTests {
private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
@Mock
+ private lateinit var windowManagerInternal: WindowManagerInternal
+
+ @Mock
private lateinit var uEventManager: UEventManager
private lateinit var service: InputManagerService
@@ -119,6 +141,10 @@ class InputManagerServiceTests {
whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager)
whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager)
+ ExtendedMockito.doReturn(windowManagerInternal).`when` {
+ LocalServices.getService(eq(WindowManagerInternal::class.java))
+ }
+
assertTrue("Local service must be registered", this::localService.isInitialized)
service.setWindowManagerCallbacks(wmCallbacks)
}
@@ -195,7 +221,7 @@ class InputManagerServiceTests {
}
@Test
- fun testAddAndRemoveVirtualmKeyboardLayoutAssociation() {
+ fun testAddAndRemoveVirtualKeyboardLayoutAssociation() {
val inputPort = "input port"
val languageTag = "language"
val layoutType = "layoutType"
@@ -206,6 +232,48 @@ class InputManagerServiceTests {
verify(native, times(2)).changeKeyboardLayoutAssociation()
}
+ @Test
+ fun testActionKeyEventsForwardedToFocusedWindow_whenCorrectlyRequested() {
+ service.systemRunning()
+ overrideSendActionKeyEventsToFocusedWindow(
+ /* hasPermission = */true,
+ /* hasPrivateFlag = */true
+ )
+ whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
+
+ for (event in ACTION_KEY_EVENTS) {
+ assertEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
+ }
+ }
+
+ @Test
+ fun testActionKeyEventsNotForwardedToFocusedWindow_whenNoPermissions() {
+ service.systemRunning()
+ overrideSendActionKeyEventsToFocusedWindow(
+ /* hasPermission = */false,
+ /* hasPrivateFlag = */true
+ )
+ whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
+
+ for (event in ACTION_KEY_EVENTS) {
+ assertNotEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
+ }
+ }
+
+ @Test
+ fun testActionKeyEventsNotForwardedToFocusedWindow_whenNoPrivateFlag() {
+ service.systemRunning()
+ overrideSendActionKeyEventsToFocusedWindow(
+ /* hasPermission = */true,
+ /* hasPrivateFlag = */false
+ )
+ whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(-1)
+
+ for (event in ACTION_KEY_EVENTS) {
+ assertNotEquals(0, service.interceptKeyBeforeDispatching(null, event, 0))
+ }
+ }
+
private fun createVirtualDisplays(count: Int): List<VirtualDisplay> {
val displayManager: DisplayManager = context.getSystemService(
DisplayManager::class.java
@@ -373,6 +441,41 @@ class InputManagerServiceTests {
verify(mockOnKeyListener).onKey(mockSurfaceView2, KeyEvent.KEYCODE_A, upEvent)
verify(mockOnKeyListener, never()).onKey(mockSurfaceView1, KeyEvent.KEYCODE_A, upEvent)
}
+
+ fun overrideSendActionKeyEventsToFocusedWindow(
+ hasPermission: Boolean,
+ hasPrivateFlag: Boolean
+ ) {
+ ExtendedMockito.doReturn(
+ if (hasPermission) {
+ PermissionChecker.PERMISSION_GRANTED
+ } else {
+ PermissionChecker.PERMISSION_HARD_DENIED
+ }
+ ).`when` {
+ PermissionChecker.checkPermissionForDataDelivery(
+ any(),
+ eq(Manifest.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW),
+ anyInt(),
+ anyInt(),
+ any(),
+ any(),
+ any()
+ )
+ }
+
+ val info = KeyInterceptionInfo(
+ /* type = */0,
+ if (hasPrivateFlag) {
+ WindowManager.LayoutParams.PRIVATE_FLAG_ALLOW_ACTION_KEY_EVENTS
+ } else {
+ 0
+ },
+ "title",
+ /* uid = */0
+ )
+ whenever(windowManagerInternal.getKeyInterceptionInfoFromToken(any())).thenReturn(info)
+ }
}
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)