Merge "Introduce monitoring behavior changes for USI devices (2/n)"
This commit is contained in:
committed by
Android (Google) Code Review
commit
47300feb4d
@@ -65,6 +65,8 @@ final class BatteryController {
|
||||
|
||||
@VisibleForTesting
|
||||
static final long POLLING_PERIOD_MILLIS = 10_000; // 10 seconds
|
||||
@VisibleForTesting
|
||||
static final long USI_BATTERY_VALIDITY_DURATION_MILLIS = 60 * 60_000; // 1 hour
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private final Context mContext;
|
||||
@@ -336,6 +338,17 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMonitorTimeout(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
|
||||
if (monitor == null) {
|
||||
return;
|
||||
}
|
||||
final long updateTime = SystemClock.uptimeMillis();
|
||||
monitor.onTimeout(updateTime);
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the current battery state of an input device. */
|
||||
public IInputDeviceBatteryState getBatteryState(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
@@ -448,7 +461,7 @@ final class BatteryController {
|
||||
|
||||
// Holds the state of an InputDevice for which battery changes are currently being monitored.
|
||||
private class DeviceMonitor {
|
||||
private final State mState;
|
||||
protected final State mState;
|
||||
// Represents whether the input device has a sysfs battery node.
|
||||
protected boolean mHasBattery = false;
|
||||
|
||||
@@ -463,7 +476,7 @@ final class BatteryController {
|
||||
configureDeviceMonitor(eventTime);
|
||||
}
|
||||
|
||||
private void processChangesAndNotify(long eventTime, Consumer<Long> changes) {
|
||||
protected void processChangesAndNotify(long eventTime, Consumer<Long> changes) {
|
||||
final State oldState = getBatteryStateForReporting();
|
||||
changes.accept(eventTime);
|
||||
final State newState = getBatteryStateForReporting();
|
||||
@@ -521,7 +534,7 @@ final class BatteryController {
|
||||
stopMonitoring();
|
||||
}
|
||||
|
||||
private void updateBatteryStateFromNative(long eventTime) {
|
||||
protected void updateBatteryStateFromNative(long eventTime) {
|
||||
mState.updateIfChanged(
|
||||
queryBatteryStateFromNative(mState.deviceId, eventTime, mHasBattery));
|
||||
}
|
||||
@@ -542,6 +555,8 @@ final class BatteryController {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onTimeout(long eventTime) {}
|
||||
|
||||
// Returns the current battery state that can be used to notify listeners BatteryController.
|
||||
public State getBatteryStateForReporting() {
|
||||
return new State(mState);
|
||||
@@ -560,10 +575,71 @@ final class BatteryController {
|
||||
// Universal Stylus Initiative (USI) protocol.
|
||||
private class UsiDeviceMonitor extends DeviceMonitor {
|
||||
|
||||
// For USI devices, we only treat the battery state as valid for a fixed amount of time
|
||||
// after receiving a battery update. Once the timeout has passed, we signal to all listeners
|
||||
// that there is no longer a battery present for the device. The battery state is valid
|
||||
// as long as this callback is non-null.
|
||||
@Nullable
|
||||
private Runnable mValidityTimeoutCallback;
|
||||
|
||||
UsiDeviceMonitor(int deviceId) {
|
||||
super(deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPoll(long eventTime) {
|
||||
// Disregard polling for USI devices.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUEvent(long eventTime) {
|
||||
processChangesAndNotify(eventTime, (time) -> {
|
||||
updateBatteryStateFromNative(time);
|
||||
markUsiBatteryValid();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(long eventTime) {
|
||||
processChangesAndNotify(eventTime, (time) -> markUsiBatteryInvalid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfiguration(long eventTime) {
|
||||
super.onConfiguration(eventTime);
|
||||
|
||||
if (!mHasBattery) {
|
||||
throw new IllegalStateException(
|
||||
"UsiDeviceMonitor: USI devices are always expected to "
|
||||
+ "report a valid battery, but no battery was detected!");
|
||||
}
|
||||
}
|
||||
|
||||
private void markUsiBatteryValid() {
|
||||
if (mValidityTimeoutCallback != null) {
|
||||
mHandler.removeCallbacks(mValidityTimeoutCallback);
|
||||
} else {
|
||||
final int deviceId = mState.deviceId;
|
||||
mValidityTimeoutCallback =
|
||||
() -> BatteryController.this.handleMonitorTimeout(deviceId);
|
||||
}
|
||||
mHandler.postDelayed(mValidityTimeoutCallback, USI_BATTERY_VALIDITY_DURATION_MILLIS);
|
||||
}
|
||||
|
||||
private void markUsiBatteryInvalid() {
|
||||
if (mValidityTimeoutCallback == null) {
|
||||
return;
|
||||
}
|
||||
mHandler.removeCallbacks(mValidityTimeoutCallback);
|
||||
mValidityTimeoutCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getBatteryStateForReporting() {
|
||||
return mValidityTimeoutCallback != null
|
||||
? new State(mState) : new State(mState.deviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPolling() {
|
||||
// Do not poll the battery state for USI devices.
|
||||
@@ -575,6 +651,12 @@ final class BatteryController {
|
||||
// Do not remove the battery monitor for USI devices.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString()
|
||||
+ ", UsiStateIsValid=" + (mValidityTimeoutCallback != null);
|
||||
}
|
||||
}
|
||||
|
||||
// An interface used to change the API of UEventObserver to a more test-friendly format.
|
||||
@@ -635,7 +717,7 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
private void reset(int deviceId) {
|
||||
public void reset(int deviceId) {
|
||||
initialize(deviceId, 0 /*updateTime*/, false /*isPresent*/, BatteryState.STATUS_UNKNOWN,
|
||||
Float.NaN /*capacity*/);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.input
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.hardware.BatteryState.STATUS_CHARGING
|
||||
import android.hardware.BatteryState.STATUS_DISCHARGING
|
||||
import android.hardware.BatteryState.STATUS_FULL
|
||||
import android.hardware.BatteryState.STATUS_UNKNOWN
|
||||
import android.hardware.input.IInputDeviceBatteryListener
|
||||
@@ -484,4 +485,53 @@ class BatteryControllerTests {
|
||||
testLooper.moveTimeForward(POLLING_PERIOD_MILLIS)
|
||||
assertFalse("There should be no polling callbacks posted to the handler", testLooper.isIdle)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testExpectedFlowForUsiBattery() {
|
||||
`when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
|
||||
`when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_DISCHARGING)
|
||||
`when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(78)
|
||||
|
||||
addInputDevice(USI_DEVICE_ID, supportsUsi = true)
|
||||
testLooper.dispatchNext()
|
||||
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
|
||||
verify(uEventManager)
|
||||
.addListener(uEventListener.capture(), eq("DEVPATH=/dev/usi_device"))
|
||||
|
||||
// A USI device's battery state is not valid until the first UEvent notification.
|
||||
// Add a listener, and ensure it is notified that the battery state is not present.
|
||||
val listener = createMockListener()
|
||||
batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
|
||||
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
|
||||
|
||||
// Ensure that querying for battery state also returns the same invalid result.
|
||||
assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
|
||||
isInvalidBatteryState(USI_DEVICE_ID))
|
||||
|
||||
// There is a UEvent signaling a battery change. The battery state is now valid.
|
||||
uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
|
||||
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f)
|
||||
assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
|
||||
matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
|
||||
|
||||
// There is another UEvent notification. The battery state is now updated.
|
||||
`when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(64)
|
||||
uEventListener.value!!.onBatteryUEvent(TIMESTAMP + 1)
|
||||
listener.verifyNotified(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f)
|
||||
assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
|
||||
matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
|
||||
|
||||
// The battery state is still valid after a millisecond.
|
||||
testLooper.moveTimeForward(1)
|
||||
testLooper.dispatchAll()
|
||||
assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
|
||||
matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.64f))
|
||||
|
||||
// The battery is no longer present after the timeout expires.
|
||||
testLooper.moveTimeForward(BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS - 1)
|
||||
testLooper.dispatchNext()
|
||||
listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID), times(2))
|
||||
assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
|
||||
isInvalidBatteryState(USI_DEVICE_ID))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user