Merge changes from topic "aidl-iinputdevicebatterystate"
* changes: Rename MonitoredDeviceState to DeviceMonitor Add AIDL definiton for IInputDeviceBatteryState
This commit is contained in:
committed by
Android (Google) Code Review
commit
b9cd74abc1
@@ -16,14 +16,14 @@
|
||||
|
||||
package android.hardware.input;
|
||||
|
||||
import android.hardware.input.IInputDeviceBatteryState;
|
||||
|
||||
/** @hide */
|
||||
oneway interface IInputDeviceBatteryListener {
|
||||
|
||||
/**
|
||||
* Called when there is a change in battery state for a monitored device. This will be called
|
||||
* immediately after the listener is successfully registered for a new device via IInputManager.
|
||||
* The parameters are values exposed through {@link android.hardware.BatteryState}.
|
||||
*/
|
||||
void onBatteryStateChanged(int deviceId, boolean isBatteryPresent, int status, float capacity,
|
||||
long eventTime);
|
||||
void onBatteryStateChanged(in IInputDeviceBatteryState batteryState);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.hardware.input;
|
||||
|
||||
/** @hide */
|
||||
@JavaDerive(equals=true)
|
||||
parcelable IInputDeviceBatteryState {
|
||||
/** The deviceId of the input device that this battery state is associated with. */
|
||||
int deviceId;
|
||||
|
||||
/**
|
||||
* The timestamp of the last time the battery state was updated, in the
|
||||
* {@link SystemClock.uptimeMillis()} time base.
|
||||
*/
|
||||
long updateTime;
|
||||
|
||||
/** Whether the input device has a battery. */
|
||||
boolean isPresent;
|
||||
|
||||
/** The battery status for this input device. */
|
||||
@JavaPassthrough(annotation="@android.hardware.BatteryState.BatteryStatus")
|
||||
int status;
|
||||
|
||||
/** The battery capacity for this input device, in a range between 0 and 1. */
|
||||
float capacity;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import android.hardware.input.InputDeviceIdentifier;
|
||||
import android.hardware.input.KeyboardLayout;
|
||||
import android.hardware.input.IInputDevicesChangedListener;
|
||||
import android.hardware.input.IInputDeviceBatteryListener;
|
||||
import android.hardware.input.IInputDeviceBatteryState;
|
||||
import android.hardware.input.ITabletModeChangedListener;
|
||||
import android.hardware.input.TouchCalibration;
|
||||
import android.os.CombinedVibration;
|
||||
@@ -110,9 +111,7 @@ interface IInputManager {
|
||||
boolean registerVibratorStateListener(int deviceId, in IVibratorStateListener listener);
|
||||
boolean unregisterVibratorStateListener(int deviceId, in IVibratorStateListener listener);
|
||||
|
||||
// Input device battery query.
|
||||
int getBatteryStatus(int deviceId);
|
||||
int getBatteryCapacity(int deviceId);
|
||||
IInputDeviceBatteryState getBatteryState(int deviceId);
|
||||
|
||||
void setPointerIconType(int typeId);
|
||||
void setCustomPointerIcon(in PointerIcon icon);
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.hardware.input;
|
||||
|
||||
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
|
||||
import static android.os.IInputConstants.INVALID_BATTERY_CAPACITY;
|
||||
|
||||
import android.hardware.BatteryState;
|
||||
|
||||
/**
|
||||
* Battery implementation for input devices.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class InputDeviceBatteryState extends BatteryState {
|
||||
private static final float NULL_BATTERY_CAPACITY = Float.NaN;
|
||||
|
||||
private final InputManager mInputManager;
|
||||
private final int mDeviceId;
|
||||
private final boolean mHasBattery;
|
||||
|
||||
InputDeviceBatteryState(InputManager inputManager, int deviceId, boolean hasBattery) {
|
||||
mInputManager = inputManager;
|
||||
mDeviceId = deviceId;
|
||||
mHasBattery = hasBattery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent() {
|
||||
return mHasBattery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStatus() {
|
||||
if (!mHasBattery) {
|
||||
return BATTERY_STATUS_UNKNOWN;
|
||||
}
|
||||
return mInputManager.getBatteryStatus(mDeviceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCapacity() {
|
||||
if (mHasBattery) {
|
||||
int capacity = mInputManager.getBatteryCapacity(mDeviceId);
|
||||
if (capacity != INVALID_BATTERY_CAPACITY) {
|
||||
return (float) capacity / 100.0f;
|
||||
}
|
||||
}
|
||||
return NULL_BATTERY_CAPACITY;
|
||||
}
|
||||
}
|
||||
@@ -1307,32 +1307,6 @@ public final class InputManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the battery status of the input device
|
||||
* @param deviceId The input device ID
|
||||
* @hide
|
||||
*/
|
||||
public int getBatteryStatus(int deviceId) {
|
||||
try {
|
||||
return mIm.getBatteryStatus(deviceId);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining battery capacity of the input device
|
||||
* @param deviceId The input device ID
|
||||
* @hide
|
||||
*/
|
||||
public int getBatteryCapacity(int deviceId) {
|
||||
try {
|
||||
return mIm.getBatteryCapacity(deviceId);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a runtime association between the input port and the display port. This overrides any
|
||||
* static associations.
|
||||
@@ -1622,8 +1596,17 @@ public final class InputManager {
|
||||
* @return The battery, never null.
|
||||
* @hide
|
||||
*/
|
||||
public InputDeviceBatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) {
|
||||
return new InputDeviceBatteryState(this, deviceId, hasBattery);
|
||||
@NonNull
|
||||
public BatteryState getInputDeviceBatteryState(int deviceId, boolean hasBattery) {
|
||||
if (!hasBattery) {
|
||||
return new LocalBatteryState();
|
||||
}
|
||||
try {
|
||||
final IInputDeviceBatteryState state = mIm.getBatteryState(deviceId);
|
||||
return new LocalBatteryState(state.isPresent, state.status, state.capacity);
|
||||
} catch (RemoteException ex) {
|
||||
throw ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1767,8 +1750,8 @@ public final class InputManager {
|
||||
listenersForDevice.mDelegates.add(delegate);
|
||||
|
||||
// Notify the listener immediately if we already have the latest battery state.
|
||||
if (listenersForDevice.mLatestBatteryState != null) {
|
||||
delegate.notifyBatteryStateChanged(listenersForDevice.mLatestBatteryState);
|
||||
if (listenersForDevice.mInputDeviceBatteryState != null) {
|
||||
delegate.notifyBatteryStateChanged(listenersForDevice.mInputDeviceBatteryState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1952,20 +1935,21 @@ public final class InputManager {
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of the android.hardware.BatteryState interface used to report the battery
|
||||
// state via the InputDevice#getBatteryState() and InputDeviceBatteryListener interfaces.
|
||||
private static final class LocalBatteryState extends BatteryState {
|
||||
final int mDeviceId;
|
||||
final boolean mIsPresent;
|
||||
final int mStatus;
|
||||
final float mCapacity;
|
||||
final long mEventTime;
|
||||
private final boolean mIsPresent;
|
||||
private final int mStatus;
|
||||
private final float mCapacity;
|
||||
|
||||
LocalBatteryState(int deviceId, boolean isPresent, int status, float capacity,
|
||||
long eventTime) {
|
||||
mDeviceId = deviceId;
|
||||
LocalBatteryState() {
|
||||
this(false /*isPresent*/, BatteryState.STATUS_UNKNOWN, Float.NaN /*capacity*/);
|
||||
}
|
||||
|
||||
LocalBatteryState(boolean isPresent, int status, float capacity) {
|
||||
mIsPresent = isPresent;
|
||||
mStatus = status;
|
||||
mCapacity = capacity;
|
||||
mEventTime = eventTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1986,7 +1970,7 @@ public final class InputManager {
|
||||
|
||||
private static final class RegisteredBatteryListeners {
|
||||
final List<InputDeviceBatteryListenerDelegate> mDelegates = new ArrayList<>();
|
||||
LocalBatteryState mLatestBatteryState;
|
||||
IInputDeviceBatteryState mInputDeviceBatteryState;
|
||||
}
|
||||
|
||||
private static final class InputDeviceBatteryListenerDelegate {
|
||||
@@ -1998,27 +1982,24 @@ public final class InputManager {
|
||||
mExecutor = executor;
|
||||
}
|
||||
|
||||
void notifyBatteryStateChanged(LocalBatteryState batteryState) {
|
||||
void notifyBatteryStateChanged(IInputDeviceBatteryState state) {
|
||||
mExecutor.execute(() ->
|
||||
mListener.onBatteryStateChanged(batteryState.mDeviceId, batteryState.mEventTime,
|
||||
batteryState));
|
||||
mListener.onBatteryStateChanged(state.deviceId, state.updateTime,
|
||||
new LocalBatteryState(state.isPresent, state.status, state.capacity)));
|
||||
}
|
||||
}
|
||||
|
||||
private class LocalInputDeviceBatteryListener extends IInputDeviceBatteryListener.Stub {
|
||||
@Override
|
||||
public void onBatteryStateChanged(int deviceId, boolean isBatteryPresent, int status,
|
||||
float capacity, long eventTime) {
|
||||
public void onBatteryStateChanged(IInputDeviceBatteryState state) {
|
||||
synchronized (mBatteryListenersLock) {
|
||||
if (mBatteryListeners == null) return;
|
||||
final RegisteredBatteryListeners entry = mBatteryListeners.get(deviceId);
|
||||
final RegisteredBatteryListeners entry = mBatteryListeners.get(state.deviceId);
|
||||
if (entry == null) return;
|
||||
|
||||
entry.mLatestBatteryState =
|
||||
new LocalBatteryState(
|
||||
deviceId, isBatteryPresent, status, capacity, eventTime);
|
||||
entry.mInputDeviceBatteryState = state;
|
||||
for (InputDeviceBatteryListenerDelegate delegate : entry.mDelegates) {
|
||||
delegate.notifyBatteryStateChanged(entry.mLatestBatteryState);
|
||||
delegate.notifyBatteryStateChanged(entry.mInputDeviceBatteryState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,9 +91,6 @@ public final class InputDevice implements Parcelable {
|
||||
@GuardedBy("mMotionRanges")
|
||||
private SensorManager mSensorManager;
|
||||
|
||||
@GuardedBy("mMotionRanges")
|
||||
private BatteryState mBatteryState;
|
||||
|
||||
@GuardedBy("mMotionRanges")
|
||||
private LightsManager mLightsManager;
|
||||
|
||||
@@ -1058,10 +1055,7 @@ public final class InputDevice implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public BatteryState getBatteryState() {
|
||||
if (mBatteryState == null) {
|
||||
mBatteryState = InputManager.getInstance().getInputDeviceBatteryState(mId, mHasBattery);
|
||||
}
|
||||
return mBatteryState;
|
||||
return InputManager.getInstance().getInputDeviceBatteryState(mId, mHasBattery);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -112,7 +112,13 @@ class InputDeviceBatteryListenerTest {
|
||||
capacity: Float = 1.0f,
|
||||
eventTime: Long = 12345L
|
||||
) {
|
||||
registeredListener!!.onBatteryStateChanged(deviceId, isPresent, status, capacity, eventTime)
|
||||
registeredListener!!.onBatteryStateChanged(IInputDeviceBatteryState().apply {
|
||||
this.deviceId = deviceId
|
||||
this.updateTime = eventTime
|
||||
this.isPresent = isPresent
|
||||
this.status = status
|
||||
this.capacity = capacity
|
||||
})
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.BatteryState;
|
||||
import android.hardware.input.IInputDeviceBatteryListener;
|
||||
import android.hardware.input.IInputDeviceBatteryState;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
@@ -47,8 +48,13 @@ import java.util.Set;
|
||||
/**
|
||||
* A thread-safe component of {@link InputManagerService} responsible for managing the battery state
|
||||
* of input devices.
|
||||
*
|
||||
* Interactions with BatteryController can happen on several threads, including Binder threads, the
|
||||
* {@link UEventObserver}'s thread, or its own Handler thread, among others. All public methods, and
|
||||
* private methods prefixed with "handle-" (e.g. {@link #handleListeningProcessDied(int)}),
|
||||
* serve as entry points for these threads.
|
||||
*/
|
||||
final class BatteryController implements InputManager.InputDeviceListener {
|
||||
final class BatteryController {
|
||||
private static final String TAG = BatteryController.class.getSimpleName();
|
||||
|
||||
// To enable these logs, run:
|
||||
@@ -69,10 +75,9 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<Integer, ListenerRecord> mListenerRecords = new ArrayMap<>();
|
||||
|
||||
// Maps a deviceId that is being monitored to the battery state for the device.
|
||||
// This must be kept in sync with {@link #mListenerRecords}.
|
||||
// Maps a deviceId that is being monitored to the monitor for the battery state of the device.
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayMap<Integer, MonitoredDeviceState> mMonitoredDeviceStates = new ArrayMap<>();
|
||||
private final ArrayMap<Integer, DeviceMonitor> mDeviceMonitors = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsPolling = false;
|
||||
@@ -92,9 +97,9 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
mUEventManager = uEventManager;
|
||||
}
|
||||
|
||||
void systemRunning() {
|
||||
public void systemRunning() {
|
||||
Objects.requireNonNull(mContext.getSystemService(InputManager.class))
|
||||
.registerInputDeviceListener(this, mHandler);
|
||||
.registerInputDeviceListener(mInputDeviceListener, mHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +107,7 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
* state.
|
||||
*/
|
||||
@BinderThread
|
||||
void registerBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
|
||||
public void registerBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
|
||||
int pid) {
|
||||
synchronized (mLock) {
|
||||
ListenerRecord listenerRecord = mListenerRecords.get(pid);
|
||||
@@ -131,11 +136,11 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
+ " is already monitoring deviceId " + deviceId);
|
||||
}
|
||||
|
||||
MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
|
||||
if (deviceState == null) {
|
||||
DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
|
||||
if (monitor == null) {
|
||||
// This is the first listener that is monitoring this device.
|
||||
deviceState = new MonitoredDeviceState(deviceId);
|
||||
mMonitoredDeviceStates.put(deviceId, deviceState);
|
||||
monitor = new DeviceMonitor(deviceId);
|
||||
mDeviceMonitors.put(deviceId, monitor);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
@@ -144,36 +149,35 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
|
||||
updatePollingLocked(true /*delayStart*/);
|
||||
notifyBatteryListener(listenerRecord, deviceState);
|
||||
notifyBatteryListener(listenerRecord, monitor.getBatteryStateForReporting());
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyBatteryListener(ListenerRecord listenerRecord,
|
||||
MonitoredDeviceState deviceState) {
|
||||
private static void notifyBatteryListener(ListenerRecord listenerRecord, State state) {
|
||||
try {
|
||||
listenerRecord.mListener.onBatteryStateChanged(
|
||||
deviceState.mDeviceId,
|
||||
deviceState.mHasBattery,
|
||||
deviceState.mBatteryStatus,
|
||||
deviceState.mBatteryCapacity,
|
||||
deviceState.mLastUpdateTime);
|
||||
listenerRecord.mListener.onBatteryStateChanged(state);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to notify listener", e);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Notified battery listener from pid " + listenerRecord.mPid
|
||||
+ " of state of deviceId " + state.deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void notifyAllListenersForDeviceLocked(MonitoredDeviceState deviceState) {
|
||||
private void notifyAllListenersForDeviceLocked(State state) {
|
||||
if (DEBUG) Slog.d(TAG, "Notifying all listeners of battery state: " + state);
|
||||
mListenerRecords.forEach((pid, listenerRecord) -> {
|
||||
if (listenerRecord.mMonitoredDevices.contains(deviceState.mDeviceId)) {
|
||||
notifyBatteryListener(listenerRecord, deviceState);
|
||||
if (listenerRecord.mMonitoredDevices.contains(state.deviceId)) {
|
||||
notifyBatteryListener(listenerRecord, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private void updatePollingLocked(boolean delayStart) {
|
||||
if (mMonitoredDeviceStates.isEmpty() || !mIsInteractive) {
|
||||
if (mDeviceMonitors.isEmpty() || !mIsInteractive) {
|
||||
// Stop polling.
|
||||
mIsPolling = false;
|
||||
mHandler.removeCallbacks(this::handlePollEvent);
|
||||
@@ -196,8 +200,8 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private MonitoredDeviceState getDeviceStateOrThrowLocked(int deviceId) {
|
||||
return Objects.requireNonNull(mMonitoredDeviceStates.get(deviceId),
|
||||
private DeviceMonitor getDeviceMonitorOrThrowLocked(int deviceId) {
|
||||
return Objects.requireNonNull(mDeviceMonitors.get(deviceId),
|
||||
"Maps are out of sync: Cannot find device state for deviceId " + deviceId);
|
||||
}
|
||||
|
||||
@@ -207,8 +211,8 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
* removed.
|
||||
*/
|
||||
@BinderThread
|
||||
void unregisterBatteryListener(int deviceId, @NonNull IInputDeviceBatteryListener listener,
|
||||
int pid) {
|
||||
public void unregisterBatteryListener(int deviceId,
|
||||
@NonNull IInputDeviceBatteryListener listener, int pid) {
|
||||
synchronized (mLock) {
|
||||
final ListenerRecord listenerRecord = mListenerRecords.get(pid);
|
||||
if (listenerRecord == null) {
|
||||
@@ -247,9 +251,9 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
|
||||
if (!hasRegisteredListenerForDeviceLocked(deviceId)) {
|
||||
// There are no more listeners monitoring this device.
|
||||
final MonitoredDeviceState deviceState = getDeviceStateOrThrowLocked(deviceId);
|
||||
deviceState.stopMonitoring();
|
||||
mMonitoredDeviceStates.remove(deviceId);
|
||||
final DeviceMonitor monitor = getDeviceMonitorOrThrowLocked(deviceId);
|
||||
monitor.stopMonitoring();
|
||||
mDeviceMonitors.remove(deviceId);
|
||||
}
|
||||
|
||||
if (listenerRecord.mMonitoredDevices.isEmpty()) {
|
||||
@@ -288,15 +292,14 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
}
|
||||
|
||||
// Query the battery state for the device and notify all listeners if there is a change.
|
||||
private void handleBatteryChangeNotification(int deviceId, long eventTime) {
|
||||
private void handleUEventNotification(int deviceId, long eventTime) {
|
||||
synchronized (mLock) {
|
||||
final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
|
||||
if (deviceState == null) {
|
||||
final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
|
||||
if (monitor == null) {
|
||||
return;
|
||||
}
|
||||
if (deviceState.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(deviceState);
|
||||
if (monitor.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(monitor.getBatteryStateForReporting());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,11 +310,11 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
return;
|
||||
}
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
mMonitoredDeviceStates.forEach((deviceId, deviceState) -> {
|
||||
mDeviceMonitors.forEach((deviceId, monitor) -> {
|
||||
// Re-acquire lock in the lambda to silence error-prone build warnings.
|
||||
synchronized (mLock) {
|
||||
if (deviceState.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(deviceState);
|
||||
if (monitor.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(monitor.getBatteryStateForReporting());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -319,62 +322,90 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
}
|
||||
|
||||
void onInteractiveChanged(boolean interactive) {
|
||||
/** Gets the current battery state of an input device. */
|
||||
public IInputDeviceBatteryState getBatteryState(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
final long updateTime = SystemClock.uptimeMillis();
|
||||
final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
|
||||
if (monitor == null) {
|
||||
// The input device's battery is not being monitored by any listener.
|
||||
return queryBatteryStateFromNative(deviceId, updateTime);
|
||||
}
|
||||
// Force the battery state to update, and notify listeners if necessary.
|
||||
final boolean stateChanged = monitor.updateBatteryState(updateTime);
|
||||
final State state = monitor.getBatteryStateForReporting();
|
||||
if (stateChanged) {
|
||||
notifyAllListenersForDeviceLocked(state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
public void onInteractiveChanged(boolean interactive) {
|
||||
synchronized (mLock) {
|
||||
mIsInteractive = interactive;
|
||||
updatePollingLocked(false /*delayStart*/);
|
||||
}
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
synchronized (mLock) {
|
||||
pw.println(prefix + TAG + ": "
|
||||
+ mListenerRecords.size() + " battery listeners"
|
||||
+ ", Polling = " + mIsPolling
|
||||
final String indent = prefix + " ";
|
||||
final String indent2 = indent + " ";
|
||||
|
||||
pw.println(prefix + TAG + ":");
|
||||
pw.println(indent + "State: Polling = " + mIsPolling
|
||||
+ ", Interactive = " + mIsInteractive);
|
||||
|
||||
pw.println(indent + "Listeners: " + mListenerRecords.size() + " battery listeners");
|
||||
for (int i = 0; i < mListenerRecords.size(); i++) {
|
||||
pw.println(prefix + " " + i + ": " + mListenerRecords.valueAt(i));
|
||||
pw.println(indent2 + i + ": " + mListenerRecords.valueAt(i));
|
||||
}
|
||||
|
||||
pw.println(indent + "Device Monitors: " + mDeviceMonitors.size() + " monitors");
|
||||
for (int i = 0; i < mDeviceMonitors.size(); i++) {
|
||||
pw.println(indent2 + i + ": " + mDeviceMonitors.valueAt(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
void monitor() {
|
||||
public void monitor() {
|
||||
synchronized (mLock) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {}
|
||||
private final InputManager.InputDeviceListener mInputDeviceListener =
|
||||
new InputManager.InputDeviceListener() {
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {}
|
||||
@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);
|
||||
@Override
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
synchronized (mLock) {
|
||||
final DeviceMonitor monitor = mDeviceMonitors.get(deviceId);
|
||||
if (monitor == null) {
|
||||
return;
|
||||
}
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
if (monitor.updateBatteryState(eventTime)) {
|
||||
notifyAllListenersForDeviceLocked(monitor.getBatteryStateForReporting());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// A record of a registered battery listener from one process.
|
||||
private class ListenerRecord {
|
||||
final int mPid;
|
||||
final IInputDeviceBatteryListener mListener;
|
||||
final IBinder.DeathRecipient mDeathRecipient;
|
||||
public final int mPid;
|
||||
public final IInputDeviceBatteryListener mListener;
|
||||
public final IBinder.DeathRecipient mDeathRecipient;
|
||||
// The set of deviceIds that are currently being monitored by this listener.
|
||||
final Set<Integer> mMonitoredDevices;
|
||||
public final Set<Integer> mMonitoredDevices;
|
||||
|
||||
ListenerRecord(int pid, IInputDeviceBatteryListener listener) {
|
||||
mPid = pid;
|
||||
@@ -390,21 +421,27 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
}
|
||||
|
||||
// Holds the state of an InputDevice for which battery changes are currently being monitored.
|
||||
private class MonitoredDeviceState {
|
||||
private final int mDeviceId;
|
||||
// Queries the battery state of an input device from native code.
|
||||
private State queryBatteryStateFromNative(int deviceId, long updateTime) {
|
||||
final boolean isPresent = hasBattery(deviceId);
|
||||
return new State(
|
||||
deviceId,
|
||||
updateTime,
|
||||
isPresent,
|
||||
isPresent ? mNative.getBatteryStatus(deviceId) : BatteryState.STATUS_UNKNOWN,
|
||||
isPresent ? mNative.getBatteryCapacity(deviceId) / 100.f : Float.NaN);
|
||||
}
|
||||
|
||||
private long mLastUpdateTime = 0;
|
||||
private boolean mHasBattery = false;
|
||||
@BatteryState.BatteryStatus
|
||||
private int mBatteryStatus = BatteryState.STATUS_UNKNOWN;
|
||||
private float mBatteryCapacity = Float.NaN;
|
||||
// Holds the state of an InputDevice for which battery changes are currently being monitored.
|
||||
private class DeviceMonitor {
|
||||
@NonNull
|
||||
private State mState;
|
||||
|
||||
@Nullable
|
||||
private UEventListener mUEventListener;
|
||||
|
||||
MonitoredDeviceState(int deviceId) {
|
||||
mDeviceId = deviceId;
|
||||
DeviceMonitor(int deviceId) {
|
||||
mState = new State(deviceId);
|
||||
|
||||
// Load the initial battery state and start monitoring.
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
@@ -412,56 +449,57 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
|
||||
// Returns true if the battery state changed since the last time it was updated.
|
||||
boolean updateBatteryState(long eventTime) {
|
||||
mLastUpdateTime = eventTime;
|
||||
public boolean updateBatteryState(long updateTime) {
|
||||
mState.updateTime = updateTime;
|
||||
|
||||
final boolean batteryPresenceChanged = mHasBattery != hasBattery(mDeviceId);
|
||||
if (batteryPresenceChanged) {
|
||||
mHasBattery = !mHasBattery;
|
||||
if (mHasBattery) {
|
||||
final State updatedState = queryBatteryStateFromNative(mState.deviceId, updateTime);
|
||||
if (mState.equals(updatedState)) {
|
||||
return false;
|
||||
}
|
||||
if (mState.isPresent != updatedState.isPresent) {
|
||||
if (updatedState.isPresent) {
|
||||
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;
|
||||
mState = updatedState;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startMonitoring() {
|
||||
final String batteryPath = mNative.getBatteryDevicePath(mDeviceId);
|
||||
final String batteryPath = mNative.getBatteryDevicePath(mState.deviceId);
|
||||
if (batteryPath == null) {
|
||||
return;
|
||||
}
|
||||
final int deviceId = mState.deviceId;
|
||||
mUEventListener = new UEventListener() {
|
||||
@Override
|
||||
void onUEvent(long eventTime) {
|
||||
handleBatteryChangeNotification(mDeviceId, eventTime);
|
||||
public void onUEvent(long eventTime) {
|
||||
handleUEventNotification(deviceId, eventTime);
|
||||
}
|
||||
};
|
||||
mUEventManager.addListener(mUEventListener, "DEVPATH=" + batteryPath);
|
||||
}
|
||||
|
||||
// This must be called when the device is no longer being monitored.
|
||||
void stopMonitoring() {
|
||||
public void stopMonitoring() {
|
||||
if (mUEventListener != null) {
|
||||
mUEventManager.removeListener(mUEventListener);
|
||||
mUEventListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current battery state that can be used to notify listeners BatteryController.
|
||||
public State getBatteryStateForReporting() {
|
||||
return new State(mState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "state=" + mState
|
||||
+ ", uEventListener=" + (mUEventListener != null ? "added" : "none");
|
||||
}
|
||||
}
|
||||
|
||||
// An interface used to change the API of UEventObserver to a more test-friendly format.
|
||||
@@ -483,7 +521,7 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
}
|
||||
};
|
||||
|
||||
abstract void onUEvent(long eventTime);
|
||||
public abstract void onUEvent(long eventTime);
|
||||
}
|
||||
|
||||
default void addListener(UEventListener listener, String match) {
|
||||
@@ -494,4 +532,37 @@ final class BatteryController implements InputManager.InputDeviceListener {
|
||||
listener.mObserver.stopObserving();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper class that adds copying and printing functionality to IInputDeviceBatteryState.
|
||||
private static class State extends IInputDeviceBatteryState {
|
||||
|
||||
State(int deviceId) {
|
||||
initialize(deviceId, 0 /*updateTime*/, false /*isPresent*/, BatteryState.STATUS_UNKNOWN,
|
||||
Float.NaN /*capacity*/);
|
||||
}
|
||||
|
||||
State(IInputDeviceBatteryState s) {
|
||||
initialize(s.deviceId, s.updateTime, s.isPresent, s.status, s.capacity);
|
||||
}
|
||||
|
||||
State(int deviceId, long updateTime, boolean isPresent, int status, float capacity) {
|
||||
initialize(deviceId, updateTime, isPresent, status, capacity);
|
||||
}
|
||||
|
||||
private void initialize(int deviceId, long updateTime, boolean isPresent, int status,
|
||||
float capacity) {
|
||||
this.deviceId = deviceId;
|
||||
this.updateTime = updateTime;
|
||||
this.isPresent = isPresent;
|
||||
this.status = status;
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BatteryState{deviceId=" + deviceId + ", updateTime=" + updateTime
|
||||
+ ", isPresent=" + isPresent + ", status=" + status + ", capacity=" + capacity
|
||||
+ " }";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import android.hardware.SensorPrivacyManagerInternal;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayViewport;
|
||||
import android.hardware.input.IInputDeviceBatteryListener;
|
||||
import android.hardware.input.IInputDeviceBatteryState;
|
||||
import android.hardware.input.IInputDevicesChangedListener;
|
||||
import android.hardware.input.IInputManager;
|
||||
import android.hardware.input.IInputSensorEventListener;
|
||||
@@ -2305,14 +2306,8 @@ public class InputManagerService extends IInputManager.Stub
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public int getBatteryStatus(int deviceId) {
|
||||
return mNative.getBatteryStatus(deviceId);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
@Override
|
||||
public int getBatteryCapacity(int deviceId) {
|
||||
return mNative.getBatteryCapacity(deviceId);
|
||||
public IInputDeviceBatteryState getBatteryState(int deviceId) {
|
||||
return mBatteryController.getBatteryState(deviceId);
|
||||
}
|
||||
|
||||
// Binder call
|
||||
|
||||
@@ -22,6 +22,7 @@ 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.IInputDeviceBatteryState
|
||||
import android.hardware.input.IInputDevicesChangedListener
|
||||
import android.hardware.input.IInputManager
|
||||
import android.hardware.input.InputManager
|
||||
@@ -32,6 +33,12 @@ import android.platform.test.annotations.Presubmit
|
||||
import android.view.InputDevice
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import com.android.server.input.BatteryController.UEventManager
|
||||
import org.hamcrest.Description
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers
|
||||
import org.hamcrest.TypeSafeMatcher
|
||||
import org.hamcrest.core.IsEqual.equalTo
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.fail
|
||||
@@ -42,7 +49,6 @@ import org.mockito.ArgumentCaptor
|
||||
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
|
||||
@@ -52,7 +58,9 @@ import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.hamcrest.MockitoHamcrest
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.verification.VerificationMode
|
||||
|
||||
private fun createInputDevice(deviceId: Int, hasBattery: Boolean = true): InputDevice =
|
||||
InputDevice.Builder()
|
||||
@@ -64,6 +72,64 @@ private fun createInputDevice(deviceId: Int, hasBattery: Boolean = true): InputD
|
||||
.setGeneration(0)
|
||||
.build()
|
||||
|
||||
// Returns a matcher that helps match member variables of a class.
|
||||
private fun <T, U> memberMatcher(
|
||||
member: String,
|
||||
memberProvider: (T) -> U,
|
||||
match: Matcher<U>
|
||||
): TypeSafeMatcher<T> =
|
||||
object : TypeSafeMatcher<T>() {
|
||||
|
||||
override fun matchesSafely(item: T?): Boolean {
|
||||
return match.matches(memberProvider(item!!))
|
||||
}
|
||||
|
||||
override fun describeMismatchSafely(item: T?, mismatchDescription: Description?) {
|
||||
match.describeMismatch(item, mismatchDescription)
|
||||
}
|
||||
|
||||
override fun describeTo(description: Description?) {
|
||||
match.describeTo(description?.appendText("matches member $member"))
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a matcher for IInputDeviceBatteryState that optionally matches some arguments.
|
||||
private fun matchesState(
|
||||
deviceId: Int,
|
||||
isPresent: Boolean = true,
|
||||
status: Int? = null,
|
||||
capacity: Float? = null,
|
||||
eventTime: Long? = null
|
||||
): Matcher<IInputDeviceBatteryState> {
|
||||
val batteryStateMatchers = mutableListOf<Matcher<IInputDeviceBatteryState>>(
|
||||
memberMatcher("deviceId", { it.deviceId }, equalTo(deviceId)),
|
||||
memberMatcher("isPresent", { it.isPresent }, equalTo(isPresent))
|
||||
)
|
||||
if (eventTime != null) {
|
||||
batteryStateMatchers.add(memberMatcher("updateTime", { it.updateTime }, equalTo(eventTime)))
|
||||
}
|
||||
if (status != null) {
|
||||
batteryStateMatchers.add(memberMatcher("status", { it.status }, equalTo(status)))
|
||||
}
|
||||
if (capacity != null) {
|
||||
batteryStateMatchers.add(memberMatcher("capacity", { it.capacity }, equalTo(capacity)))
|
||||
}
|
||||
return Matchers.allOf(batteryStateMatchers)
|
||||
}
|
||||
|
||||
// Helper used to verify interactions with a mocked battery listener.
|
||||
private fun IInputDeviceBatteryListener.verifyNotified(
|
||||
deviceId: Int,
|
||||
mode: VerificationMode = times(1),
|
||||
isPresent: Boolean = true,
|
||||
status: Int? = null,
|
||||
capacity: Float? = null,
|
||||
eventTime: Long? = null
|
||||
) {
|
||||
verify(this, mode).onBatteryStateChanged(
|
||||
MockitoHamcrest.argThat(matchesState(deviceId, isPresent, status, capacity, eventTime)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for {@link InputDeviceBatteryController}.
|
||||
*
|
||||
@@ -184,14 +250,12 @@ class BatteryControllerTests {
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(100)
|
||||
val listener = createMockListener()
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
eq(STATUS_FULL), eq(1f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, status = STATUS_FULL, capacity = 1.0f)
|
||||
|
||||
`when`(native.getBatteryStatus(SECOND_DEVICE_ID)).thenReturn(STATUS_CHARGING)
|
||||
`when`(native.getBatteryCapacity(SECOND_DEVICE_ID)).thenReturn(78)
|
||||
batteryController.registerBatteryListener(SECOND_DEVICE_ID, listener, PID)
|
||||
verify(listener).onBatteryStateChanged(eq(SECOND_DEVICE_ID), eq(true /*isPresent*/),
|
||||
eq(STATUS_CHARGING), eq(0.78f), anyLong())
|
||||
listener.verifyNotified(SECOND_DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -203,14 +267,13 @@ class BatteryControllerTests {
|
||||
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())
|
||||
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
|
||||
|
||||
// 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)
|
||||
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f,
|
||||
eventTime = TIMESTAMP)
|
||||
|
||||
// If the battery state has not changed when an UEvent is sent, the listeners are not
|
||||
// notified.
|
||||
@@ -233,20 +296,15 @@ class BatteryControllerTests {
|
||||
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()
|
||||
)
|
||||
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
|
||||
|
||||
// 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()
|
||||
)
|
||||
listener.verifyNotified(DEVICE_ID, isPresent = false, status = STATUS_UNKNOWN,
|
||||
capacity = Float.NaN)
|
||||
// Since the battery is no longer present, the UEventListener should be removed.
|
||||
verify(uEventManager).removeListener(uEventListener.value)
|
||||
|
||||
@@ -255,36 +313,32 @@ class BatteryControllerTests {
|
||||
.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()
|
||||
)
|
||||
listener.verifyNotified(DEVICE_ID, mode = times(2), status = STATUS_CHARGING,
|
||||
capacity = 0.78f)
|
||||
// Ensure that a new UEventListener was added.
|
||||
verify(uEventManager, times(2))
|
||||
.addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStartPollingWhenListenerIsRegistered() {
|
||||
val listener = createMockListener()
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/), anyInt(),
|
||||
eq(0.78f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, capacity = 0.78f)
|
||||
|
||||
// Assume there is a change in the battery state. Ensure the listener is not notified
|
||||
// while the polling period has not elapsed.
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
|
||||
testLooper.moveTimeForward(1)
|
||||
testLooper.dispatchAll()
|
||||
verify(listener, never()).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
anyInt(), eq(0.80f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, mode = never(), capacity = 0.80f)
|
||||
|
||||
// Move the time forward so that the polling period has elapsed.
|
||||
// The listener should be notified.
|
||||
testLooper.moveTimeForward(BatteryController.POLLING_PERIOD_MILLIS - 1)
|
||||
testLooper.dispatchNext()
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/), anyInt(),
|
||||
eq(0.80f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, capacity = 0.80f)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,28 +348,50 @@ class BatteryControllerTests {
|
||||
val listener = createMockListener()
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/), anyInt(),
|
||||
eq(0.78f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, capacity = 0.78f)
|
||||
|
||||
// The battery state changed, but we should not be polling for battery changes when the
|
||||
// device is not interactive.
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
|
||||
testLooper.moveTimeForward(BatteryController.POLLING_PERIOD_MILLIS)
|
||||
testLooper.dispatchAll()
|
||||
verify(listener, never()).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
anyInt(), eq(0.80f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, mode = never(), capacity = 0.80f)
|
||||
|
||||
// The device is now interactive. Battery state polling begins immediately.
|
||||
batteryController.onInteractiveChanged(true /*interactive*/)
|
||||
testLooper.dispatchNext()
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
anyInt(), eq(0.80f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, capacity = 0.80f)
|
||||
|
||||
// Ensure that we continue to poll for battery changes.
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(90)
|
||||
testLooper.moveTimeForward(BatteryController.POLLING_PERIOD_MILLIS)
|
||||
testLooper.dispatchNext()
|
||||
verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
|
||||
anyInt(), eq(0.90f), anyLong())
|
||||
listener.verifyNotified(DEVICE_ID, capacity = 0.90f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetBatteryState() {
|
||||
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
|
||||
val batteryState = batteryController.getBatteryState(DEVICE_ID)
|
||||
assertThat("battery state matches", batteryState,
|
||||
matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetBatteryStateWithListener() {
|
||||
val listener = createMockListener()
|
||||
`when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.78f)
|
||||
|
||||
// If getBatteryState() is called when a listener is monitoring the device and there is a
|
||||
// change in the battery state, the listener is also notified.
|
||||
`when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
|
||||
val batteryState = batteryController.getBatteryState(DEVICE_ID)
|
||||
assertThat("battery matches state", batteryState,
|
||||
matchesState(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f))
|
||||
listener.verifyNotified(DEVICE_ID, status = STATUS_CHARGING, capacity = 0.80f)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user