Merge "Update monitored InputDevice battery state on an UEvent notification"
This commit is contained in:
committed by
Android (Google) Code Review
commit
df642e79f2
@@ -18,12 +18,17 @@ package com.android.server.input;
|
||||
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.BatteryState;
|
||||
import android.hardware.input.IInputDeviceBatteryListener;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UEventObserver;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
@@ -31,6 +36,8 @@ import android.util.Slog;
|
||||
import android.view.InputDevice;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.input.BatteryController.UEventManager.UEventListener;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
@@ -41,7 +48,7 @@ import java.util.Set;
|
||||
* A thread-safe component of {@link InputManagerService} responsible for managing the battery state
|
||||
* of input devices.
|
||||
*/
|
||||
final class BatteryController {
|
||||
final class BatteryController implements InputManager.InputDeviceListener {
|
||||
private static final String TAG = BatteryController.class.getSimpleName();
|
||||
|
||||
// To enable these logs, run:
|
||||
@@ -51,15 +58,35 @@ final class BatteryController {
|
||||
private final Object mLock = new Object();
|
||||
private final Context mContext;
|
||||
private final NativeInputManagerService mNative;
|
||||
private final Handler mHandler;
|
||||
private final UEventManager mUEventManager;
|
||||
|
||||
// Maps a pid to the registered listener record for that process. There can only be one battery
|
||||
// listener per process.
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<Integer, ListenerRecord> mListenerRecords = new ArrayMap<>();
|
||||
|
||||
BatteryController(Context context, NativeInputManagerService nativeService) {
|
||||
// Maps a deviceId that is being monitored to the battery state for the device.
|
||||
// This must be kept in sync with {@link #mListenerRecords}.
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<Integer, MonitoredDeviceState> mMonitoredDeviceStates = new ArrayMap<>();
|
||||
|
||||
BatteryController(Context context, NativeInputManagerService nativeService, Looper looper) {
|
||||
this(context, nativeService, looper, new UEventManager() {});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
BatteryController(Context context, NativeInputManagerService nativeService, Looper looper,
|
||||
UEventManager uEventManager) {
|
||||
mContext = context;
|
||||
mNative = nativeService;
|
||||
mHandler = new Handler(looper);
|
||||
mUEventManager = uEventManager;
|
||||
}
|
||||
|
||||
void systemRunning() {
|
||||
Objects.requireNonNull(mContext.getSystemService(InputManager.class))
|
||||
.registerInputDeviceListener(this, mHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,29 +123,45 @@ final class BatteryController {
|
||||
+ " is already monitoring deviceId " + deviceId);
|
||||
}
|
||||
|
||||
MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
|
||||
if (deviceState == null) {
|
||||
// This is the first listener that is monitoring this device.
|
||||
deviceState = new MonitoredDeviceState(deviceId);
|
||||
mMonitoredDeviceStates.put(deviceId, deviceState);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Battery listener for pid " + pid
|
||||
+ " is monitoring deviceId " + deviceId);
|
||||
}
|
||||
|
||||
notifyBatteryListener(deviceId, listenerRecord);
|
||||
notifyBatteryListener(listenerRecord, deviceState);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyBatteryListener(int deviceId, ListenerRecord record) {
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
private static void notifyBatteryListener(ListenerRecord listenerRecord,
|
||||
MonitoredDeviceState deviceState) {
|
||||
try {
|
||||
record.mListener.onBatteryStateChanged(
|
||||
deviceId,
|
||||
hasBattery(deviceId),
|
||||
mNative.getBatteryStatus(deviceId),
|
||||
mNative.getBatteryCapacity(deviceId) / 100.f,
|
||||
eventTime);
|
||||
listenerRecord.mListener.onBatteryStateChanged(
|
||||
deviceState.mDeviceId,
|
||||
deviceState.mHasBattery,
|
||||
deviceState.mBatteryStatus,
|
||||
deviceState.mBatteryCapacity,
|
||||
deviceState.mLastUpdateTime);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to notify listener", e);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void notifyAllListenersForDeviceLocked(MonitoredDeviceState deviceState) {
|
||||
mListenerRecords.forEach((pid, listenerRecord) -> {
|
||||
if (listenerRecord.mMonitoredDevices.contains(deviceState.mDeviceId)) {
|
||||
notifyBatteryListener(listenerRecord, deviceState);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean hasBattery(int deviceId) {
|
||||
final InputDevice device =
|
||||
Objects.requireNonNull(mContext.getSystemService(InputManager.class))
|
||||
@@ -126,6 +169,12 @@ final class BatteryController {
|
||||
return device != null && device.hasBattery();
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private MonitoredDeviceState getDeviceStateOrThrowLocked(int deviceId) {
|
||||
return Objects.requireNonNull(mMonitoredDeviceStates.get(deviceId),
|
||||
"Maps are out of sync: Cannot find device state for deviceId " + deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the battery listener for the given input device and stop monitoring its battery
|
||||
* state. If there are no other input devices that this listener is monitoring, the listener is
|
||||
@@ -170,6 +219,13 @@ final class BatteryController {
|
||||
+ pid);
|
||||
}
|
||||
|
||||
if (!hasRegisteredListenerForDeviceLocked(deviceId)) {
|
||||
// There are no more listeners monitoring this device.
|
||||
final MonitoredDeviceState deviceState = getDeviceStateOrThrowLocked(deviceId);
|
||||
deviceState.stopMonitoring();
|
||||
mMonitoredDeviceStates.remove(deviceId);
|
||||
}
|
||||
|
||||
if (listenerRecord.mMonitoredDevices.isEmpty()) {
|
||||
// There are no more devices being monitored by this listener.
|
||||
listenerRecord.mListener.asBinder().unlinkToDeath(listenerRecord.mDeathRecipient, 0);
|
||||
@@ -178,6 +234,16 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean hasRegisteredListenerForDeviceLocked(int deviceId) {
|
||||
for (int i = 0; i < mListenerRecords.size(); i++) {
|
||||
if (mListenerRecords.valueAt(i).mMonitoredDevices.contains(deviceId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleListeningProcessDied(int pid) {
|
||||
synchronized (mLock) {
|
||||
final ListenerRecord listenerRecord = mListenerRecords.get(pid);
|
||||
@@ -194,6 +260,19 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
// Query the battery state for the device and notify all listeners if there is a change.
|
||||
private void handleBatteryChangeNotification(int deviceId, long eventTime) {
|
||||
synchronized (mLock) {
|
||||
final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
|
||||
if (deviceState == null) {
|
||||
return;
|
||||
}
|
||||
if (deviceState.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(deviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
synchronized (mLock) {
|
||||
pw.println(prefix + TAG + ": " + mListenerRecords.size()
|
||||
@@ -211,6 +290,29 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
|
||||
if (deviceState == null) {
|
||||
return;
|
||||
}
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
if (deviceState.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(deviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A record of a registered battery listener from one process.
|
||||
private class ListenerRecord {
|
||||
final int mPid;
|
||||
@@ -232,4 +334,109 @@ final class BatteryController {
|
||||
+ ", monitored devices=" + Arrays.toString(mMonitoredDevices.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
// Holds the state of an InputDevice for which battery changes are currently being monitored.
|
||||
private class MonitoredDeviceState {
|
||||
private final int mDeviceId;
|
||||
|
||||
private long mLastUpdateTime = 0;
|
||||
private boolean mHasBattery = false;
|
||||
@BatteryState.BatteryStatus
|
||||
private int mBatteryStatus = BatteryState.STATUS_UNKNOWN;
|
||||
private float mBatteryCapacity = Float.NaN;
|
||||
|
||||
@Nullable
|
||||
private UEventListener mUEventListener;
|
||||
|
||||
MonitoredDeviceState(int deviceId) {
|
||||
mDeviceId = deviceId;
|
||||
|
||||
// Load the initial battery state and start monitoring.
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
updateBatteryState(eventTime);
|
||||
}
|
||||
|
||||
// Returns true if the battery state changed since the last time it was updated.
|
||||
boolean updateBatteryState(long eventTime) {
|
||||
mLastUpdateTime = eventTime;
|
||||
|
||||
final boolean batteryPresenceChanged = mHasBattery != hasBattery(mDeviceId);
|
||||
if (batteryPresenceChanged) {
|
||||
mHasBattery = !mHasBattery;
|
||||
if (mHasBattery) {
|
||||
startMonitoring();
|
||||
} else {
|
||||
stopMonitoring();
|
||||
}
|
||||
}
|
||||
|
||||
final int oldStatus = mBatteryStatus;
|
||||
final float oldCapacity = mBatteryCapacity;
|
||||
|
||||
if (mHasBattery) {
|
||||
mBatteryStatus = mNative.getBatteryStatus(mDeviceId);
|
||||
mBatteryCapacity = mNative.getBatteryCapacity(mDeviceId) / 100.f;
|
||||
} else {
|
||||
mBatteryStatus = BatteryState.STATUS_UNKNOWN;
|
||||
mBatteryCapacity = Float.NaN;
|
||||
}
|
||||
|
||||
return batteryPresenceChanged
|
||||
|| mBatteryStatus != oldStatus
|
||||
|| mBatteryCapacity != oldCapacity;
|
||||
}
|
||||
|
||||
private void startMonitoring() {
|
||||
final String batteryPath = mNative.getBatteryDevicePath(mDeviceId);
|
||||
if (batteryPath == null) {
|
||||
return;
|
||||
}
|
||||
mUEventListener = new UEventListener() {
|
||||
@Override
|
||||
void onUEvent(long eventTime) {
|
||||
handleBatteryChangeNotification(mDeviceId, eventTime);
|
||||
}
|
||||
};
|
||||
mUEventManager.addListener(mUEventListener, "DEVPATH=" + batteryPath);
|
||||
}
|
||||
|
||||
// This must be called when the device is no longer being monitored.
|
||||
void stopMonitoring() {
|
||||
if (mUEventListener != null) {
|
||||
mUEventManager.removeListener(mUEventListener);
|
||||
mUEventListener = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// An interface used to change the API of UEventObserver to a more test-friendly format.
|
||||
@VisibleForTesting
|
||||
interface UEventManager {
|
||||
|
||||
@VisibleForTesting
|
||||
abstract class UEventListener {
|
||||
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);
|
||||
}
|
||||
UEventListener.this.onUEvent(eventTime);
|
||||
}
|
||||
};
|
||||
|
||||
abstract void onUEvent(long eventTime);
|
||||
}
|
||||
|
||||
default void addListener(UEventListener listener, String match) {
|
||||
listener.mObserver.startObserving(match);
|
||||
}
|
||||
|
||||
default void removeListener(UEventListener listener) {
|
||||
listener.mObserver.stopObserving();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ public class InputManagerService extends IInputManager.Stub
|
||||
mContext = injector.getContext();
|
||||
mHandler = new InputManagerHandler(injector.getLooper());
|
||||
mNative = injector.getNativeService(this);
|
||||
mBatteryController = new BatteryController(mContext, mNative);
|
||||
mBatteryController = new BatteryController(mContext, mNative, injector.getLooper());
|
||||
|
||||
mUseDevInputEventForAudioJack =
|
||||
mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
|
||||
@@ -561,6 +561,8 @@ public class InputManagerService extends IInputManager.Stub
|
||||
if (mWiredAccessoryCallbacks != null) {
|
||||
mWiredAccessoryCallbacks.systemReady();
|
||||
}
|
||||
|
||||
mBatteryController.systemRunning();
|
||||
}
|
||||
|
||||
private void reloadKeyboardLayouts() {
|
||||
|
||||
@@ -20,16 +20,20 @@ import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.hardware.BatteryState.STATUS_CHARGING
|
||||
import android.hardware.BatteryState.STATUS_FULL
|
||||
import android.hardware.BatteryState.STATUS_UNKNOWN
|
||||
import android.hardware.input.IInputDeviceBatteryListener
|
||||
import android.hardware.input.IInputDevicesChangedListener
|
||||
import android.hardware.input.IInputManager
|
||||
import android.hardware.input.InputDeviceCountryCode
|
||||
import android.hardware.input.InputManager
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.os.test.TestLooper
|
||||
import android.platform.test.annotations.Presubmit
|
||||
import android.view.InputDevice
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import com.android.server.input.BatteryController.UEventManager
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@@ -39,15 +43,27 @@ import org.mockito.ArgumentMatchers.notNull
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.anyLong
|
||||
import org.mockito.Mockito.clearInvocations
|
||||
import org.mockito.Mockito.eq
|
||||
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.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
private fun createInputDevice(deviceId: Int, hasBattery: Boolean = true): InputDevice =
|
||||
InputDevice.Builder()
|
||||
.setId(deviceId)
|
||||
.setName("Device $deviceId")
|
||||
.setDescriptor("descriptor $deviceId")
|
||||
.setExternal(true)
|
||||
.setHasBattery(hasBattery)
|
||||
.setGeneration(0)
|
||||
.build()
|
||||
|
||||
/**
|
||||
* Tests for {@link InputDeviceBatteryController}.
|
||||
*
|
||||
@@ -60,6 +76,7 @@ class BatteryControllerTests {
|
||||
const val PID = 42
|
||||
const val DEVICE_ID = 13
|
||||
const val SECOND_DEVICE_ID = 11
|
||||
const val TIMESTAMP = 123456789L
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
@@ -69,13 +86,19 @@ class BatteryControllerTests {
|
||||
private lateinit var native: NativeInputManagerService
|
||||
@Mock
|
||||
private lateinit var iInputManager: IInputManager
|
||||
@Mock
|
||||
private lateinit var uEventManager: UEventManager
|
||||
|
||||
private lateinit var batteryController: BatteryController
|
||||
private lateinit var context: Context
|
||||
private lateinit var testLooper: TestLooper
|
||||
private lateinit var devicesChangedListener: IInputDevicesChangedListener
|
||||
private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = spy(ContextWrapper(InstrumentationRegistry.getContext()))
|
||||
testLooper = TestLooper()
|
||||
val inputManager = InputManager.resetInstance(iInputManager)
|
||||
`when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
|
||||
`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID, SECOND_DEVICE_ID))
|
||||
@@ -83,17 +106,18 @@ class BatteryControllerTests {
|
||||
`when`(iInputManager.getInputDevice(SECOND_DEVICE_ID))
|
||||
.thenReturn(createInputDevice(SECOND_DEVICE_ID))
|
||||
|
||||
batteryController = BatteryController(context, native)
|
||||
batteryController = BatteryController(context, native, testLooper.looper, uEventManager)
|
||||
batteryController.systemRunning()
|
||||
val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
|
||||
verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture())
|
||||
devicesChangedListener = listenerCaptor.value
|
||||
}
|
||||
|
||||
private fun createInputDevice(deviceId: Int): InputDevice =
|
||||
InputDevice.Builder()
|
||||
.setId(deviceId)
|
||||
.setName("Device $deviceId")
|
||||
.setDescriptor("descriptor $deviceId")
|
||||
.setExternal(true)
|
||||
.setHasBattery(true)
|
||||
.build()
|
||||
private fun notifyDeviceChanged(deviceId: Int) {
|
||||
deviceGenerationMap[deviceId] = deviceGenerationMap[deviceId]?.plus(1) ?: 1
|
||||
val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
|
||||
devicesChangedListener.onInputDevicesChanged(list.toIntArray())
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
@@ -169,4 +193,68 @@ class BatteryControllerTests {
|
||||
verify(listener).onBatteryStateChanged(eq(SECOND_DEVICE_ID), eq(true /*isPresent*/),
|
||||
eq(STATUS_CHARGING), eq(0.78f), anyLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testListenersNotifiedOnUEventNotification() {
|
||||
`when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/test/device1")
|
||||
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
|
||||
val listener = createMockListener()
|
||||
val uEventListener = ArgumentCaptor.forClass(UEventManager.UEventListener::class.java)
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
eq(STATUS_CHARGING), eq(0.78f), anyLong())
|
||||
|
||||
// If the battery state has changed when an UEvent is sent, the listeners are notified.
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
|
||||
uEventListener.value!!.onUEvent(TIMESTAMP)
|
||||
verify(listener).onBatteryStateChanged(DEVICE_ID, true /*isPresent*/, STATUS_CHARGING,
|
||||
0.80f, TIMESTAMP)
|
||||
|
||||
// If the battery state has not changed when an UEvent is sent, the listeners are not
|
||||
// notified.
|
||||
clearInvocations(listener)
|
||||
uEventListener.value!!.onUEvent(TIMESTAMP + 1)
|
||||
verifyNoMoreInteractions(listener)
|
||||
|
||||
batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(uEventManager).removeListener(uEventListener.capture())
|
||||
assertEquals("The same observer must be registered and unregistered",
|
||||
uEventListener.allValues[0], uEventListener.allValues[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBatteryPresenceChanged() {
|
||||
`when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/test/device1")
|
||||
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
|
||||
val listener = createMockListener()
|
||||
val uEventListener = ArgumentCaptor.forClass(UEventManager.UEventListener::class.java)
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
eq(STATUS_CHARGING), eq(0.78f), anyLong())
|
||||
|
||||
// If the battery presence for the InputDevice changes, the listener is notified.
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID))
|
||||
.thenReturn(createInputDevice(DEVICE_ID, hasBattery = false))
|
||||
notifyDeviceChanged(DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(false /*isPresent*/),
|
||||
eq(STATUS_UNKNOWN), eq(Float.NaN), anyLong())
|
||||
// Since the battery is no longer present, the UEventListener should be removed.
|
||||
verify(uEventManager).removeListener(uEventListener.value)
|
||||
|
||||
// If the battery becomes present again, the listener is notified.
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID))
|
||||
.thenReturn(createInputDevice(DEVICE_ID, hasBattery = true))
|
||||
notifyDeviceChanged(DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
verify(listener, times(2)).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
eq(STATUS_CHARGING), eq(0.78f), anyLong())
|
||||
// Ensure that a new UEventListener was added.
|
||||
verify(uEventManager, times(2))
|
||||
.addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user