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

Commit 7c7d28cd authored by Pavlin Radoslavov's avatar Pavlin Radoslavov Committed by android-build-merger
Browse files

Merge "Update the Bluetooth Active Device state in the Device Manager"

am: 5b2fe53a

Change-Id: I5928ddaea907e0beca598cdb4f1507a0930ea592
parents d6d07385 5b2fe53a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -444,6 +444,7 @@ public class BluetoothEventManager {

    private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
                                             int bluetoothProfile) {
        mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
        synchronized (mCallbacks) {
            for (BluetoothCallback callback : mCallbacks) {
                callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+24 −3
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.bluetooth.BluetoothAdapter;
import android.support.annotation.VisibleForTesting;

import com.android.settingslib.R;

@@ -461,12 +462,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
    }

    /**
     * Set the device status as active or non-active per Bluetooth profile.
     * Update the device status as active or non-active per Bluetooth profile.
     *
     * @param isActive true if the device is active
     * @param bluetoothProfile the Bluetooth profile
     */
    public void setActiveDevice(boolean isActive, int bluetoothProfile) {
    public void onActiveDeviceChanged(boolean isActive, int bluetoothProfile) {
        boolean changed = false;
        switch (bluetoothProfile) {
        case BluetoothProfile.A2DP:
@@ -478,7 +479,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
            mIsActiveDeviceHeadset = isActive;
            break;
        default:
            Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
            Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
                    " isActive " + isActive);
            break;
        }
@@ -487,6 +488,26 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
        }
    }

    /**
     * Get the device status as active or non-active per Bluetooth profile.
     *
     * @param bluetoothProfile the Bluetooth profile
     * @return true if the device is active
     */
    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    public boolean isActiveDevice(int bluetoothProfile) {
        switch (bluetoothProfile) {
            case BluetoothProfile.A2DP:
                return mIsActiveDeviceA2dp;
            case BluetoothProfile.HEADSET:
                return mIsActiveDeviceHeadset;
            default:
                Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
                break;
        }
        return false;
    }

    void setRssi(short rssi) {
        if (mRssi != rssi) {
            mRssi = rssi;
+10 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -167,6 +168,15 @@ public class CachedBluetoothDeviceManager {
            }
        }
    }

    public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
                                                   int bluetoothProfile) {
        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
            boolean isActive = Objects.equals(cachedDevice, activeDevice);
            cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
        }
    }

    private void log(String msg) {
        if (DEBUG) {
            Log.d(TAG, msg);
+283 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 com.android.settingslib.bluetooth;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;

import com.android.settingslib.R;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;

import java.util.Collection;

@RunWith(RobolectricTestRunner.class)
public class CachedBluetoothDeviceManagerTest {
    private final static String DEVICE_NAME_1 = "TestName_1";
    private final static String DEVICE_NAME_2 = "TestName_2";
    private final static String DEVICE_ALIAS_1 = "TestAlias_1";
    private final static String DEVICE_ALIAS_2 = "TestAlias_2";
    private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
    private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
    private final BluetoothClass DEVICE_CLASS_1 =
        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
    private final BluetoothClass DEVICE_CLASS_2 =
        new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
    @Mock
    private LocalBluetoothAdapter mLocalAdapter;
    @Mock
    private LocalBluetoothProfileManager mLocalProfileManager;
    @Mock
    private LocalBluetoothManager mLocalBluetoothManager;
    @Mock
    private BluetoothEventManager mBluetoothEventManager;
    @Mock
    private HeadsetProfile mHfpProfile;
    @Mock
    private A2dpProfile mA2dpProfile;
    @Mock
    private PanProfile mPanProfile;
    @Mock
    private BluetoothDevice mDevice1;
    @Mock
    private BluetoothDevice mDevice2;
    private CachedBluetoothDeviceManager mCachedDeviceManager;
    private Context mContext;
    private String[] mActiveDeviceStringsArray;
    private String mActiveDeviceStringNone;
    private String mActiveDeviceStringAll;
    private String mActiveDeviceStringMedia;
    private String mActiveDeviceStringPhone;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;
        when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
        when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
        when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
        when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
        when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
        when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
        when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
        when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);

        when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
        when(mLocalAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
        when(mHfpProfile.isProfileReady()).thenReturn(true);
        when(mA2dpProfile.isProfileReady()).thenReturn(true);
        when(mPanProfile.isProfileReady()).thenReturn(true);
        mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
    }

    /**
     * Test to verify addDevice().
     */
    @Test
    public void testAddDevice_validCachedDevices_devicesAdded() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice2);
        assertThat(cachedDevice2).isNotNull();

        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
        assertThat(devices).contains(cachedDevice1);
        assertThat(devices).contains(cachedDevice2);

        assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
        assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
    }

    /**
     * Test to verify getName().
     */
    @Test
    public void testGetName_validCachedDevice_nameFound() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
    }

    /**
     * Test to verify onDeviceNameUpdated().
     */
    @Test
    public void testOnDeviceNameUpdated_validName_nameUpdated() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);

        final String newAliasName = "NewAliasName";
        when(mDevice1.getAliasName()).thenReturn(newAliasName);
        mCachedDeviceManager.onDeviceNameUpdated(mDevice1);
        assertThat(cachedDevice1.getName()).isEqualTo(newAliasName);
    }

    /**
     * Test to verify clearNonBondedDevices().
     */
    @Test
    public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice2);
        assertThat(cachedDevice2).isNotNull();

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
        mCachedDeviceManager.clearNonBondedDevices();
        Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
        assertThat(devices).contains(cachedDevice1);
        assertThat(devices).contains(cachedDevice2);

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
        mCachedDeviceManager.clearNonBondedDevices();
        devices = mCachedDeviceManager.getCachedDevicesCopy();
        assertThat(devices).contains(cachedDevice1);
        assertThat(devices).doesNotContain(cachedDevice2);

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
        mCachedDeviceManager.clearNonBondedDevices();
        devices = mCachedDeviceManager.getCachedDevicesCopy();
        assertThat(devices).doesNotContain(cachedDevice1);
        assertThat(devices).doesNotContain(cachedDevice2);
    }

    /**
     * Test to verify onBtClassChanged().
     */
    @Test
    public void testOnBtClassChanged_validBtClass_classChanged() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);

        final BluetoothClass newBluetoothClass = DEVICE_CLASS_2;
        when(mDevice1.getBluetoothClass()).thenReturn(newBluetoothClass);
        mCachedDeviceManager.onBtClassChanged(mDevice1);
        assertThat(cachedDevice1.getBtClass()).isEqualTo(newBluetoothClass);
    }

    /**
     * Test to verify onDeviceDisappeared().
     */
    @Test
    public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isFalse();

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
        assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
    }

    /**
     * Test to verify onActiveDeviceChanged().
     */
    @Test
    public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice1);
        assertThat(cachedDevice1).isNotNull();
        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
                mLocalProfileManager, mDevice2);
        assertThat(cachedDevice2).isNotNull();

        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);

        // Connect both devices for A2DP and HFP
        cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
        cachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
        cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
        cachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);

        // Verify that both devices are connected and none is Active
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();

        // The first device is active for A2DP, the second device is active for HFP
        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();

        // The first device is active for A2DP and HFP
        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();

        // The second device is active for A2DP and HFP
        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.A2DP);
        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();

        // No active device for A2DP
        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();

        // No active device for HFP
        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
    }
}
+11 −11
Original line number Diff line number Diff line
@@ -152,7 +152,7 @@ public class CachedBluetoothDeviceTest {
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");

        // Set device as Active for A2DP and test connection state summary
        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");

        // Test with battery level
@@ -168,7 +168,7 @@ public class CachedBluetoothDeviceTest {
        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
        // Set A2DP profile to be connected, Active and test connection state summary
        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");

        // Set A2DP profile to be disconnected and test connection state summary
@@ -184,7 +184,7 @@ public class CachedBluetoothDeviceTest {
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");

        // Set device as Active for HFP and test connection state summary
        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");

        // Test with battery level
@@ -200,7 +200,7 @@ public class CachedBluetoothDeviceTest {
        mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
        // Set HFP profile to be connected, Active and test connection state summary
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");

        // Set HFP profile to be disconnected and test connection state summary
@@ -217,8 +217,8 @@ public class CachedBluetoothDeviceTest {
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");

        // Set device as Active for A2DP and HFP and test connection state summary
        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");

        // Test with battery level
@@ -227,16 +227,16 @@ public class CachedBluetoothDeviceTest {
                "Connected, battery 10%, active");

        // Disconnect A2DP only and test connection state summary
        mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                "Connected (no media), battery 10%, active(phone)");

        // Disconnect HFP only and test connection state summary
        mCachedDevice.setActiveDevice(false, BluetoothProfile.HEADSET);
        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                "Connected (no phone), battery 10%, active(media)");

@@ -245,8 +245,8 @@ public class CachedBluetoothDeviceTest {
        // Set A2DP and HFP profiles to be connected, Active and test connection state summary
        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
        mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
        mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");

        // Set A2DP and HFP profiles to be disconnected and test connection state summary