summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/input/BatteryController.java29
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt102
3 files changed, 131 insertions, 1 deletions
diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java
index 36199debaa6e..9d4f18113555 100644
--- a/services/core/java/com/android/server/input/BatteryController.java
+++ b/services/core/java/com/android/server/input/BatteryController.java
@@ -371,6 +371,17 @@ final class BatteryController {
}
}
+ public void notifyStylusGestureStarted(int deviceId, long eventTime) {
+ synchronized (mLock) {
+ final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
+ if (monitor == null) {
+ return;
+ }
+
+ monitor.onStylusGestureStarted(eventTime);
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
synchronized (mLock) {
final String indent = prefix + " ";
@@ -557,6 +568,8 @@ final class BatteryController {
public void onTimeout(long eventTime) {}
+ public void onStylusGestureStarted(long eventTime) {}
+
// Returns the current battery state that can be used to notify listeners BatteryController.
public State getBatteryStateForReporting() {
return new State(mState);
@@ -600,6 +613,22 @@ final class BatteryController {
}
@Override
+ public void onStylusGestureStarted(long eventTime) {
+ processChangesAndNotify(eventTime, (time) -> {
+ final boolean wasValid = mValidityTimeoutCallback != null;
+ if (!wasValid && mState.capacity == 0.f) {
+ // Handle a special case where the USI device reports a battery capacity of 0
+ // at boot until the first battery update. To avoid wrongly sending out a
+ // battery capacity of 0 if we detect stylus presence before the capacity
+ // is first updated, do not validate the battery state when the state is not
+ // valid and the capacity is 0.
+ return;
+ }
+ markUsiBatteryValid();
+ });
+ }
+
+ @Override
public void onTimeout(long eventTime) {
processChangesAndNotify(eventTime, (time) -> markUsiBatteryInvalid());
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3d990cc7c9c4..08e16763c6aa 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -3055,6 +3055,7 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
@SuppressWarnings("unused")
private void notifyStylusGestureStarted(int deviceId, long eventTime) {
+ mBatteryController.notifyStylusGestureStarted(deviceId, eventTime);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
index c68db3460dac..6590a2b500e4 100644
--- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
@@ -36,6 +36,7 @@ import androidx.test.InstrumentationRegistry
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.USI_BATTERY_VALIDITY_DURATION_MILLIS
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.MatcherAssert.assertThat
@@ -528,10 +529,109 @@ class BatteryControllerTests {
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.moveTimeForward(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))
}
+
+ @Test
+ fun testStylusPresenceExtendsValidUsiBatteryState() {
+ `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"))
+
+ // There is a UEvent signaling a battery change. The battery state is now valid.
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ 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))
+
+ // Stylus presence is detected before the validity timeout expires.
+ testLooper.moveTimeForward(100)
+ testLooper.dispatchAll()
+ batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+ // Ensure that timeout was extended, and the battery state is now valid for longer.
+ testLooper.moveTimeForward(USI_BATTERY_VALIDITY_DURATION_MILLIS - 100)
+ testLooper.dispatchAll()
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_DISCHARGING, capacity = 0.78f))
+
+ // Ensure the validity period expires after the expected amount of time.
+ testLooper.moveTimeForward(100)
+ testLooper.dispatchNext()
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+ }
+
+ @Test
+ fun testStylusPresenceMakesUsiBatteryStateValid() {
+ `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"))
+
+ // The USI battery state is initially invalid.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // A stylus presence is detected. This validates the battery state.
+ batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, 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))
+ }
+
+ @Test
+ fun testStylusPresenceDoesNotMakeUsiBatteryStateValidAtBoot() {
+ `when`(native.getBatteryDevicePath(USI_DEVICE_ID)).thenReturn("/sys/dev/usi_device")
+ // At boot, the USI device always reports a capacity value of 0.
+ `when`(native.getBatteryStatus(USI_DEVICE_ID)).thenReturn(STATUS_UNKNOWN)
+ `when`(native.getBatteryCapacity(USI_DEVICE_ID)).thenReturn(0)
+
+ 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"))
+
+ // The USI battery state is initially invalid.
+ val listener = createMockListener()
+ batteryController.registerBatteryListener(USI_DEVICE_ID, listener, PID)
+ listener.verifyNotified(isInvalidBatteryState(USI_DEVICE_ID))
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // Since the capacity reported is 0, stylus presence does not validate the battery state.
+ batteryController.notifyStylusGestureStarted(USI_DEVICE_ID, TIMESTAMP)
+
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ isInvalidBatteryState(USI_DEVICE_ID))
+
+ // However, if a UEvent reports a battery capacity of 0, the battery state is now valid.
+ uEventListener.value!!.onBatteryUEvent(TIMESTAMP)
+ listener.verifyNotified(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f)
+ assertThat("battery state matches", batteryController.getBatteryState(USI_DEVICE_ID),
+ matchesState(USI_DEVICE_ID, status = STATUS_UNKNOWN, capacity = 0f))
+ }
}