Merge changes I28d77e66,I24cd9fe1
* changes: BatteryController: Listen to battery changes from Bluetooth metadata BatteryController: Get Bluetooth battery level from callback
This commit is contained in:
committed by
Android (Google) Code Review
commit
64b1e9f45f
@@ -31,6 +31,7 @@ import android.hardware.input.IInputDeviceBatteryListener;
|
||||
import android.hardware.input.IInputDeviceBatteryState;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
@@ -51,6 +52,7 @@ import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
@@ -102,7 +104,7 @@ final class BatteryController {
|
||||
|
||||
BatteryController(Context context, NativeInputManagerService nativeService, Looper looper) {
|
||||
this(context, nativeService, looper, new UEventManager() {},
|
||||
new LocalBluetoothBatteryManager(context));
|
||||
new LocalBluetoothBatteryManager(context, looper));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -163,7 +165,7 @@ final class BatteryController {
|
||||
// This is the first listener that is monitoring this device.
|
||||
monitor = new DeviceMonitor(deviceId);
|
||||
mDeviceMonitors.put(deviceId, monitor);
|
||||
updateBluetoothMonitoring();
|
||||
updateBluetoothBatteryMonitoring();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
@@ -378,13 +380,26 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBluetoothBatteryLevelChange(long eventTime, String address) {
|
||||
private void handleBluetoothBatteryLevelChange(long eventTime, String address,
|
||||
int batteryLevel) {
|
||||
synchronized (mLock) {
|
||||
final DeviceMonitor monitor = findIf(mDeviceMonitors, (m) ->
|
||||
(m.mBluetoothDevice != null
|
||||
&& address.equals(m.mBluetoothDevice.getAddress())));
|
||||
if (monitor != null) {
|
||||
monitor.onBluetoothBatteryChanged(eventTime);
|
||||
monitor.onBluetoothBatteryChanged(eventTime, batteryLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleBluetoothMetadataChange(@NonNull BluetoothDevice device, int key,
|
||||
@Nullable byte[] value) {
|
||||
synchronized (mLock) {
|
||||
final DeviceMonitor monitor =
|
||||
findIf(mDeviceMonitors, (m) -> device.equals(m.mBluetoothDevice));
|
||||
if (monitor != null) {
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
monitor.onBluetoothMetadataChanged(eventTime, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,31 +529,19 @@ final class BatteryController {
|
||||
isPresent ? mNative.getBatteryCapacity(deviceId) / 100.f : Float.NaN);
|
||||
}
|
||||
|
||||
// Queries the battery state of an input device from Bluetooth.
|
||||
private State queryBatteryStateFromBluetooth(int deviceId, long updateTime,
|
||||
@NonNull BluetoothDevice bluetoothDevice) {
|
||||
final int level = mBluetoothBatteryManager.getBatteryLevel(bluetoothDevice.getAddress());
|
||||
if (level == BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF
|
||||
|| level == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
|
||||
return new State(deviceId);
|
||||
}
|
||||
return new State(deviceId, updateTime, true /*isPresent*/, BatteryState.STATUS_UNKNOWN,
|
||||
level / 100.f);
|
||||
}
|
||||
|
||||
private void updateBluetoothMonitoring() {
|
||||
private void updateBluetoothBatteryMonitoring() {
|
||||
synchronized (mLock) {
|
||||
if (anyOf(mDeviceMonitors, (m) -> m.mBluetoothDevice != null)) {
|
||||
// At least one input device being monitored is connected over Bluetooth.
|
||||
if (mBluetoothBatteryListener == null) {
|
||||
if (DEBUG) Slog.d(TAG, "Registering bluetooth battery listener");
|
||||
mBluetoothBatteryListener = this::handleBluetoothBatteryLevelChange;
|
||||
mBluetoothBatteryManager.addListener(mBluetoothBatteryListener);
|
||||
mBluetoothBatteryManager.addBatteryListener(mBluetoothBatteryListener);
|
||||
}
|
||||
} else if (mBluetoothBatteryListener != null) {
|
||||
// No Bluetooth input devices are monitored, so remove the registered listener.
|
||||
if (DEBUG) Slog.d(TAG, "Unregistering bluetooth battery listener");
|
||||
mBluetoothBatteryManager.removeListener(mBluetoothBatteryListener);
|
||||
mBluetoothBatteryManager.removeBatteryListener(mBluetoothBatteryListener);
|
||||
mBluetoothBatteryListener = null;
|
||||
}
|
||||
}
|
||||
@@ -550,16 +553,23 @@ final class BatteryController {
|
||||
// Represents whether the input device has a sysfs battery node.
|
||||
protected boolean mHasBattery = false;
|
||||
|
||||
protected final State mBluetoothState;
|
||||
@Nullable
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
long mBluetoothEventTime = 0;
|
||||
// The battery level reported by the Bluetooth Hands-Free Profile (HPF) obtained through
|
||||
// BluetoothDevice#getBatteryLevel().
|
||||
int mBluetoothBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
||||
// The battery level and status reported through the Bluetooth device's metadata.
|
||||
int mBluetoothMetadataBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
||||
int mBluetoothMetadataBatteryStatus = BatteryState.STATUS_UNKNOWN;
|
||||
@Nullable
|
||||
private BluetoothAdapter.OnMetadataChangedListener mBluetoothMetadataListener;
|
||||
|
||||
@Nullable
|
||||
private UEventBatteryListener mUEventBatteryListener;
|
||||
|
||||
DeviceMonitor(int deviceId) {
|
||||
mState = new State(deviceId);
|
||||
mBluetoothState = new State(deviceId);
|
||||
|
||||
// Load the initial battery state and start monitoring.
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
@@ -570,7 +580,7 @@ final class BatteryController {
|
||||
final State oldState = getBatteryStateForReporting();
|
||||
changes.accept(eventTime);
|
||||
final State newState = getBatteryStateForReporting();
|
||||
if (!oldState.equals(newState)) {
|
||||
if (!oldState.equalsIgnoringUpdateTime(newState)) {
|
||||
notifyAllListenersForDevice(newState);
|
||||
}
|
||||
}
|
||||
@@ -594,13 +604,22 @@ final class BatteryController {
|
||||
final BluetoothDevice bluetoothDevice = getBluetoothDevice(deviceId);
|
||||
if (!Objects.equals(mBluetoothDevice, bluetoothDevice)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Bluetooth device "
|
||||
+ ((bluetoothDevice != null) ? "is" : "is not")
|
||||
+ " now present for deviceId " + deviceId);
|
||||
Slog.d(TAG, "Bluetooth device is now "
|
||||
+ ((bluetoothDevice != null) ? "" : "not")
|
||||
+ " present for deviceId " + deviceId);
|
||||
}
|
||||
|
||||
mBluetoothBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
||||
stopBluetoothMetadataMonitoring();
|
||||
|
||||
mBluetoothDevice = bluetoothDevice;
|
||||
updateBluetoothMonitoring();
|
||||
updateBatteryStateFromBluetooth(eventTime);
|
||||
updateBluetoothBatteryMonitoring();
|
||||
|
||||
if (mBluetoothDevice != null) {
|
||||
mBluetoothBatteryLevel = mBluetoothBatteryManager.getBatteryLevel(
|
||||
mBluetoothDevice.getAddress());
|
||||
startBluetoothMetadataMonitoring(eventTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,11 +651,39 @@ final class BatteryController {
|
||||
}
|
||||
}
|
||||
|
||||
private void startBluetoothMetadataMonitoring(long eventTime) {
|
||||
Objects.requireNonNull(mBluetoothDevice);
|
||||
|
||||
mBluetoothMetadataListener = BatteryController.this::handleBluetoothMetadataChange;
|
||||
mBluetoothBatteryManager.addMetadataListener(mBluetoothDevice.getAddress(),
|
||||
mBluetoothMetadataListener);
|
||||
updateBluetoothMetadataState(eventTime, BluetoothDevice.METADATA_MAIN_BATTERY,
|
||||
mBluetoothBatteryManager.getMetadata(mBluetoothDevice.getAddress(),
|
||||
BluetoothDevice.METADATA_MAIN_BATTERY));
|
||||
updateBluetoothMetadataState(eventTime, BluetoothDevice.METADATA_MAIN_CHARGING,
|
||||
mBluetoothBatteryManager.getMetadata(mBluetoothDevice.getAddress(),
|
||||
BluetoothDevice.METADATA_MAIN_CHARGING));
|
||||
}
|
||||
|
||||
private void stopBluetoothMetadataMonitoring() {
|
||||
if (mBluetoothMetadataListener == null) {
|
||||
return;
|
||||
}
|
||||
Objects.requireNonNull(mBluetoothDevice);
|
||||
|
||||
mBluetoothBatteryManager.removeMetadataListener(
|
||||
mBluetoothDevice.getAddress(), mBluetoothMetadataListener);
|
||||
mBluetoothMetadataListener = null;
|
||||
mBluetoothMetadataBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
||||
mBluetoothMetadataBatteryStatus = BatteryState.STATUS_UNKNOWN;
|
||||
}
|
||||
|
||||
// This must be called when the device is no longer being monitored.
|
||||
public void onMonitorDestroy() {
|
||||
stopNativeMonitoring();
|
||||
stopBluetoothMetadataMonitoring();
|
||||
mBluetoothDevice = null;
|
||||
updateBluetoothMonitoring();
|
||||
updateBluetoothBatteryMonitoring();
|
||||
}
|
||||
|
||||
protected void updateBatteryStateFromNative(long eventTime) {
|
||||
@@ -644,13 +691,6 @@ final class BatteryController {
|
||||
queryBatteryStateFromNative(mState.deviceId, eventTime, mHasBattery));
|
||||
}
|
||||
|
||||
protected void updateBatteryStateFromBluetooth(long eventTime) {
|
||||
final State bluetoothState = mBluetoothDevice == null ? new State(mState.deviceId)
|
||||
: queryBatteryStateFromBluetooth(mState.deviceId, eventTime,
|
||||
mBluetoothDevice);
|
||||
mBluetoothState.updateIfChanged(bluetoothState);
|
||||
}
|
||||
|
||||
public void onPoll(long eventTime) {
|
||||
processChangesAndNotify(eventTime, this::updateBatteryStateFromNative);
|
||||
}
|
||||
@@ -659,8 +699,51 @@ final class BatteryController {
|
||||
processChangesAndNotify(eventTime, this::updateBatteryStateFromNative);
|
||||
}
|
||||
|
||||
public void onBluetoothBatteryChanged(long eventTime) {
|
||||
processChangesAndNotify(eventTime, this::updateBatteryStateFromBluetooth);
|
||||
public void onBluetoothBatteryChanged(long eventTime, int bluetoothBatteryLevel) {
|
||||
processChangesAndNotify(eventTime, (time) -> {
|
||||
mBluetoothBatteryLevel = bluetoothBatteryLevel;
|
||||
mBluetoothEventTime = time;
|
||||
});
|
||||
}
|
||||
|
||||
public void onBluetoothMetadataChanged(long eventTime, int key, @Nullable byte[] value) {
|
||||
processChangesAndNotify(eventTime,
|
||||
(time) -> updateBluetoothMetadataState(time, key, value));
|
||||
}
|
||||
|
||||
private void updateBluetoothMetadataState(long eventTime, int key,
|
||||
@Nullable byte[] value) {
|
||||
switch (key) {
|
||||
case BluetoothDevice.METADATA_MAIN_BATTERY:
|
||||
mBluetoothEventTime = eventTime;
|
||||
mBluetoothMetadataBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
||||
if (value != null) {
|
||||
try {
|
||||
mBluetoothMetadataBatteryLevel = Integer.parseInt(
|
||||
new String(value));
|
||||
} catch (NumberFormatException e) {
|
||||
Slog.wtf(TAG,
|
||||
"Failed to parse bluetooth METADATA_MAIN_BATTERY with "
|
||||
+ "value '"
|
||||
+ new String(value) + "' for device "
|
||||
+ mBluetoothDevice);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BluetoothDevice.METADATA_MAIN_CHARGING:
|
||||
mBluetoothEventTime = eventTime;
|
||||
if (value != null) {
|
||||
mBluetoothMetadataBatteryStatus = Boolean.parseBoolean(
|
||||
new String(value))
|
||||
? BatteryState.STATUS_CHARGING
|
||||
: BatteryState.STATUS_DISCHARGING;
|
||||
} else {
|
||||
mBluetoothMetadataBatteryStatus = BatteryState.STATUS_UNKNOWN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean requiresPolling() {
|
||||
@@ -677,11 +760,25 @@ final class BatteryController {
|
||||
|
||||
// Returns the current battery state that can be used to notify listeners BatteryController.
|
||||
public State getBatteryStateForReporting() {
|
||||
// Give precedence to the Bluetooth battery state if it's present.
|
||||
if (mBluetoothState.isPresent) {
|
||||
return new State(mBluetoothState);
|
||||
// Give precedence to the Bluetooth battery state, and fall back to the native state.
|
||||
return Objects.requireNonNullElseGet(resolveBluetoothBatteryState(),
|
||||
() -> new State(mState));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected State resolveBluetoothBatteryState() {
|
||||
final int level;
|
||||
// Prefer battery level obtained from the metadata over the Bluetooth Hands-Free
|
||||
// Profile (HFP).
|
||||
if (mBluetoothMetadataBatteryLevel >= 0 && mBluetoothMetadataBatteryLevel <= 100) {
|
||||
level = mBluetoothMetadataBatteryLevel;
|
||||
} else if (mBluetoothBatteryLevel >= 0 && mBluetoothBatteryLevel <= 100) {
|
||||
level = mBluetoothBatteryLevel;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return new State(mState);
|
||||
return new State(mState.deviceId, mBluetoothEventTime, true,
|
||||
mBluetoothMetadataBatteryStatus, level / 100.f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -690,7 +787,7 @@ final class BatteryController {
|
||||
+ ", Name='" + getInputDeviceName(mState.deviceId) + "'"
|
||||
+ ", NativeBattery=" + mState
|
||||
+ ", UEventListener=" + (mUEventBatteryListener != null ? "added" : "none")
|
||||
+ ", BluetoothBattery=" + mBluetoothState;
|
||||
+ ", BluetoothState=" + resolveBluetoothBatteryState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -775,12 +872,10 @@ final class BatteryController {
|
||||
|
||||
@Override
|
||||
public State getBatteryStateForReporting() {
|
||||
// Give precedence to the Bluetooth battery state if it's present.
|
||||
if (mBluetoothState.isPresent) {
|
||||
return new State(mBluetoothState);
|
||||
}
|
||||
return mValidityTimeoutCallback != null
|
||||
? new State(mState) : new State(mState.deviceId);
|
||||
// Give precedence to the Bluetooth battery state, and fall back to the native state.
|
||||
return Objects.requireNonNullElseGet(resolveBluetoothBatteryState(),
|
||||
() -> mValidityTimeoutCallback != null
|
||||
? new State(mState) : new State(mState.deviceId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -844,15 +939,24 @@ final class BatteryController {
|
||||
interface BluetoothBatteryManager {
|
||||
@VisibleForTesting
|
||||
interface BluetoothBatteryListener {
|
||||
void onBluetoothBatteryChanged(long eventTime, String address);
|
||||
void onBluetoothBatteryChanged(long eventTime, String address, int batteryLevel);
|
||||
}
|
||||
void addListener(BluetoothBatteryListener listener);
|
||||
void removeListener(BluetoothBatteryListener listener);
|
||||
// Methods used for obtaining the Bluetooth battery level through Bluetooth HFP.
|
||||
void addBatteryListener(BluetoothBatteryListener listener);
|
||||
void removeBatteryListener(BluetoothBatteryListener listener);
|
||||
int getBatteryLevel(String address);
|
||||
|
||||
// Methods used for obtaining the battery level through Bluetooth metadata.
|
||||
void addMetadataListener(String address,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener);
|
||||
void removeMetadataListener(String address,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener);
|
||||
byte[] getMetadata(String address, int key);
|
||||
}
|
||||
|
||||
private static class LocalBluetoothBatteryManager implements BluetoothBatteryManager {
|
||||
private final Context mContext;
|
||||
private final Executor mExecutor;
|
||||
@Nullable
|
||||
@GuardedBy("mBroadcastReceiver")
|
||||
private BluetoothBatteryListener mRegisteredListener;
|
||||
@@ -868,24 +972,25 @@ final class BatteryController {
|
||||
if (bluetoothDevice == null) {
|
||||
return;
|
||||
}
|
||||
// We do not use the EXTRA_LEVEL value. Instead, the battery level will be queried
|
||||
// from BluetoothDevice later so that we use a single source for the battery level.
|
||||
final int batteryLevel = intent.getIntExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL,
|
||||
BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
|
||||
synchronized (mBroadcastReceiver) {
|
||||
if (mRegisteredListener != null) {
|
||||
final long eventTime = SystemClock.uptimeMillis();
|
||||
mRegisteredListener.onBluetoothBatteryChanged(
|
||||
eventTime, bluetoothDevice.getAddress());
|
||||
eventTime, bluetoothDevice.getAddress(), batteryLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
LocalBluetoothBatteryManager(Context context) {
|
||||
LocalBluetoothBatteryManager(Context context, Looper looper) {
|
||||
mContext = context;
|
||||
mExecutor = new HandlerExecutor(new Handler(looper));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(BluetoothBatteryListener listener) {
|
||||
public void addBatteryListener(BluetoothBatteryListener listener) {
|
||||
synchronized (mBroadcastReceiver) {
|
||||
if (mRegisteredListener != null) {
|
||||
throw new IllegalStateException(
|
||||
@@ -898,7 +1003,7 @@ final class BatteryController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(BluetoothBatteryListener listener) {
|
||||
public void removeBatteryListener(BluetoothBatteryListener listener) {
|
||||
synchronized (mBroadcastReceiver) {
|
||||
if (!listener.equals(mRegisteredListener)) {
|
||||
throw new IllegalStateException("Listener is not registered.");
|
||||
@@ -912,6 +1017,28 @@ final class BatteryController {
|
||||
public int getBatteryLevel(String address) {
|
||||
return getBluetoothDevice(mContext, address).getBatteryLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMetadataListener(String address,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener) {
|
||||
Objects.requireNonNull(mContext.getSystemService(BluetoothManager.class))
|
||||
.getAdapter().addOnMetadataChangedListener(
|
||||
getBluetoothDevice(mContext, address), mExecutor,
|
||||
listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMetadataListener(String address,
|
||||
BluetoothAdapter.OnMetadataChangedListener listener) {
|
||||
Objects.requireNonNull(mContext.getSystemService(BluetoothManager.class))
|
||||
.getAdapter().removeOnMetadataChangedListener(
|
||||
getBluetoothDevice(mContext, address), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getMetadata(String address, int key) {
|
||||
return getBluetoothDevice(mContext, address).getMetadata(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper class that adds copying and printing functionality to IInputDeviceBatteryState.
|
||||
@@ -954,7 +1081,7 @@ final class BatteryController {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
private boolean equalsIgnoringUpdateTime(IInputDeviceBatteryState other) {
|
||||
public boolean equalsIgnoringUpdateTime(IInputDeviceBatteryState other) {
|
||||
long updateTime = this.updateTime;
|
||||
this.updateTime = other.updateTime;
|
||||
boolean eq = this.equals(other);
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package com.android.server.input
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.hardware.BatteryState.STATUS_CHARGING
|
||||
@@ -246,6 +248,11 @@ class BatteryControllerTests {
|
||||
notifyDeviceChanged(deviceId, hasBattery, supportsUsi)
|
||||
}
|
||||
|
||||
private fun createBluetoothDevice(address: String): BluetoothDevice {
|
||||
return context.getSystemService(BluetoothManager::class.java)!!
|
||||
.adapter.getRemoteDevice(address)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
InputManager.clearInstance()
|
||||
@@ -656,29 +663,31 @@ class BatteryControllerTests {
|
||||
addInputDevice(SECOND_BT_DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
|
||||
// Ensure that a BT battery listener is not added when there are no monitored BT devices.
|
||||
verify(bluetoothBatteryManager, never()).addListener(any())
|
||||
// Listen to a non-Bluetooth device and ensure that the BT battery listener is not added
|
||||
// when there are no monitored BT devices.
|
||||
val listener = createMockListener()
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager, never()).addBatteryListener(any())
|
||||
|
||||
val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
|
||||
val listener = createMockListener()
|
||||
|
||||
// The BT battery listener is added when the first BT input device is monitored.
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager).addListener(bluetoothListener.capture())
|
||||
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
|
||||
|
||||
// The BT listener is only added once for all BT devices.
|
||||
batteryController.registerBatteryListener(SECOND_BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager, times(1)).addListener(any())
|
||||
verify(bluetoothBatteryManager, times(1)).addBatteryListener(any())
|
||||
|
||||
// The BT listener is only removed when there are no monitored BT devices.
|
||||
batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager, never()).removeListener(any())
|
||||
verify(bluetoothBatteryManager, never()).removeBatteryListener(any())
|
||||
|
||||
`when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
|
||||
.thenReturn(null)
|
||||
notifyDeviceChanged(SECOND_BT_DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
verify(bluetoothBatteryManager).removeListener(bluetoothListener.value)
|
||||
verify(bluetoothBatteryManager).removeBatteryListener(bluetoothListener.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -690,15 +699,14 @@ class BatteryControllerTests {
|
||||
val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
|
||||
val listener = createMockListener()
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager).addListener(bluetoothListener.capture())
|
||||
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
|
||||
|
||||
// When the state has not changed, the listener is not notified again.
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF")
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 21)
|
||||
listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
|
||||
|
||||
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(25)
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF")
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 25)
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f)
|
||||
}
|
||||
|
||||
@@ -717,7 +725,7 @@ class BatteryControllerTests {
|
||||
// When the device is first monitored and both native and BT battery is available,
|
||||
// the latter is used.
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager).addListener(bluetoothListener.capture())
|
||||
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
|
||||
verify(uEventManager).addListener(uEventListener.capture(), any())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
|
||||
assertThat("battery state matches", batteryController.getBatteryState(BT_DEVICE_ID),
|
||||
@@ -744,25 +752,144 @@ class BatteryControllerTests {
|
||||
val uEventListener = ArgumentCaptor.forClass(UEventBatteryListener::class.java)
|
||||
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager).addListener(bluetoothListener.capture())
|
||||
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
|
||||
verify(uEventManager).addListener(uEventListener.capture(), any())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f)
|
||||
|
||||
// Fall back to the native state when BT is off.
|
||||
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF")))
|
||||
.thenReturn(BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF)
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF")
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF",
|
||||
BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF)
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.98f)
|
||||
|
||||
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(22)
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF")
|
||||
verify(bluetoothBatteryManager).addListener(bluetoothListener.capture())
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 22)
|
||||
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.22f)
|
||||
|
||||
// Fall back to the native state when BT battery is unknown.
|
||||
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF")))
|
||||
.thenReturn(BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF")
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF",
|
||||
BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
|
||||
listener.verifyNotified(BT_DEVICE_ID, mode = times(2), capacity = 0.98f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRegisterBluetoothMetadataListenerForMonitoredBluetoothDevices() {
|
||||
`when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
|
||||
.thenReturn("AA:BB:CC:DD:EE:FF")
|
||||
`when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
|
||||
.thenReturn("11:22:33:44:55:66")
|
||||
addInputDevice(BT_DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
addInputDevice(SECOND_BT_DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
|
||||
// Listen to a non-Bluetooth device and ensure that the metadata listener is not added when
|
||||
// there are no monitored BT devices.
|
||||
val listener = createMockListener()
|
||||
batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager, never()).addMetadataListener(any(), any())
|
||||
|
||||
val metadataListener1 = ArgumentCaptor.forClass(
|
||||
BluetoothAdapter.OnMetadataChangedListener::class.java)
|
||||
val metadataListener2 = ArgumentCaptor.forClass(
|
||||
BluetoothAdapter.OnMetadataChangedListener::class.java)
|
||||
|
||||
// The metadata listener is added when the first BT input device is monitored.
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager)
|
||||
.addMetadataListener(eq("AA:BB:CC:DD:EE:FF"), metadataListener1.capture())
|
||||
|
||||
// There is one metadata listener added for each BT device.
|
||||
batteryController.registerBatteryListener(SECOND_BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager)
|
||||
.addMetadataListener(eq("11:22:33:44:55:66"), metadataListener2.capture())
|
||||
|
||||
// The metadata listener is removed when the device is no longer monitored.
|
||||
batteryController.unregisterBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager)
|
||||
.removeMetadataListener("AA:BB:CC:DD:EE:FF", metadataListener1.value)
|
||||
|
||||
`when`(iInputManager.getInputDeviceBluetoothAddress(SECOND_BT_DEVICE_ID))
|
||||
.thenReturn(null)
|
||||
notifyDeviceChanged(SECOND_BT_DEVICE_ID)
|
||||
testLooper.dispatchNext()
|
||||
verify(bluetoothBatteryManager)
|
||||
.removeMetadataListener("11:22:33:44:55:66", metadataListener2.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNotifiesBluetoothMetadataBatteryChanges() {
|
||||
`when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
|
||||
.thenReturn("AA:BB:CC:DD:EE:FF")
|
||||
`when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
|
||||
BluetoothDevice.METADATA_MAIN_BATTERY))
|
||||
.thenReturn("21".toByteArray())
|
||||
addInputDevice(BT_DEVICE_ID)
|
||||
val metadataListener = ArgumentCaptor.forClass(
|
||||
BluetoothAdapter.OnMetadataChangedListener::class.java)
|
||||
val listener = createMockListener()
|
||||
val bluetoothDevice = createBluetoothDevice("AA:BB:CC:DD:EE:FF")
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
verify(bluetoothBatteryManager)
|
||||
.addMetadataListener(eq("AA:BB:CC:DD:EE:FF"), metadataListener.capture())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.21f, status = STATUS_UNKNOWN)
|
||||
|
||||
// When the state has not changed, the listener is not notified again.
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "21".toByteArray())
|
||||
listener.verifyNotified(BT_DEVICE_ID, mode = times(1), capacity = 0.21f)
|
||||
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "25".toByteArray())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_UNKNOWN)
|
||||
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_CHARGING)
|
||||
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "false".toByteArray())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.25f, status = STATUS_DISCHARGING)
|
||||
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, null)
|
||||
listener.verifyNotified(BT_DEVICE_ID, mode = times(2), capacity = 0.25f,
|
||||
status = STATUS_UNKNOWN)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBluetoothMetadataBatteryIsPrioritized() {
|
||||
`when`(iInputManager.getInputDeviceBluetoothAddress(BT_DEVICE_ID))
|
||||
.thenReturn("AA:BB:CC:DD:EE:FF")
|
||||
`when`(bluetoothBatteryManager.getBatteryLevel(eq("AA:BB:CC:DD:EE:FF"))).thenReturn(21)
|
||||
`when`(bluetoothBatteryManager.getMetadata("AA:BB:CC:DD:EE:FF",
|
||||
BluetoothDevice.METADATA_MAIN_BATTERY))
|
||||
.thenReturn("22".toByteArray())
|
||||
addInputDevice(BT_DEVICE_ID)
|
||||
val bluetoothListener = ArgumentCaptor.forClass(BluetoothBatteryListener::class.java)
|
||||
val metadataListener = ArgumentCaptor.forClass(
|
||||
BluetoothAdapter.OnMetadataChangedListener::class.java)
|
||||
val listener = createMockListener()
|
||||
val bluetoothDevice = createBluetoothDevice("AA:BB:CC:DD:EE:FF")
|
||||
batteryController.registerBatteryListener(BT_DEVICE_ID, listener, PID)
|
||||
|
||||
verify(bluetoothBatteryManager).addBatteryListener(bluetoothListener.capture())
|
||||
verify(bluetoothBatteryManager)
|
||||
.addMetadataListener(eq("AA:BB:CC:DD:EE:FF"), metadataListener.capture())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.22f)
|
||||
|
||||
// A change in the Bluetooth battery level has no effect while there is a valid battery
|
||||
// level obtained through the metadata.
|
||||
bluetoothListener.value!!.onBluetoothBatteryChanged(TIMESTAMP, "AA:BB:CC:DD:EE:FF", 23)
|
||||
listener.verifyNotified(BT_DEVICE_ID, mode = never(), capacity = 0.23f)
|
||||
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, "24".toByteArray())
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.24f)
|
||||
|
||||
// When the battery level from the metadata is no longer valid, we fall back to using the
|
||||
// Bluetooth battery level.
|
||||
metadataListener.value!!.onMetadataChanged(
|
||||
bluetoothDevice, BluetoothDevice.METADATA_MAIN_BATTERY, null)
|
||||
listener.verifyNotified(BT_DEVICE_ID, capacity = 0.23f)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user