Add tests to ensure input settings are loaded on boot
The input settings are loaded on systemReady(). To add these tests, we must mock UEventManager throughout input to prevent UEventObservers from being added from the test process, which doesn't have permissions. Bug: 283782679 Test: atest InputManagerServiceTests Change-Id: I023e5c1ef44367c2147978acae5f225c9f50f11c Merged-In: I023e5c1ef44367c2147978acae5f225c9f50f11c
This commit is contained in:
@@ -46,7 +46,6 @@ import android.view.InputDevice;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
@@ -102,8 +101,9 @@ final class BatteryController {
|
||||
@GuardedBy("mLock")
|
||||
private BluetoothBatteryManager.BluetoothBatteryListener mBluetoothBatteryListener;
|
||||
|
||||
BatteryController(Context context, NativeInputManagerService nativeService, Looper looper) {
|
||||
this(context, nativeService, looper, new UEventManager() {},
|
||||
BatteryController(Context context, NativeInputManagerService nativeService, Looper looper,
|
||||
UEventManager uEventManager) {
|
||||
this(context, nativeService, looper, uEventManager,
|
||||
new LocalBluetoothBatteryManager(context, looper));
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ final class BatteryController {
|
||||
private BluetoothAdapter.OnMetadataChangedListener mBluetoothMetadataListener;
|
||||
|
||||
@Nullable
|
||||
private UEventBatteryListener mUEventBatteryListener;
|
||||
private BatteryController.UEventBatteryListener mUEventBatteryListener;
|
||||
|
||||
DeviceMonitor(int deviceId) {
|
||||
mState = new State(deviceId);
|
||||
@@ -630,7 +630,7 @@ final class BatteryController {
|
||||
return;
|
||||
}
|
||||
final int deviceId = mState.deviceId;
|
||||
mUEventBatteryListener = new UEventBatteryListener() {
|
||||
mUEventBatteryListener = new BatteryController.UEventBatteryListener() {
|
||||
@Override
|
||||
public void onBatteryUEvent(long eventTime) {
|
||||
handleUEventNotification(deviceId, eventTime);
|
||||
@@ -898,40 +898,25 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
// An interface used to change the API of UEventObserver to a more test-friendly format.
|
||||
@VisibleForTesting
|
||||
interface UEventManager {
|
||||
|
||||
@VisibleForTesting
|
||||
abstract class UEventBatteryListener {
|
||||
private final UEventObserver mObserver = new UEventObserver() {
|
||||
@Override
|
||||
public void onUEvent(UEvent event) {
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"UEventListener: Received UEvent: "
|
||||
+ event + " eventTime: " + eventTime);
|
||||
}
|
||||
if (!"CHANGE".equalsIgnoreCase(event.get("ACTION"))
|
||||
|| !"POWER_SUPPLY".equalsIgnoreCase(event.get("SUBSYSTEM"))) {
|
||||
// Disregard any UEvents that do not correspond to battery changes.
|
||||
return;
|
||||
}
|
||||
UEventBatteryListener.this.onBatteryUEvent(eventTime);
|
||||
}
|
||||
};
|
||||
|
||||
public abstract void onBatteryUEvent(long eventTime);
|
||||
abstract static class UEventBatteryListener extends UEventManager.UEventListener {
|
||||
@Override
|
||||
public void onUEvent(UEventObserver.UEvent event) {
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"UEventListener: Received UEvent: "
|
||||
+ event + " eventTime: " + eventTime);
|
||||
}
|
||||
if (!"CHANGE".equalsIgnoreCase(event.get("ACTION"))
|
||||
|| !"POWER_SUPPLY".equalsIgnoreCase(event.get("SUBSYSTEM"))) {
|
||||
// Disregard any UEvents that do not correspond to battery changes.
|
||||
return;
|
||||
}
|
||||
UEventBatteryListener.this.onBatteryUEvent(eventTime);
|
||||
}
|
||||
|
||||
default void addListener(UEventBatteryListener listener, String match) {
|
||||
listener.mObserver.startObserving(match);
|
||||
}
|
||||
|
||||
default void removeListener(UEventBatteryListener listener) {
|
||||
listener.mObserver.stopObserving();
|
||||
}
|
||||
public abstract void onBatteryUEvent(long eventTime);
|
||||
}
|
||||
|
||||
// An interface used to change the API of adding a bluetooth battery listener to a more
|
||||
|
||||
@@ -393,10 +393,12 @@ public class InputManagerService extends IInputManager.Stub
|
||||
static class Injector {
|
||||
private final Context mContext;
|
||||
private final Looper mLooper;
|
||||
private final UEventManager mUEventManager;
|
||||
|
||||
Injector(Context context, Looper looper) {
|
||||
Injector(Context context, Looper looper, UEventManager uEventManager) {
|
||||
mContext = context;
|
||||
mLooper = looper;
|
||||
mUEventManager = uEventManager;
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
@@ -407,6 +409,10 @@ public class InputManagerService extends IInputManager.Stub
|
||||
return mLooper;
|
||||
}
|
||||
|
||||
UEventManager getUEventManager() {
|
||||
return mUEventManager;
|
||||
}
|
||||
|
||||
NativeInputManagerService getNativeService(InputManagerService service) {
|
||||
return new NativeInputManagerService.NativeImpl(service, mLooper.getQueue());
|
||||
}
|
||||
@@ -417,7 +423,7 @@ public class InputManagerService extends IInputManager.Stub
|
||||
}
|
||||
|
||||
public InputManagerService(Context context) {
|
||||
this(new Injector(context, DisplayThread.get().getLooper()));
|
||||
this(new Injector(context, DisplayThread.get().getLooper(), new UEventManager() {}));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -432,10 +438,12 @@ public class InputManagerService extends IInputManager.Stub
|
||||
mSettingsObserver = new InputSettingsObserver(mContext, mHandler, this, mNative);
|
||||
mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore,
|
||||
injector.getLooper());
|
||||
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper());
|
||||
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper(),
|
||||
injector.getUEventManager());
|
||||
mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled()
|
||||
? new KeyboardBacklightController(mContext, mNative, mDataStore,
|
||||
injector.getLooper()) : new KeyboardBacklightControllerInterface() {};
|
||||
injector.getLooper(), injector.getUEventManager())
|
||||
: new KeyboardBacklightControllerInterface() {};
|
||||
mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper());
|
||||
|
||||
mUseDevInputEventForAudioJack =
|
||||
|
||||
@@ -94,6 +94,7 @@ final class KeyboardBacklightController implements
|
||||
private final PersistentDataStore mDataStore;
|
||||
private final Handler mHandler;
|
||||
private final AnimatorFactory mAnimatorFactory;
|
||||
private final UEventManager mUEventManager;
|
||||
// Always access on handler thread or need to lock this for synchronization.
|
||||
private final SparseArray<KeyboardBacklightState> mKeyboardBacklights = new SparseArray<>(1);
|
||||
// Maintains state if all backlights should be on or turned off
|
||||
@@ -123,19 +124,21 @@ final class KeyboardBacklightController implements
|
||||
}
|
||||
|
||||
KeyboardBacklightController(Context context, NativeInputManagerService nativeService,
|
||||
PersistentDataStore dataStore, Looper looper) {
|
||||
this(context, nativeService, dataStore, looper, ValueAnimator::ofInt);
|
||||
PersistentDataStore dataStore, Looper looper, UEventManager uEventManager) {
|
||||
this(context, nativeService, dataStore, looper, ValueAnimator::ofInt, uEventManager);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
KeyboardBacklightController(Context context, NativeInputManagerService nativeService,
|
||||
PersistentDataStore dataStore, Looper looper, AnimatorFactory animatorFactory) {
|
||||
PersistentDataStore dataStore, Looper looper, AnimatorFactory animatorFactory,
|
||||
UEventManager uEventManager) {
|
||||
mContext = context;
|
||||
mNative = nativeService;
|
||||
mDataStore = dataStore;
|
||||
mHandler = new Handler(looper, this::handleMessage);
|
||||
mAnimatorFactory = animatorFactory;
|
||||
mAmbientController = new AmbientKeyboardBacklightController(context, looper);
|
||||
mUEventManager = uEventManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,13 +155,12 @@ final class KeyboardBacklightController implements
|
||||
// We want to observe creation of such LED nodes since they might be created after device
|
||||
// FD created and InputDevice creation logic doesn't initialize LED nodes which leads to
|
||||
// backlight not working.
|
||||
UEventObserver observer = new UEventObserver() {
|
||||
mUEventManager.addListener(new UEventManager.UEventListener() {
|
||||
@Override
|
||||
public void onUEvent(UEvent event) {
|
||||
public void onUEvent(UEventObserver.UEvent event) {
|
||||
onKeyboardBacklightUEvent(event);
|
||||
}
|
||||
};
|
||||
observer.startObserving(UEVENT_KEYBOARD_BACKLIGHT_TAG);
|
||||
}, UEVENT_KEYBOARD_BACKLIGHT_TAG);
|
||||
|
||||
if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) {
|
||||
// Start ambient backlight controller
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2023 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.input;
|
||||
|
||||
import android.os.UEventObserver;
|
||||
|
||||
/** An interface used to change the API of UEventObserver to a more test-friendly format. */
|
||||
interface UEventManager {
|
||||
|
||||
abstract class UEventListener {
|
||||
private final UEventObserver mObserver = new UEventObserver() {
|
||||
@Override
|
||||
public void onUEvent(UEvent event) {
|
||||
UEventListener.this.onUEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
public abstract void onUEvent(UEventObserver.UEvent event);
|
||||
}
|
||||
|
||||
default void addListener(UEventListener listener, String match) {
|
||||
listener.mObserver.startObserving(match);
|
||||
}
|
||||
|
||||
default void removeListener(UEventListener listener) {
|
||||
listener.mObserver.stopObserving();
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,7 @@ import androidx.test.core.app.ApplicationProvider
|
||||
import com.android.server.input.BatteryController.BluetoothBatteryManager
|
||||
import com.android.server.input.BatteryController.BluetoothBatteryManager.BluetoothBatteryListener
|
||||
import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS
|
||||
import com.android.server.input.BatteryController.UEventManager
|
||||
import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener
|
||||
import com.android.server.input.BatteryController.UEventBatteryListener
|
||||
import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS
|
||||
import org.hamcrest.Description
|
||||
import org.hamcrest.Matcher
|
||||
|
||||
@@ -23,9 +23,12 @@ import android.hardware.display.DisplayViewport
|
||||
import android.os.IInputConstants
|
||||
import android.os.test.TestLooper
|
||||
import android.platform.test.annotations.Presubmit
|
||||
import android.provider.Settings
|
||||
import android.test.mock.MockContentResolver
|
||||
import android.view.Display
|
||||
import android.view.PointerIcon
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import com.android.internal.util.test.FakeSettingsProvider
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -33,6 +36,8 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
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
|
||||
@@ -44,7 +49,9 @@ import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.verifyZeroInteractions
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.stubbing.OngoingStubbing
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@@ -58,7 +65,10 @@ import java.util.concurrent.TimeUnit
|
||||
class InputManagerServiceTests {
|
||||
|
||||
@get:Rule
|
||||
val rule = MockitoJUnit.rule()!!
|
||||
val mockitoRule = MockitoJUnit.rule()!!
|
||||
|
||||
@get:Rule
|
||||
val fakeSettingsProviderRule = FakeSettingsProvider.rule()!!
|
||||
|
||||
@Mock
|
||||
private lateinit var native: NativeInputManagerService
|
||||
@@ -66,17 +76,25 @@ class InputManagerServiceTests {
|
||||
@Mock
|
||||
private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks
|
||||
|
||||
@Mock
|
||||
private lateinit var uEventManager: UEventManager
|
||||
|
||||
private lateinit var service: InputManagerService
|
||||
private lateinit var localService: InputManagerInternal
|
||||
private lateinit var context: Context
|
||||
private lateinit var testLooper: TestLooper
|
||||
private lateinit var contentResolver: MockContentResolver
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = spy(ContextWrapper(InstrumentationRegistry.getContext()))
|
||||
contentResolver = MockContentResolver(context)
|
||||
contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider())
|
||||
whenever(context.contentResolver).thenReturn(contentResolver)
|
||||
testLooper = TestLooper()
|
||||
service =
|
||||
InputManagerService(object : InputManagerService.Injector(context, testLooper.looper) {
|
||||
InputManagerService(object : InputManagerService.Injector(
|
||||
context, testLooper.looper, uEventManager) {
|
||||
override fun getNativeService(
|
||||
service: InputManagerService?
|
||||
): NativeInputManagerService {
|
||||
@@ -91,10 +109,37 @@ class InputManagerServiceTests {
|
||||
service.setWindowManagerCallbacks(wmCallbacks)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStart() {
|
||||
verifyZeroInteractions(native)
|
||||
|
||||
service.start()
|
||||
verify(native).start()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInputSettingsUpdatedOnSystemRunning() {
|
||||
verifyZeroInteractions(native)
|
||||
|
||||
service.systemRunning()
|
||||
|
||||
verify(native).setPointerSpeed(anyInt())
|
||||
verify(native).setTouchpadPointerSpeed(anyInt())
|
||||
verify(native).setTouchpadNaturalScrollingEnabled(anyBoolean())
|
||||
verify(native).setTouchpadTapToClickEnabled(anyBoolean())
|
||||
verify(native).setTouchpadRightClickZoneEnabled(anyBoolean())
|
||||
verify(native).setShowTouches(anyBoolean())
|
||||
verify(native).reloadPointerIcons()
|
||||
verify(native).notifyKeyGestureTimeoutsChanged()
|
||||
verify(native).setMotionClassifierEnabled(anyBoolean())
|
||||
verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
|
||||
verify(native).setStylusPointerIconEnabled(anyBoolean())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPointerDisplayUpdatesWhenDisplayViewportsChanged() {
|
||||
val displayId = 123
|
||||
`when`(wmCallbacks.pointerDisplayId).thenReturn(displayId)
|
||||
whenever(wmCallbacks.pointerDisplayId).thenReturn(displayId)
|
||||
val viewports = listOf<DisplayViewport>()
|
||||
localService.setDisplayViewports(viewports)
|
||||
verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java))
|
||||
@@ -337,3 +382,5 @@ class InputManagerServiceTests {
|
||||
thread.join(100 /*millis*/)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall)
|
||||
|
||||
@@ -105,6 +105,8 @@ class KeyboardBacklightControllerTests {
|
||||
private lateinit var iInputManager: IInputManager
|
||||
@Mock
|
||||
private lateinit var native: NativeInputManagerService
|
||||
@Mock
|
||||
private lateinit var uEventManager: UEventManager
|
||||
private lateinit var keyboardBacklightController: KeyboardBacklightController
|
||||
private lateinit var context: Context
|
||||
private lateinit var dataStore: PersistentDataStore
|
||||
@@ -130,7 +132,7 @@ class KeyboardBacklightControllerTests {
|
||||
})
|
||||
testLooper = TestLooper()
|
||||
keyboardBacklightController = KeyboardBacklightController(context, native, dataStore,
|
||||
testLooper.looper, FakeAnimatorFactory())
|
||||
testLooper.looper, FakeAnimatorFactory(), uEventManager)
|
||||
InputManagerGlobal.resetInstance(iInputManager)
|
||||
val inputManager = InputManager(context)
|
||||
`when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
|
||||
|
||||
Reference in New Issue
Block a user