Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 95b9a3dd authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

BatteryController: Get Bluetooth battery level from callback

Previously, when there was a Bluetooth battery update, we ignored the
updated battery level from the broadcast's EXTRA_BATTERY_LEVEL and
instead queried the BluetoothDevice for the battery level.

In this CL, we change this behavior so that we obtain the battery level
directly from the intent extra, removing the need to query the Bluetooth
service again for the battery level.

This refactor is to set up for getting battery level from the Bluetooth
metadata in the following CL.

Also, rename the BluetoothBatteryManager#add/removeListener() APIs to
add/removeBatteryListener.

Bug: 243005009
Test: atest BatteryControllerTests
Change-Id: I24cd9fe10a3e2c6c400439a3cf891b1cda5f3a3b
parent 251c7563
Loading
Loading
Loading
Loading
+46 −49
Original line number Diff line number Diff line
@@ -378,13 +378,14 @@ 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);
            }
        }
    }
@@ -514,18 +515,6 @@ 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() {
        synchronized (mLock) {
            if (anyOf(mDeviceMonitors, (m) -> m.mBluetoothDevice != null)) {
@@ -533,12 +522,12 @@ final class BatteryController {
                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 +539,16 @@ 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;
        int mBluetoothBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
        long mBluetoothEventTime = 0;

        @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 +559,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);
            }
        }
@@ -600,7 +589,14 @@ final class BatteryController {
                }
                mBluetoothDevice = bluetoothDevice;
                updateBluetoothMonitoring();
                updateBatteryStateFromBluetooth(eventTime);
                mBluetoothEventTime = eventTime;

                if (mBluetoothDevice != null) {
                    mBluetoothBatteryLevel = mBluetoothBatteryManager.getBatteryLevel(
                            mBluetoothDevice.getAddress());
                } else {
                    mBluetoothBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
                }
            }
        }

@@ -644,13 +640,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 +648,11 @@ 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 boolean requiresPolling() {
@@ -677,11 +669,18 @@ 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));
        }
            return new State(mState);

        @Nullable
        protected State resolveBluetoothBatteryState() {
            if (mBluetoothBatteryLevel < 0 || mBluetoothBatteryLevel > 100) {
                return null;
            }
            return new State(mState.deviceId, mBluetoothEventTime, true,
                    BatteryState.STATUS_UNKNOWN, mBluetoothBatteryLevel / 100.f);
        }

        @Override
@@ -690,7 +689,7 @@ final class BatteryController {
                    + ", Name='" + getInputDeviceName(mState.deviceId) + "'"
                    + ", NativeBattery=" + mState
                    + ", UEventListener=" + (mUEventBatteryListener != null ? "added" : "none")
                    + ", BluetoothBattery=" + mBluetoothState;
                    + ", BluetoothState=" + resolveBluetoothBatteryState();
        }
    }

@@ -775,12 +774,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,10 +841,10 @@ 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);
        void addBatteryListener(BluetoothBatteryListener listener);
        void removeBatteryListener(BluetoothBatteryListener listener);
        int getBatteryLevel(String address);
    }

@@ -868,13 +865,13 @@ 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);
                    }
                }
            }
@@ -885,7 +882,7 @@ final class BatteryController {
        }

        @Override
        public void addListener(BluetoothBatteryListener listener) {
        public void addBatteryListener(BluetoothBatteryListener listener) {
            synchronized (mBroadcastReceiver) {
                if (mRegisteredListener != null) {
                    throw new IllegalStateException(
@@ -898,7 +895,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.");
@@ -954,7 +951,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 −20
Original line number Diff line number Diff line
@@ -657,28 +657,28 @@ class BatteryControllerTests {
        testLooper.dispatchNext()

        // Ensure that a BT battery listener is not added when there are no monitored BT devices.
        verify(bluetoothBatteryManager, never()).addListener(any())
        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 +690,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 +716,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 +743,22 @@ 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)
    }
}