1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.bluetooth; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.bluetooth.BluetoothA2dp; 29 import android.bluetooth.BluetoothAdapter; 30 import android.bluetooth.BluetoothDevice; 31 import android.bluetooth.BluetoothHeadset; 32 import android.bluetooth.BluetoothHearingAid; 33 import android.bluetooth.BluetoothPan; 34 import android.bluetooth.BluetoothProfile; 35 import android.bluetooth.BluetoothUuid; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.os.ParcelUuid; 39 40 import java.util.ArrayList; 41 import java.util.List; 42 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 import org.robolectric.RobolectricTestRunner; 49 import org.robolectric.RuntimeEnvironment; 50 import org.robolectric.annotation.Config; 51 52 @RunWith(RobolectricTestRunner.class) 53 @Config(resourceDir = "../../res") 54 public class LocalBluetoothProfileManagerTest { 55 @Mock 56 private CachedBluetoothDeviceManager mDeviceManager; 57 @Mock 58 private BluetoothEventManager mEventManager; 59 @Mock 60 private LocalBluetoothAdapter mAdapter; 61 @Mock 62 private BluetoothDevice mDevice; 63 @Mock 64 private CachedBluetoothDevice mCachedBluetoothDevice; 65 66 private Context mContext; 67 private LocalBluetoothProfileManager mProfileManager; 68 private Intent mIntent; 69 70 @Before 71 public void setUp() { 72 MockitoAnnotations.initMocks(this); 73 mContext = spy(RuntimeEnvironment.application); 74 mEventManager = spy(new BluetoothEventManager(mAdapter, 75 mDeviceManager, mContext)); 76 when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON); 77 when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice); 78 } 79 80 /** 81 * Verify HID and HID Device profiles are not null without running updateUuids() 82 */ 83 @Test 84 public void constructor_initiateHidAndHidDeviceProfile() { 85 mProfileManager = 86 new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, mEventManager); 87 88 assertThat(mProfileManager.getHidProfile()).isNotNull(); 89 assertThat(mProfileManager.getHidDeviceProfile()).isNotNull(); 90 } 91 92 /** 93 * Verify updateLocalProfiles() for a local A2DP source adds A2dpProfile 94 */ 95 @Test 96 public void updateLocalProfiles_addA2dpToLocalProfiles() { 97 mProfileManager = 98 new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, mEventManager); 99 when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource}); 100 assertThat(mProfileManager.getA2dpProfile()).isNull(); 101 assertThat(mProfileManager.getHeadsetProfile()).isNull(); 102 103 ParcelUuid[] uuids = mAdapter.getUuids(); 104 mProfileManager.updateLocalProfiles(uuids); 105 106 assertThat(mProfileManager.getA2dpProfile()).isNotNull(); 107 assertThat(mProfileManager.getHeadsetProfile()).isNull(); 108 } 109 110 /** 111 * Verify updateProfiles() for a remote HID device updates profiles and removedProfiles 112 */ 113 @Test 114 public void updateProfiles_addHidProfileForRemoteDevice() { 115 mProfileManager = 116 new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, mEventManager); 117 ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.Hid}; 118 ParcelUuid[] localUuids = new ParcelUuid[]{}; 119 List<LocalBluetoothProfile> profiles = new ArrayList<>(); 120 List<LocalBluetoothProfile> removedProfiles = new ArrayList<>(); 121 122 mProfileManager.updateProfiles(uuids, localUuids, profiles, removedProfiles, false, 123 mDevice); 124 125 assertThat(mProfileManager.getHidProfile()).isNotNull(); 126 assertThat(profiles.contains(mProfileManager.getHidProfile())).isTrue(); 127 assertThat(removedProfiles.contains(mProfileManager.getHidProfile())).isFalse(); 128 } 129 130 /** 131 * Verify BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to 132 * profile connection state changed callback 133 */ 134 @Test 135 public void stateChangedHandler_receiveA2dpConnectionStateChanged_shouldDispatchCallback() { 136 when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource}); 137 mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, 138 mEventManager); 139 // Refer to BluetoothControllerImpl, it will call setReceiverHandler after 140 // LocalBluetoothProfileManager created. 141 mEventManager.setReceiverHandler(null); 142 mIntent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 143 mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 144 mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 145 mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 146 147 mContext.sendBroadcast(mIntent); 148 149 verify(mEventManager).dispatchProfileConnectionStateChanged( 150 mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP); 151 } 152 153 /** 154 * Verify BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to 155 * profile connection state changed callback 156 */ 157 @Test 158 public void stateChangedHandler_receiveHeadsetConnectionStateChanged_shouldDispatchCallback() { 159 when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.Handsfree_AG}); 160 mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, 161 mEventManager); 162 // Refer to BluetoothControllerImpl, it will call setReceiverHandler after 163 // LocalBluetoothProfileManager created. 164 mEventManager.setReceiverHandler(null); 165 mIntent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 166 mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 167 mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 168 mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 169 170 mContext.sendBroadcast(mIntent); 171 172 verify(mEventManager).dispatchProfileConnectionStateChanged(mCachedBluetoothDevice, 173 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEADSET); 174 } 175 176 /** 177 * Verify BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to 178 * profile connection state changed callback 179 */ 180 @Test 181 public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchCallback() { 182 ArrayList<Integer> supportProfiles = new ArrayList<>(); 183 supportProfiles.add(BluetoothProfile.HEARING_AID); 184 when(mAdapter.getSupportedProfiles()).thenReturn(supportProfiles); 185 when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.HearingAid}); 186 mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, 187 mEventManager); 188 // Refer to BluetoothControllerImpl, it will call setReceiverHandler after 189 // LocalBluetoothProfileManager created. 190 mEventManager.setReceiverHandler(null); 191 mIntent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); 192 mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 193 mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 194 mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 195 196 mContext.sendBroadcast(mIntent); 197 198 verify(mEventManager).dispatchProfileConnectionStateChanged(mCachedBluetoothDevice, 199 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); 200 } 201 202 /** 203 * Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuid will dispatch to 204 * profile connection state changed callback 205 */ 206 @Test 207 public void stateChangedHandler_receivePanConnectionStateChanged_shouldNotDispatchCallback() { 208 when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource}); 209 mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, 210 mEventManager); 211 // Refer to BluetoothControllerImpl, it will call setReceiverHandler after 212 // LocalBluetoothProfileManager created. 213 mEventManager.setReceiverHandler(null); 214 mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 215 mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 216 mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 217 mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 218 219 mContext.sendBroadcast(mIntent); 220 221 verify(mEventManager).dispatchProfileConnectionStateChanged( 222 any(CachedBluetoothDevice.class), anyInt(), anyInt()); 223 } 224 225 /** 226 * Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent without uuids will not dispatch to 227 * handler and refresh CachedBluetoothDevice 228 */ 229 @Test 230 public void stateChangedHandler_receivePanConnectionStateChangedWithoutUuid_shouldNotRefresh() { 231 when(mAdapter.getUuids()).thenReturn(null); 232 mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, 233 mEventManager); 234 // Refer to BluetoothControllerImpl, it will call setReceiverHandler after 235 // LocalBluetoothProfileManager created. 236 mEventManager.setReceiverHandler(null); 237 mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 238 mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 239 mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 240 mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 241 242 mContext.sendBroadcast(mIntent); 243 244 verify(mCachedBluetoothDevice).refresh(); 245 } 246 247 /** 248 * Verify BluetoothPan.ACTION_CONNECTION_STATE_CHANGED intent with uuids will dispatch to 249 * handler and refresh CachedBluetoothDevice 250 */ 251 @Test 252 public void stateChangedHandler_receivePanConnectionStateChangedWithUuids_shouldRefresh() { 253 when(mAdapter.getUuids()).thenReturn(new ParcelUuid[]{BluetoothUuid.AudioSource}); 254 mProfileManager = new LocalBluetoothProfileManager(mContext, mAdapter, mDeviceManager, 255 mEventManager); 256 // Refer to BluetoothControllerImpl, it will call setReceiverHandler after 257 // LocalBluetoothProfileManager created. 258 mEventManager.setReceiverHandler(null); 259 mIntent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED); 260 mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 261 mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING); 262 mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 263 264 mContext.sendBroadcast(mIntent); 265 266 verify(mCachedBluetoothDevice).refresh(); 267 } 268 } 269