Merge "Add tests to ensure input settings are loaded on boot" into udc-qpr-dev

This commit is contained in:
TreeHugger Robot
2023-06-06 20:39:23 +00:00
committed by Android (Google) Code Review
7 changed files with 138 additions and 53 deletions

View File

@@ -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

View File

@@ -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 =

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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)