1 /* 2 * Copyright (C) 2016 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.bluetooth.hfpclient; 18 19 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; 20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 21 import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE; 22 import static android.bluetooth.BluetoothProfile.EXTRA_STATE; 23 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; 24 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; 25 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; 26 import static android.content.pm.PackageManager.FEATURE_WATCH; 27 28 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; 29 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; 30 31 import static com.android.bluetooth.TestUtils.MockitoRule; 32 import static com.android.bluetooth.TestUtils.getTestDevice; 33 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.AT_OK; 34 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.ENTER_PRIVATE_MODE; 35 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER; 36 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.VOICE_RECOGNITION_START; 37 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.VOICE_RECOGNITION_STOP; 38 39 import static com.google.common.truth.Truth.assertThat; 40 41 import static org.mockito.Mockito.*; 42 43 import android.app.BroadcastOptions; 44 import android.bluetooth.BluetoothAdapter; 45 import android.bluetooth.BluetoothAssignedNumbers; 46 import android.bluetooth.BluetoothDevice; 47 import android.bluetooth.BluetoothHeadsetClient; 48 import android.bluetooth.BluetoothHeadsetClientCall; 49 import android.bluetooth.BluetoothSinkAudioPolicy; 50 import android.bluetooth.BluetoothStatusCodes; 51 import android.content.Intent; 52 import android.content.pm.PackageManager; 53 import android.content.res.Resources; 54 import android.media.AudioManager; 55 import android.os.Bundle; 56 import android.os.Looper; 57 import android.os.Message; 58 import android.platform.test.annotations.EnableFlags; 59 import android.platform.test.flag.junit.SetFlagsRule; 60 import android.util.Pair; 61 62 import androidx.test.filters.SmallTest; 63 import androidx.test.runner.AndroidJUnit4; 64 65 import com.android.bluetooth.R; 66 import com.android.bluetooth.TestLooper; 67 import com.android.bluetooth.btservice.AdapterService; 68 import com.android.bluetooth.btservice.RemoteDevices; 69 import com.android.bluetooth.flags.Flags; 70 import com.android.bluetooth.hfp.HeadsetService; 71 72 import org.hamcrest.Matcher; 73 import org.hamcrest.core.AllOf; 74 import org.junit.After; 75 import org.junit.Before; 76 import org.junit.Rule; 77 import org.junit.Test; 78 import org.junit.runner.RunWith; 79 import org.mockito.ArgumentCaptor; 80 import org.mockito.InOrder; 81 import org.mockito.Mock; 82 import org.mockito.hamcrest.MockitoHamcrest; 83 84 import java.util.List; 85 import java.util.Set; 86 87 /** Test cases for {@link HeadsetClientStateMachine}. */ 88 @SmallTest 89 @RunWith(AndroidJUnit4.class) 90 public class HeadsetClientStateMachineTest { 91 private final BluetoothDevice mTestDevice = getTestDevice(42); 92 93 private TestHeadsetClientStateMachine mHeadsetClientStateMachine; 94 private InOrder mInOrder; 95 private TestLooper mTestLooper; 96 97 @Rule public final MockitoRule mMockitoRule = new MockitoRule(); 98 99 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 100 101 @Mock private AdapterService mAdapterService; 102 @Mock private Resources mMockHfpResources; 103 @Mock private HeadsetService mHeadsetService; 104 @Mock private HeadsetClientService mHeadsetClientService; 105 @Mock private AudioManager mAudioManager; 106 @Mock private RemoteDevices mRemoteDevices; 107 @Mock private PackageManager mPackageManager; 108 @Mock private NativeInterface mNativeInterface; 109 110 @Before setUp()111 public void setUp() throws Exception { 112 mInOrder = inOrder(mHeadsetClientService); 113 114 // Set a valid volume 115 doReturn(2).when(mAudioManager).getStreamVolume(anyInt()); 116 doReturn(10).when(mAudioManager).getStreamMaxVolume(anyInt()); 117 doReturn(1).when(mAudioManager).getStreamMinVolume(anyInt()); 118 119 doReturn(mAudioManager).when(mHeadsetClientService).getAudioManager(); 120 doReturn(mMockHfpResources).when(mHeadsetClientService).getResources(); 121 doReturn(mPackageManager).when(mHeadsetClientService).getPackageManager(); 122 doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetClientService).getConnectionPolicy(any()); 123 124 doReturn(true).when(mMockHfpResources).getBoolean(eq(R.bool.hfp_clcc_poll_during_call)); 125 doReturn(2000) 126 .when(mMockHfpResources) 127 .getInteger(eq(R.integer.hfp_clcc_poll_interval_during_call)); 128 129 doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices(); 130 doReturn(true).when(mNativeInterface).sendAndroidAt(any(), anyString()); 131 132 doReturn(true).when(mNativeInterface).disconnect(any(BluetoothDevice.class)); 133 134 mTestLooper = new TestLooper(); 135 mHeadsetClientStateMachine = 136 new TestHeadsetClientStateMachine( 137 mAdapterService, 138 mHeadsetClientService, 139 mHeadsetService, 140 mTestLooper.getLooper(), 141 mNativeInterface); 142 mHeadsetClientStateMachine.start(); 143 mTestLooper.dispatchAll(); 144 } 145 146 @After tearDown()147 public void tearDown() throws Exception { 148 mTestLooper.dispatchAll(); 149 mHeadsetClientStateMachine.allowConnect = null; 150 mHeadsetClientStateMachine.doQuit(); 151 mTestLooper.dispatchAll(); 152 verifyNoMoreInteractions(mHeadsetService); 153 } 154 155 /** Test that default state is disconnected */ 156 @Test testDefaultDisconnectedState()157 public void testDefaultDisconnectedState() { 158 assertThat(mHeadsetClientStateMachine.getConnectionState(null)) 159 .isEqualTo(STATE_DISCONNECTED); 160 } 161 162 /** Test that an incoming connection with low priority is rejected */ 163 @Test testIncomingPriorityReject()164 public void testIncomingPriorityReject() { 165 doReturn(CONNECTION_POLICY_FORBIDDEN) 166 .when(mHeadsetClientService) 167 .getConnectionPolicy(any()); 168 169 // Inject an event for when incoming connection is requested 170 StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 171 connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 172 connStCh.device = mTestDevice; 173 sendMessage(StackEvent.STACK_EVENT, connStCh); 174 175 // Verify that only DISCONNECTED -> DISCONNECTED broadcast is fired 176 verifySendBroadcastMultiplePermissions( 177 hasAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED), 178 hasExtra(EXTRA_STATE, STATE_DISCONNECTED), 179 hasExtra(EXTRA_PREVIOUS_STATE, STATE_DISCONNECTED)); 180 assertThat(mHeadsetClientStateMachine.getCurrentState()) 181 .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); 182 } 183 184 /** Test that an incoming connection with high priority is accepted */ 185 @Test testIncomingPriorityAccept()186 public void testIncomingPriorityAccept() { 187 // Inject an event for when incoming connection is requested 188 StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 189 connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 190 connStCh.device = mTestDevice; 191 sendMessage(StackEvent.STACK_EVENT, connStCh); 192 193 // Verify that one connection state broadcast is executed 194 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); 195 196 assertThat(mHeadsetClientStateMachine.getCurrentState()) 197 .isInstanceOf(HeadsetClientStateMachine.Connecting.class); 198 199 // Send a message to trigger SLC connection 200 StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 201 slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; 202 slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; 203 slcEvent.device = mTestDevice; 204 sendMessage(StackEvent.STACK_EVENT, slcEvent); 205 206 setUpAndroidAt(false); 207 208 // Verify that one connection state broadcast is executed 209 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTED)); 210 assertThat(mHeadsetClientStateMachine.getCurrentState()) 211 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 212 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); 213 } 214 215 /** Test that an incoming connection that times out */ 216 @Test testIncomingTimeout()217 public void testIncomingTimeout() { 218 // Inject an event for when incoming connection is requested 219 StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 220 connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 221 connStCh.device = mTestDevice; 222 sendMessage(StackEvent.STACK_EVENT, connStCh); 223 224 // Verify that one connection state broadcast is executed 225 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); 226 assertThat(mHeadsetClientStateMachine.getCurrentState()) 227 .isInstanceOf(HeadsetClientStateMachine.Connecting.class); 228 229 // Trigger timeout 230 mTestLooper.moveTimeForward(HeadsetClientStateMachine.CONNECTING_TIMEOUT_MS); 231 mTestLooper.dispatchAll(); 232 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_DISCONNECTED)); 233 assertThat(mHeadsetClientStateMachine.getCurrentState()) 234 .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); 235 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 236 } 237 processAndroidSlcCommand(String command)238 private boolean processAndroidSlcCommand(String command) { 239 return mHeadsetClientStateMachine.processAndroidSlcCommand(command, mTestDevice); 240 } 241 242 @Test testProcessAndroidSlcCommand()243 public void testProcessAndroidSlcCommand() { 244 initToConnectedState(); 245 246 // True on correct AT command and BluetoothDevice 247 assertThat(processAndroidSlcCommand("+ANDROID: (SINKAUDIOPOLICY)")).isTrue(); 248 assertThat(processAndroidSlcCommand("+ANDROID: ()")).isTrue(); 249 assertThat(processAndroidSlcCommand("+ANDROID: (,,,)")).isTrue(); 250 assertThat(processAndroidSlcCommand("+ANDROID: (SINKAUDIOPOLICY),(OTHERFEATURE)")).isTrue(); 251 assertThat( 252 processAndroidSlcCommand( 253 "+ANDROID: (SINKAUDIOPOLICY),(OTHERFEATURE,1,2,3),(1,2,3)")) 254 .isTrue(); 255 assertThat(processAndroidSlcCommand("+ANDROID: 123")).isTrue(); 256 assertThat(processAndroidSlcCommand("+ANDROID: ")).isTrue(); 257 258 // False on incorrect AT command format 259 assertThat(processAndroidSlcCommand("+ANDROID= (SINKAUDIOPOLICY)")).isFalse(); 260 assertThat(processAndroidSlcCommand("RANDOM ^%$# STRING")).isFalse(); 261 assertThat(processAndroidSlcCommand("")).isFalse(); 262 263 // False on incorrect BluetoothDevice 264 assertThat( 265 mHeadsetClientStateMachine.processAndroidSlcCommand( 266 "+ANDROID: (SINKAUDIOPOLICY)", getTestDevice(123))) 267 .isFalse(); 268 } 269 270 @Test testProcessAndroidSlcCommand_checkSinkAudioPolicy()271 public void testProcessAndroidSlcCommand_checkSinkAudioPolicy() { 272 initToConnectedState(); 273 274 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); 275 assertThat(processAndroidSlcCommand("RANDOM ^%$# STRING")).isFalse(); 276 assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) 277 .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); 278 279 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); 280 assertThat(processAndroidSlcCommand("+ANDROID= (SINKAUDIOPOLICY)")).isFalse(); 281 assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) 282 .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); 283 284 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); 285 assertThat(processAndroidSlcCommand("+ANDROID: (SINKAUDIOPOLICY)")).isTrue(); 286 assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) 287 .isEqualTo(BluetoothStatusCodes.FEATURE_SUPPORTED); 288 } 289 290 /** Test that In Band Ringtone information is relayed from phone. */ 291 @Test testInBandRingtone()292 public void testInBandRingtone() { 293 assertThat(mHeadsetClientStateMachine.getInBandRing()).isFalse(); 294 295 // Inject an event for when incoming connection is requested 296 StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 297 connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 298 connStCh.device = mTestDevice; 299 sendMessage(StackEvent.STACK_EVENT, connStCh); 300 301 // Verify that one connection state broadcast is executed 302 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); 303 304 // Send a message to trigger SLC connection 305 StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 306 slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; 307 slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; 308 slcEvent.device = mTestDevice; 309 sendMessage(StackEvent.STACK_EVENT, slcEvent); 310 311 setUpAndroidAt(false); 312 313 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTED)); 314 315 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); 316 317 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_IN_BAND_RINGTONE); 318 event.valueInt = 0; 319 event.device = mTestDevice; 320 321 // Enable In Band Ring and verify state gets propagated. 322 StackEvent eventInBandRing = new StackEvent(StackEvent.EVENT_TYPE_IN_BAND_RINGTONE); 323 eventInBandRing.valueInt = 1; 324 eventInBandRing.device = mTestDevice; 325 sendMessage(StackEvent.STACK_EVENT, eventInBandRing); 326 verifySendBroadcast(hasExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 1)); 327 assertThat(mHeadsetClientStateMachine.getInBandRing()).isTrue(); 328 329 // Simulate a new incoming phone call 330 StackEvent eventCallStatusUpdated = new StackEvent(StackEvent.EVENT_TYPE_CLIP); 331 sendMessage(StackEvent.STACK_EVENT, eventCallStatusUpdated); 332 mInOrder.verify(mHeadsetClientService, never()) 333 .sendBroadcast(any(Intent.class), anyString(), any(Bundle.class)); 334 335 // Provide information about the new call 336 StackEvent eventIncomingCall = new StackEvent(StackEvent.EVENT_TYPE_CURRENT_CALLS); 337 eventIncomingCall.valueInt = 1; // index 338 eventIncomingCall.valueInt2 = 1; // direction 339 eventIncomingCall.valueInt3 = 4; // state 340 eventIncomingCall.valueInt4 = 0; // multi party 341 eventIncomingCall.valueString = "5551212"; // phone number 342 eventIncomingCall.device = mTestDevice; 343 344 sendMessage(StackEvent.STACK_EVENT, eventIncomingCall); 345 mInOrder.verify(mHeadsetClientService, never()) 346 .sendBroadcast(any(Intent.class), anyString(), any(Bundle.class)); 347 348 // Signal that the complete list of calls was received. 349 StackEvent eventCommandStatus = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 350 eventCommandStatus.valueInt = AT_OK; 351 sendMessage(StackEvent.STACK_EVENT, eventCommandStatus); 352 353 ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); 354 mInOrder.verify(mHeadsetClientService) 355 .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); 356 // Verify that the new call is being registered with the inBandRing flag set. 357 HfpClientCall clientCall = 358 (HfpClientCall) 359 intentArgument 360 .getValue() 361 .getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL); 362 assertThat(clientCall.isInBandRing()).isTrue(); 363 364 // Disable In Band Ring and verify state gets propagated. 365 eventInBandRing.valueInt = 0; 366 sendMessage(StackEvent.STACK_EVENT, eventInBandRing); 367 verifySendBroadcast(hasExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 0)); 368 assertThat(mHeadsetClientStateMachine.getInBandRing()).isFalse(); 369 } 370 371 /** Test that wearables use {@code BluetoothHeadsetClientCall} in intent. */ 372 @Test testWearablesUseBluetoothHeadsetClientCallInIntent()373 public void testWearablesUseBluetoothHeadsetClientCallInIntent() { 374 // Specify the watch form factor when package manager is asked 375 doReturn(true).when(mPackageManager).hasSystemFeature(FEATURE_WATCH); 376 377 // Skip over the Android AT commands to test this code path 378 doReturn(false).when(mNativeInterface).sendAndroidAt(any(), anyString()); 379 380 // Send an incoming connection event 381 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 382 event.device = mTestDevice; 383 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 384 sendMessage(StackEvent.STACK_EVENT, event); 385 386 // Send a message to trigger service level connection using the required ECS feature 387 event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 388 event.device = mTestDevice; 389 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; 390 event.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; 391 sendMessage(StackEvent.STACK_EVENT, event); 392 393 // Dial a phone call, which will fail as @{code dial} method is not specified in @{code 394 // mNativeInterface} mock and trigger a call state changed broadcast 395 sendMessage( 396 HeadsetClientStateMachine.DIAL_NUMBER, 397 new HfpClientCall( 398 mTestDevice, 399 0, 400 HfpClientCall.CALL_STATE_WAITING, 401 "1", 402 false, 403 false, 404 false)); 405 406 // Verify the broadcast 407 ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class); 408 mInOrder.verify(mHeadsetClientService) 409 .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class)); 410 411 // Verify that the parcelable extra has a legacy {@code BluetoothHeadsetClientCall} type for 412 // wearables. 413 Object clientCall = 414 (Object) 415 intentArgument 416 .getValue() 417 .getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL); 418 assertThat(clientCall).isInstanceOf(BluetoothHeadsetClientCall.class); 419 420 // To satisfy the @After verification 421 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); 422 } 423 424 /* Utility function to simulate HfpClient is connected. */ setUpHfpClientConnection()425 private void setUpHfpClientConnection() { 426 // Trigger an incoming connection is requested 427 StackEvent connStCh = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 428 connStCh.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 429 connStCh.device = mTestDevice; 430 sendMessage(StackEvent.STACK_EVENT, connStCh); 431 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTING)); 432 } 433 434 /* Utility function to simulate SLC connection. */ setUpServiceLevelConnection()435 private void setUpServiceLevelConnection() { 436 setUpServiceLevelConnection(false); 437 } 438 setUpServiceLevelConnection(boolean androidAtSupported)439 private void setUpServiceLevelConnection(boolean androidAtSupported) { 440 // Trigger SLC connection 441 StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 442 slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED; 443 slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS; 444 slcEvent.valueInt2 |= HeadsetClientHalConstants.PEER_FEAT_HF_IND; 445 slcEvent.device = mTestDevice; 446 sendMessage(StackEvent.STACK_EVENT, slcEvent); 447 448 setUpAndroidAt(androidAtSupported); 449 450 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_CONNECTED)); 451 assertThat(mHeadsetClientStateMachine.getCurrentState()) 452 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 453 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); 454 } 455 456 /** 457 * Set up and verify AT Android related commands and events. Make sure this method is invoked 458 * after SLC is setup. 459 */ setUpAndroidAt(boolean androidAtSupported)460 private void setUpAndroidAt(boolean androidAtSupported) { 461 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=?"); 462 if (androidAtSupported) { 463 // inject Android AT features 464 StackEvent unknownEvt = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); 465 unknownEvt.valueString = "+ANDROID: (SINKAUDIOPOLICY)"; 466 unknownEvt.device = mTestDevice; 467 sendMessage(StackEvent.STACK_EVENT, unknownEvt); 468 469 // receive CMD_RESULT OK after the Android AT command from remote 470 StackEvent cmdResEvt = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 471 cmdResEvt.valueInt = StackEvent.CMD_RESULT_TYPE_OK; 472 cmdResEvt.device = mTestDevice; 473 sendMessage(StackEvent.STACK_EVENT, cmdResEvt); 474 475 assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) 476 .isEqualTo(BluetoothStatusCodes.FEATURE_SUPPORTED); 477 } else { 478 // receive CMD_RESULT CME_ERROR due to remote not supporting Android AT 479 StackEvent cmdResEvt = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 480 cmdResEvt.valueInt = StackEvent.CMD_RESULT_TYPE_CME_ERROR; 481 cmdResEvt.device = mTestDevice; 482 sendMessage(StackEvent.STACK_EVENT, cmdResEvt); 483 484 assertThat(mHeadsetClientStateMachine.getAudioPolicyRemoteSupported()) 485 .isEqualTo(BluetoothStatusCodes.FEATURE_NOT_SUPPORTED); 486 } 487 } 488 489 /* Utility function: supported AT command should lead to native call */ runSupportedVendorAtCommand(String atCommand, int vendorId)490 private void runSupportedVendorAtCommand(String atCommand, int vendorId) { 491 setUpHfpClientConnection(); 492 setUpServiceLevelConnection(); 493 494 sendMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand); 495 496 verify(mNativeInterface) 497 .sendATCmd( 498 mTestDevice, 499 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_VENDOR_SPECIFIC_CMD, 500 0, 501 0, 502 atCommand); 503 } 504 505 /** Test: supported vendor specific command: set operation */ 506 @Test testSupportedVendorAtCommandSet()507 public void testSupportedVendorAtCommandSet() { 508 int vendorId = BluetoothAssignedNumbers.APPLE; 509 String atCommand = "+XAPL=ABCD-1234-0100,100"; 510 runSupportedVendorAtCommand(atCommand, vendorId); 511 } 512 513 /** Test: supported vendor specific command: read operation */ 514 @Test testSupportedVendorAtCommandRead()515 public void testSupportedVendorAtCommandRead() { 516 int vendorId = BluetoothAssignedNumbers.APPLE; 517 String atCommand = "+APLSIRI?"; 518 runSupportedVendorAtCommand(atCommand, vendorId); 519 } 520 521 /* utility function: unsupported vendor specific command shall be filtered. */ runUnsupportedVendorAtCommand(String atCommand, int vendorId)522 public void runUnsupportedVendorAtCommand(String atCommand, int vendorId) { 523 setUpHfpClientConnection(); 524 setUpServiceLevelConnection(); 525 526 sendMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, vendorId, 0, atCommand); 527 528 verify(mNativeInterface, never()).sendATCmd(any(), anyInt(), anyInt(), anyInt(), any()); 529 } 530 531 /** Test: unsupported vendor specific command shall be filtered: bad command code */ 532 @Test testUnsupportedVendorAtCommandBadCode()533 public void testUnsupportedVendorAtCommandBadCode() { 534 String atCommand = "+XAAPL=ABCD-1234-0100,100"; 535 int vendorId = BluetoothAssignedNumbers.APPLE; 536 runUnsupportedVendorAtCommand(atCommand, vendorId); 537 } 538 539 /** Test: unsupported vendor specific command shall be filtered: no back to back command */ 540 @Test testUnsupportedVendorAtCommandBackToBack()541 public void testUnsupportedVendorAtCommandBackToBack() { 542 String atCommand = "+XAPL=ABCD-1234-0100,100; +XAPL=ab"; 543 int vendorId = BluetoothAssignedNumbers.APPLE; 544 runUnsupportedVendorAtCommand(atCommand, vendorId); 545 } 546 547 /* Utility test function: supported vendor specific event 548 * shall lead to broadcast intent 549 */ runSupportedVendorEvent( int vendorId, String vendorEventCode, String vendorEventArgument)550 private void runSupportedVendorEvent( 551 int vendorId, String vendorEventCode, String vendorEventArgument) { 552 setUpHfpClientConnection(); 553 setUpServiceLevelConnection(); 554 555 // Simulate a known event arrive 556 String vendorEvent = vendorEventCode + vendorEventArgument; 557 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); 558 event.device = mTestDevice; 559 event.valueString = vendorEvent; 560 sendMessage(StackEvent.STACK_EVENT, event); 561 562 // Validate broadcast intent 563 verifySendBroadcast( 564 hasAction(BluetoothHeadsetClient.ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT), 565 hasExtra(BluetoothHeadsetClient.EXTRA_VENDOR_ID, vendorId), 566 hasExtra(BluetoothHeadsetClient.EXTRA_VENDOR_EVENT_CODE, vendorEventCode), 567 hasExtra(BluetoothHeadsetClient.EXTRA_VENDOR_EVENT_FULL_ARGS, vendorEvent)); 568 } 569 570 /** Test: supported vendor specific response: response to read command */ 571 @Test testSupportedVendorEventReadResponse()572 public void testSupportedVendorEventReadResponse() { 573 final int vendorId = BluetoothAssignedNumbers.APPLE; 574 final String vendorResponseCode = "+XAPL="; 575 final String vendorResponseArgument = "iPhone,2"; 576 runSupportedVendorEvent(vendorId, vendorResponseCode, vendorResponseArgument); 577 } 578 579 /** Test: supported vendor specific response: response to test command */ 580 @Test testSupportedVendorEventTestResponse()581 public void testSupportedVendorEventTestResponse() { 582 final int vendorId = BluetoothAssignedNumbers.APPLE; 583 final String vendorResponseCode = "+APLSIRI:"; 584 final String vendorResponseArgumentWithSpace = " 2"; 585 runSupportedVendorEvent(vendorId, vendorResponseCode, vendorResponseArgumentWithSpace); 586 } 587 588 /* Utility test function: unsupported vendor specific response shall be filtered out*/ runUnsupportedVendorEvent( int vendorId, String vendorEventCode, String vendorEventArgument)589 public void runUnsupportedVendorEvent( 590 int vendorId, String vendorEventCode, String vendorEventArgument) { 591 setUpHfpClientConnection(); 592 setUpServiceLevelConnection(); 593 594 // Simulate an unknown event arrive 595 String vendorEvent = vendorEventCode + vendorEventArgument; 596 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); 597 event.device = mTestDevice; 598 event.valueString = vendorEvent; 599 sendMessage(StackEvent.STACK_EVENT, event); 600 601 // Validate no broadcast intent 602 verify(mHeadsetClientService, atMost(2)) 603 .sendBroadcast(any(), anyString(), any(Bundle.class)); 604 } 605 606 /** Test unsupported vendor response: bad read response */ 607 @Test testUnsupportedVendorEventBadReadResponse()608 public void testUnsupportedVendorEventBadReadResponse() { 609 final int vendorId = BluetoothAssignedNumbers.APPLE; 610 final String vendorResponseCode = "+XAAPL="; 611 final String vendorResponseArgument = "iPhone,2"; 612 runUnsupportedVendorEvent(vendorId, vendorResponseCode, vendorResponseArgument); 613 } 614 615 /** Test unsupported vendor response: bad test response */ 616 @Test testUnsupportedVendorEventBadTestResponse()617 public void testUnsupportedVendorEventBadTestResponse() { 618 final int vendorId = BluetoothAssignedNumbers.APPLE; 619 final String vendorResponseCode = "+AAPLSIRI:"; 620 final String vendorResponseArgument = "2"; 621 runUnsupportedVendorEvent(vendorId, vendorResponseCode, vendorResponseArgument); 622 } 623 624 /** Test voice recognition state change broadcast. */ 625 @Test testVoiceRecognitionStateChange()626 public void testVoiceRecognitionStateChange() { 627 doReturn(true).when(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class)); 628 doReturn(true).when(mNativeInterface).stopVoiceRecognition(any(BluetoothDevice.class)); 629 630 setUpHfpClientConnection(); 631 setUpServiceLevelConnection(); 632 633 // Simulate a voice recognition start 634 sendMessage(VOICE_RECOGNITION_START); 635 636 // Signal that the complete list of actions was received. 637 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 638 event.device = mTestDevice; 639 event.valueInt = AT_OK; 640 sendMessage(StackEvent.STACK_EVENT, event); 641 642 verifySendBroadcast( 643 hasAction(BluetoothHeadsetClient.ACTION_AG_EVENT), 644 hasExtra( 645 BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 646 HeadsetClientHalConstants.VR_STATE_STARTED)); 647 648 // Simulate a voice recognition stop 649 sendMessage(VOICE_RECOGNITION_STOP); 650 651 // Signal that the complete list of actions was received. 652 event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 653 event.device = mTestDevice; 654 event.valueInt = AT_OK; 655 sendMessage(StackEvent.STACK_EVENT, event); 656 657 verifySendBroadcast( 658 hasAction(BluetoothHeadsetClient.ACTION_AG_EVENT), 659 hasExtra( 660 BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 661 HeadsetClientHalConstants.VR_STATE_STOPPED)); 662 } 663 664 /** Test send BIEV command */ 665 @Test testSendBIEVCommand()666 public void testSendBIEVCommand() { 667 setUpHfpClientConnection(); 668 setUpServiceLevelConnection(); 669 670 int indicator_id = 2; 671 int indicator_value = 50; 672 673 sendMessage(HeadsetClientStateMachine.SEND_BIEV, indicator_id, indicator_value); 674 675 verify(mNativeInterface) 676 .sendATCmd( 677 mTestDevice, 678 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_BIEV, 679 indicator_id, 680 indicator_value, 681 null); 682 } 683 684 /** 685 * Test state machine shall try to send AT+BIEV command to AG to update an init battery level. 686 */ 687 @Test testSendBatteryUpdateIndicatorWhenConnect()688 public void testSendBatteryUpdateIndicatorWhenConnect() { 689 setUpHfpClientConnection(); 690 setUpServiceLevelConnection(); 691 692 verify(mHeadsetClientService).updateBatteryLevel(); 693 } 694 695 @Test testBroadcastAudioState()696 public void testBroadcastAudioState() { 697 mHeadsetClientStateMachine.broadcastAudioState( 698 mTestDevice, 699 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED, 700 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 701 702 mInOrder.verify(mHeadsetClientService).sendBroadcast(any(), any(), any()); 703 } 704 705 @Test testCallsInState()706 public void testCallsInState() { 707 HfpClientCall call = 708 new HfpClientCall( 709 mTestDevice, 0, HfpClientCall.CALL_STATE_WAITING, "1", false, false, false); 710 mHeadsetClientStateMachine.mCalls.put(0, call); 711 712 assertThat(mHeadsetClientStateMachine.callsInState(HfpClientCall.CALL_STATE_WAITING)) 713 .isEqualTo(1); 714 } 715 716 @Test testEnterPrivateMode()717 public void testEnterPrivateMode() { 718 HfpClientCall call = 719 new HfpClientCall( 720 mTestDevice, 0, HfpClientCall.CALL_STATE_ACTIVE, "1", true, false, false); 721 mHeadsetClientStateMachine.mCalls.put(0, call); 722 doReturn(true) 723 .when(mNativeInterface) 724 .handleCallAction(null, HeadsetClientHalConstants.CALL_ACTION_CHLD_2X, 0); 725 726 mHeadsetClientStateMachine.enterPrivateMode(0); 727 728 Pair expectedPair = new Pair<Integer, Object>(ENTER_PRIVATE_MODE, call); 729 assertThat(mHeadsetClientStateMachine.mQueuedActions.peek()).isEqualTo(expectedPair); 730 } 731 732 @Test testExplicitCallTransfer()733 public void testExplicitCallTransfer() { 734 HfpClientCall callOne = 735 new HfpClientCall( 736 mTestDevice, 0, HfpClientCall.CALL_STATE_ACTIVE, "1", true, false, false); 737 HfpClientCall callTwo = 738 new HfpClientCall( 739 mTestDevice, 0, HfpClientCall.CALL_STATE_ACTIVE, "1", true, false, false); 740 mHeadsetClientStateMachine.mCalls.put(0, callOne); 741 mHeadsetClientStateMachine.mCalls.put(1, callTwo); 742 doReturn(true) 743 .when(mNativeInterface) 744 .handleCallAction(null, HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1); 745 746 mHeadsetClientStateMachine.explicitCallTransfer(); 747 748 Pair expectedPair = new Pair<Integer, Object>(EXPLICIT_CALL_TRANSFER, 0); 749 assertThat(mHeadsetClientStateMachine.mQueuedActions.peek()).isEqualTo(expectedPair); 750 } 751 752 @Test testSetAudioRouteAllowed()753 public void testSetAudioRouteAllowed() { 754 mHeadsetClientStateMachine.setAudioRouteAllowed(true); 755 756 assertThat(mHeadsetClientStateMachine.getAudioRouteAllowed()).isTrue(); 757 758 // Case 1: if remote is not supported 759 // Expect: Should not send +ANDROID to remote 760 mHeadsetClientStateMachine.mCurrentDevice = mTestDevice; 761 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); 762 verify(mNativeInterface, never()) 763 .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,0,0"); 764 765 // Case 2: if remote is supported and mForceSetAudioPolicyProperty is false 766 // Expect: Should send +ANDROID=SINKAUDIOPOLICY,1,0,0 to remote 767 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(true); 768 mHeadsetClientStateMachine.setForceSetAudioPolicyProperty(false); 769 mHeadsetClientStateMachine.setAudioRouteAllowed(true); 770 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,0,0"); 771 772 mHeadsetClientStateMachine.setAudioRouteAllowed(false); 773 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,2,0,0"); 774 775 // Case 3: if remote is supported and mForceSetAudioPolicyProperty is true 776 // Expect: Should send +ANDROID=SINKAUDIOPOLICY,1,2,1 to remote 777 mHeadsetClientStateMachine.setForceSetAudioPolicyProperty(true); 778 mHeadsetClientStateMachine.setAudioRouteAllowed(true); 779 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,1,1"); 780 } 781 782 @Test testGetAudioState_withCurrentDeviceNull()783 public void testGetAudioState_withCurrentDeviceNull() { 784 assertThat(mHeadsetClientStateMachine.mCurrentDevice).isNull(); 785 786 assertThat(mHeadsetClientStateMachine.getAudioState(mTestDevice)) 787 .isEqualTo(BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); 788 } 789 790 @Test testGetAudioState_withCurrentDeviceNotNull()791 public void testGetAudioState_withCurrentDeviceNotNull() { 792 int audioState = 1; 793 mHeadsetClientStateMachine.mAudioState = audioState; 794 mHeadsetClientStateMachine.mCurrentDevice = mTestDevice; 795 796 assertThat(mHeadsetClientStateMachine.getAudioState(mTestDevice)).isEqualTo(audioState); 797 } 798 799 @Test testGetCall_withMatchingState()800 public void testGetCall_withMatchingState() { 801 HfpClientCall call = 802 new HfpClientCall( 803 mTestDevice, 0, HfpClientCall.CALL_STATE_ACTIVE, "1", true, false, false); 804 mHeadsetClientStateMachine.mCalls.put(0, call); 805 int[] states = new int[1]; 806 states[0] = HfpClientCall.CALL_STATE_ACTIVE; 807 808 assertThat(mHeadsetClientStateMachine.getCall(states)).isEqualTo(call); 809 } 810 811 @Test testGetCall_withNoMatchingState()812 public void testGetCall_withNoMatchingState() { 813 HfpClientCall call = 814 new HfpClientCall( 815 mTestDevice, 0, HfpClientCall.CALL_STATE_WAITING, "1", true, false, false); 816 mHeadsetClientStateMachine.mCalls.put(0, call); 817 int[] states = new int[1]; 818 states[0] = HfpClientCall.CALL_STATE_ACTIVE; 819 820 assertThat(mHeadsetClientStateMachine.getCall(states)).isNull(); 821 } 822 823 @Test testGetConnectionState_withNullDevice()824 public void testGetConnectionState_withNullDevice() { 825 assertThat(mHeadsetClientStateMachine.getConnectionState(null)) 826 .isEqualTo(STATE_DISCONNECTED); 827 } 828 829 @Test testGetConnectionState_withNonNullDevice()830 public void testGetConnectionState_withNonNullDevice() { 831 mHeadsetClientStateMachine.mCurrentDevice = mTestDevice; 832 833 assertThat(mHeadsetClientStateMachine.getConnectionState(mTestDevice)) 834 .isEqualTo(STATE_DISCONNECTED); 835 } 836 837 @Test testGetConnectionStateFromAudioState()838 public void testGetConnectionStateFromAudioState() { 839 assertThat( 840 HeadsetClientStateMachine.getConnectionStateFromAudioState( 841 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED)) 842 .isEqualTo(BluetoothAdapter.STATE_CONNECTED); 843 assertThat( 844 HeadsetClientStateMachine.getConnectionStateFromAudioState( 845 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING)) 846 .isEqualTo(BluetoothAdapter.STATE_CONNECTING); 847 assertThat( 848 HeadsetClientStateMachine.getConnectionStateFromAudioState( 849 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED)) 850 .isEqualTo(BluetoothAdapter.STATE_DISCONNECTED); 851 int invalidAudioState = 3; 852 assertThat(HeadsetClientStateMachine.getConnectionStateFromAudioState(invalidAudioState)) 853 .isEqualTo(BluetoothAdapter.STATE_DISCONNECTED); 854 } 855 856 @Test testGetCurrentAgEvents()857 public void testGetCurrentAgEvents() { 858 Bundle bundle = mHeadsetClientStateMachine.getCurrentAgEvents(); 859 860 assertThat(bundle.getString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO)) 861 .isEqualTo(mHeadsetClientStateMachine.mSubscriberInfo); 862 } 863 864 @Test testGetCurrentAgFeatures()865 public void testGetCurrentAgFeatures() { 866 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_3WAY; 867 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC; 868 Set<Integer> features = mHeadsetClientStateMachine.getCurrentAgFeatures(); 869 assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_3WAY)).isTrue(); 870 assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC)).isTrue(); 871 872 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_VREC; 873 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL; 874 features = mHeadsetClientStateMachine.getCurrentAgFeatures(); 875 assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_VREC)).isTrue(); 876 assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_REL)).isTrue(); 877 878 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_REJECT; 879 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL_ACC; 880 features = mHeadsetClientStateMachine.getCurrentAgFeatures(); 881 assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_REJECT)).isTrue(); 882 assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_REL_ACC)).isTrue(); 883 884 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_ECC; 885 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE; 886 features = mHeadsetClientStateMachine.getCurrentAgFeatures(); 887 assertThat(features.contains(HeadsetClientHalConstants.PEER_FEAT_ECC)).isTrue(); 888 assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_MERGE)).isTrue(); 889 890 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH; 891 features = mHeadsetClientStateMachine.getCurrentAgFeatures(); 892 assertThat(features.contains(HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH)).isTrue(); 893 } 894 895 @Test testGetCurrentAgFeaturesBundle()896 public void testGetCurrentAgFeaturesBundle() { 897 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_3WAY; 898 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC; 899 Bundle bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); 900 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING)) 901 .isTrue(); 902 assertThat( 903 bundle.getBoolean( 904 BluetoothHeadsetClient 905 .EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL)) 906 .isTrue(); 907 908 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_VREC; 909 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL; 910 bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); 911 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION)) 912 .isTrue(); 913 assertThat( 914 bundle.getBoolean( 915 BluetoothHeadsetClient 916 .EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL)) 917 .isTrue(); 918 919 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_REJECT; 920 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_REL_ACC; 921 bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); 922 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL)).isTrue(); 923 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT)) 924 .isTrue(); 925 926 mHeadsetClientStateMachine.mPeerFeatures = HeadsetClientHalConstants.PEER_FEAT_ECC; 927 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE; 928 bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); 929 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC)).isTrue(); 930 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE)).isTrue(); 931 932 mHeadsetClientStateMachine.mChldFeatures = HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH; 933 bundle = mHeadsetClientStateMachine.getCurrentAgFeaturesBundle(); 934 assertThat(bundle.getBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH)) 935 .isTrue(); 936 } 937 938 @Test testGetCurrentCalls()939 public void testGetCurrentCalls() { 940 HfpClientCall call = 941 new HfpClientCall( 942 mTestDevice, 0, HfpClientCall.CALL_STATE_WAITING, "1", true, false, false); 943 mHeadsetClientStateMachine.mCalls.put(0, call); 944 945 List<HfpClientCall> currentCalls = mHeadsetClientStateMachine.getCurrentCalls(); 946 947 assertThat(currentCalls.get(0)).isEqualTo(call); 948 } 949 assertName(int message, String message_name)950 private static void assertName(int message, String message_name) { 951 assertThat(HeadsetClientStateMachine.getMessageName(message)).isEqualTo(message_name); 952 } 953 954 @Test testGetMessageName()955 public void testGetMessageName() { 956 assertName(StackEvent.STACK_EVENT, "STACK_EVENT"); 957 assertName(HeadsetClientStateMachine.CONNECT, "CONNECT"); 958 assertName(HeadsetClientStateMachine.DISCONNECT, "DISCONNECT"); 959 assertName(HeadsetClientStateMachine.CONNECT_AUDIO, "CONNECT_AUDIO"); 960 assertName(HeadsetClientStateMachine.DISCONNECT_AUDIO, "DISCONNECT_AUDIO"); 961 assertName(VOICE_RECOGNITION_START, "VOICE_RECOGNITION_START"); 962 assertName(VOICE_RECOGNITION_STOP, "VOICE_RECOGNITION_STOP"); 963 assertName(HeadsetClientStateMachine.SET_MIC_VOLUME, "SET_MIC_VOLUME"); 964 assertName(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, "SET_SPEAKER_VOLUME"); 965 assertName(HeadsetClientStateMachine.DIAL_NUMBER, "DIAL_NUMBER"); 966 assertName(HeadsetClientStateMachine.ACCEPT_CALL, "ACCEPT_CALL"); 967 assertName(HeadsetClientStateMachine.REJECT_CALL, "REJECT_CALL"); 968 assertName(HeadsetClientStateMachine.HOLD_CALL, "HOLD_CALL"); 969 assertName(HeadsetClientStateMachine.TERMINATE_CALL, "TERMINATE_CALL"); 970 assertName(ENTER_PRIVATE_MODE, "ENTER_PRIVATE_MODE"); 971 assertName(HeadsetClientStateMachine.SEND_DTMF, "SEND_DTMF"); 972 assertName(EXPLICIT_CALL_TRANSFER, "EXPLICIT_CALL_TRANSFER"); 973 assertName(HeadsetClientStateMachine.DISABLE_NREC, "DISABLE_NREC"); 974 assertName(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, "SEND_VENDOR_AT_COMMAND"); 975 assertName(HeadsetClientStateMachine.SEND_BIEV, "SEND_BIEV"); 976 assertName(HeadsetClientStateMachine.QUERY_CURRENT_CALLS, "QUERY_CURRENT_CALLS"); 977 assertName(HeadsetClientStateMachine.QUERY_OPERATOR_NAME, "QUERY_OPERATOR_NAME"); 978 assertName(HeadsetClientStateMachine.SUBSCRIBER_INFO, "SUBSCRIBER_INFO"); 979 assertName(HeadsetClientStateMachine.CONNECTING_TIMEOUT, "CONNECTING_TIMEOUT"); 980 assertName(HeadsetClientStateMachine.DISCONNECTING_TIMEOUT, "DISCONNECTING_TIMEOUT"); 981 int unknownMessageInt = 55; 982 assertName(unknownMessageInt, "UNKNOWN(" + unknownMessageInt + ")"); 983 } 984 985 /** 986 * Tests and verify behavior of the case where remote device doesn't support At Android but 987 * tries to send audio policy. 988 */ 989 @Test testAndroidAtRemoteNotSupported_StateTransition_setAudioPolicy()990 public void testAndroidAtRemoteNotSupported_StateTransition_setAudioPolicy() { 991 setUpHfpClientConnection(); 992 setUpServiceLevelConnection(); 993 994 BluetoothSinkAudioPolicy dummyAudioPolicy = new BluetoothSinkAudioPolicy.Builder().build(); 995 mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); 996 verify(mNativeInterface, never()) 997 .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); 998 } 999 1000 @Test testSetGetCallAudioPolicy()1001 public void testSetGetCallAudioPolicy() { 1002 1003 setUpHfpClientConnection(); 1004 setUpServiceLevelConnection(true); 1005 1006 BluetoothSinkAudioPolicy dummyAudioPolicy = 1007 new BluetoothSinkAudioPolicy.Builder() 1008 .setCallEstablishPolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED) 1009 .setActiveDevicePolicyAfterConnection( 1010 BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) 1011 .setInBandRingtonePolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED) 1012 .build(); 1013 1014 // Test if not support audio policy feature 1015 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(false); 1016 mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); 1017 verify(mNativeInterface, never()) 1018 .sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,2,1"); 1019 assertThat(mHeadsetClientStateMachine.mQueuedActions).isEmpty(); 1020 1021 // Test setAudioPolicy 1022 mHeadsetClientStateMachine.setAudioPolicyRemoteSupported(true); 1023 mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); 1024 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,1,2,1"); 1025 assertThat(mHeadsetClientStateMachine.mQueuedActions).hasSize(1); 1026 mHeadsetClientStateMachine.mQueuedActions.clear(); 1027 1028 // Test if fail to sendAndroidAt 1029 doReturn(false).when(mNativeInterface).sendAndroidAt(any(), anyString()); 1030 mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy); 1031 assertThat(mHeadsetClientStateMachine.mQueuedActions).isEmpty(); 1032 } 1033 1034 @Test testTestDefaultAudioPolicy()1035 public void testTestDefaultAudioPolicy() { 1036 mHeadsetClientStateMachine.setForceSetAudioPolicyProperty(true); 1037 initToConnectedState(); 1038 1039 // Check if set default policy when Connecting -> Connected 1040 // The testing sys prop is 0. It is ok to check if set audio policy while leaving connecting 1041 // state. 1042 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); 1043 1044 // Check if won't set default policy when AudioOn -> Connected 1045 // Transit to AudioOn 1046 mHeadsetClientStateMachine.setAudioRouteAllowed(true); 1047 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1048 event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED; 1049 event.device = mTestDevice; 1050 sendMessage(StackEvent.STACK_EVENT, event); 1051 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1052 .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); 1053 1054 // Back to Connected 1055 event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1056 event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED; 1057 event.device = mTestDevice; 1058 sendMessage(StackEvent.STACK_EVENT, event); 1059 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1060 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 1061 1062 verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=SINKAUDIOPOLICY,0,0,0"); 1063 } 1064 1065 @Test testDumpDoesNotCrash()1066 public void testDumpDoesNotCrash() { 1067 mHeadsetClientStateMachine.dump(new StringBuilder()); 1068 } 1069 1070 @Test testProcessDisconnectMessage_onDisconnectedState()1071 public void testProcessDisconnectMessage_onDisconnectedState() { 1072 sendMessage(HeadsetClientStateMachine.DISCONNECT); 1073 assertThat(mHeadsetClientStateMachine.getConnectionState(mTestDevice)) 1074 .isEqualTo(STATE_DISCONNECTED); 1075 } 1076 1077 @Test testProcessConnectMessage_onDisconnectedState()1078 public void testProcessConnectMessage_onDisconnectedState() { 1079 doReturn(true).when(mNativeInterface).connect(any(BluetoothDevice.class)); 1080 sendMessageAndVerifyTransition( 1081 mHeadsetClientStateMachine.obtainMessage( 1082 HeadsetClientStateMachine.CONNECT, mTestDevice), 1083 HeadsetClientStateMachine.Connecting.class); 1084 } 1085 1086 @Test testStackEvent_toConnectingState_onDisconnectedState()1087 public void testStackEvent_toConnectingState_onDisconnectedState() { 1088 allowConnection(true); 1089 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1090 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 1091 event.device = mTestDevice; 1092 sendMessageAndVerifyTransition( 1093 mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event), 1094 HeadsetClientStateMachine.Connecting.class); 1095 } 1096 1097 @Test testStackEvent_toConnectingState_disallowConnection_onDisconnectedState()1098 public void testStackEvent_toConnectingState_disallowConnection_onDisconnectedState() { 1099 allowConnection(false); 1100 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1101 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 1102 event.device = mTestDevice; 1103 sendMessageAndVerifyTransition( 1104 mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event), 1105 HeadsetClientStateMachine.Disconnected.class); 1106 } 1107 1108 @Test testProcessConnectMessage_onConnectingState()1109 public void testProcessConnectMessage_onConnectingState() { 1110 initToConnectingState(); 1111 assertThat( 1112 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1113 HeadsetClientStateMachine.CONNECT)) 1114 .isFalse(); 1115 sendMessage(HeadsetClientStateMachine.CONNECT); 1116 assertThat( 1117 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1118 HeadsetClientStateMachine.CONNECT)) 1119 .isTrue(); 1120 } 1121 1122 @Test testProcessStackEvent_ConnectionStateChanged_Disconnected_onConnectingState()1123 public void testProcessStackEvent_ConnectionStateChanged_Disconnected_onConnectingState() { 1124 initToConnectingState(); 1125 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1126 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED; 1127 event.device = mTestDevice; 1128 sendMessageAndVerifyTransition( 1129 mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event), 1130 HeadsetClientStateMachine.Disconnected.class); 1131 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 1132 } 1133 1134 @Test testProcessStackEvent_ConnectionStateChanged_Connected_onConnectingState()1135 public void testProcessStackEvent_ConnectionStateChanged_Connected_onConnectingState() { 1136 initToConnectingState(); 1137 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1138 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED; 1139 event.device = mTestDevice; 1140 sendMessage(StackEvent.STACK_EVENT, event); 1141 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1142 .isInstanceOf(HeadsetClientStateMachine.Connecting.class); 1143 } 1144 1145 @Test testProcessStackEvent_ConnectionStateChanged_Connecting_onConnectingState()1146 public void testProcessStackEvent_ConnectionStateChanged_Connecting_onConnectingState() { 1147 initToConnectingState(); 1148 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1149 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING; 1150 event.device = mTestDevice; 1151 sendMessage(StackEvent.STACK_EVENT, event); 1152 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1153 .isInstanceOf(HeadsetClientStateMachine.Connecting.class); 1154 } 1155 1156 @Test testProcessStackEvent_Call_onConnectingState()1157 public void testProcessStackEvent_Call_onConnectingState() { 1158 initToConnectingState(); 1159 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALL); 1160 event.valueInt = StackEvent.EVENT_TYPE_CALL; 1161 event.device = mTestDevice; 1162 assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) 1163 .isFalse(); 1164 sendMessage(StackEvent.STACK_EVENT, event); 1165 assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) 1166 .isTrue(); 1167 } 1168 1169 @Test testProcessStackEvent_CmdResultWithEmptyQueuedActions_onConnectingState()1170 public void testProcessStackEvent_CmdResultWithEmptyQueuedActions_onConnectingState() { 1171 initToConnectingState(); 1172 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 1173 event.valueInt = StackEvent.CMD_RESULT_TYPE_OK; 1174 event.device = mTestDevice; 1175 sendMessage(StackEvent.STACK_EVENT, event); 1176 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1177 .isInstanceOf(HeadsetClientStateMachine.Connecting.class); 1178 } 1179 1180 @Test testProcessStackEvent_Unknown_onConnectingState()1181 public void testProcessStackEvent_Unknown_onConnectingState() { 1182 String atCommand = "+ANDROID: (SINKAUDIOPOLICY)"; 1183 1184 initToConnectingState(); 1185 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); 1186 event.valueString = atCommand; 1187 event.device = mTestDevice; 1188 sendMessage(StackEvent.STACK_EVENT, event); 1189 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1190 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 1191 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); 1192 } 1193 1194 @Test testProcessConnectTimeoutMessage_onConnectingState()1195 public void testProcessConnectTimeoutMessage_onConnectingState() { 1196 initToConnectingState(); 1197 sendMessageAndVerifyTransition( 1198 mHeadsetClientStateMachine.obtainMessage( 1199 HeadsetClientStateMachine.CONNECTING_TIMEOUT), 1200 HeadsetClientStateMachine.Disconnected.class); 1201 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 1202 } 1203 1204 @Test testProcessStackEvent_inBandRingTone_onConnectingState()1205 public void testProcessStackEvent_inBandRingTone_onConnectingState() { 1206 initToConnectingState(); 1207 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_IN_BAND_RINGTONE); 1208 event.valueInt = StackEvent.EVENT_TYPE_IN_BAND_RINGTONE; 1209 event.device = mTestDevice; 1210 assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) 1211 .isFalse(); 1212 sendMessage(StackEvent.STACK_EVENT, event); 1213 assertThat(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(StackEvent.STACK_EVENT)) 1214 .isTrue(); 1215 } 1216 1217 @Test testProcessConnectMessage_onConnectedState()1218 public void testProcessConnectMessage_onConnectedState() { 1219 initToConnectedState(); 1220 sendMessage(HeadsetClientStateMachine.CONNECT); 1221 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1222 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 1223 } 1224 1225 @Test testProcessDisconnectMessage_onConnectedState()1226 public void testProcessDisconnectMessage_onConnectedState() { 1227 initToConnectedState(); 1228 sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); 1229 verify(mNativeInterface).disconnect(any(BluetoothDevice.class)); 1230 } 1231 1232 @Test 1233 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testConnectedState_ProcessDisconnectMessage_TransitionToDisconnecting()1234 public void testConnectedState_ProcessDisconnectMessage_TransitionToDisconnecting() { 1235 initToDisconnectingState(); 1236 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1237 .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class); 1238 } 1239 1240 @Test 1241 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testProcessStackEvent_ConnectionStateChanged_Disconnected_onConnectedState()1242 public void testProcessStackEvent_ConnectionStateChanged_Disconnected_onConnectedState() { 1243 initToConnectedState(); 1244 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1245 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED; 1246 event.device = mTestDevice; 1247 sendMessage(StackEvent.STACK_EVENT, event); 1248 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1249 .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); 1250 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 1251 } 1252 1253 @Test testProcessConnectAudioMessage_onConnectedState()1254 public void testProcessConnectAudioMessage_onConnectedState() { 1255 initToConnectedState(); 1256 sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); 1257 verify(mNativeInterface).connectAudio(any(BluetoothDevice.class)); 1258 } 1259 1260 @Test testProcessDisconnectAudioMessage_onConnectedState()1261 public void testProcessDisconnectAudioMessage_onConnectedState() { 1262 initToConnectedState(); 1263 sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 1264 verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class)); 1265 } 1266 1267 @Test testProcessVoiceRecognitionStartMessage_onConnectedState()1268 public void testProcessVoiceRecognitionStartMessage_onConnectedState() { 1269 initToConnectedState(); 1270 sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START); 1271 verify(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class)); 1272 } 1273 1274 @Test testProcessDisconnectMessage_onAudioOnState()1275 public void testProcessDisconnectMessage_onAudioOnState() { 1276 initToAudioOnState(); 1277 assertThat( 1278 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1279 HeadsetClientStateMachine.DISCONNECT)) 1280 .isFalse(); 1281 sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); 1282 assertThat( 1283 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1284 HeadsetClientStateMachine.DISCONNECT)) 1285 .isTrue(); 1286 } 1287 1288 @Test testProcessDisconnectAudioMessage_onAudioOnState()1289 public void testProcessDisconnectAudioMessage_onAudioOnState() { 1290 initToAudioOnState(); 1291 sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 1292 verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class)); 1293 } 1294 1295 @Test testProcessHoldCall_onAudioOnState()1296 public void testProcessHoldCall_onAudioOnState() { 1297 initToAudioOnState(); 1298 HfpClientCall call = 1299 new HfpClientCall( 1300 mTestDevice, 0, HfpClientCall.CALL_STATE_ACTIVE, "1", true, false, false); 1301 mHeadsetClientStateMachine.mCalls.put(0, call); 1302 int[] states = new int[1]; 1303 states[0] = HfpClientCall.CALL_STATE_ACTIVE; 1304 sendMessage(HeadsetClientStateMachine.HOLD_CALL); 1305 verify(mNativeInterface).handleCallAction(any(BluetoothDevice.class), anyInt(), eq(0)); 1306 } 1307 1308 @Test testProcessStackEvent_ConnectionStateChanged_onAudioOnState()1309 public void testProcessStackEvent_ConnectionStateChanged_onAudioOnState() { 1310 initToAudioOnState(); 1311 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1312 .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); 1313 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1314 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED; 1315 event.device = mTestDevice; 1316 sendMessage(StackEvent.STACK_EVENT, event); 1317 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1318 .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); 1319 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 1320 } 1321 1322 @Test testProcessStackEvent_AudioStateChanged_onAudioOnState()1323 public void testProcessStackEvent_AudioStateChanged_onAudioOnState() { 1324 initToAudioOnState(); 1325 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1326 .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); 1327 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1328 event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED; 1329 event.device = mTestDevice; 1330 sendMessage(StackEvent.STACK_EVENT, event); 1331 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1332 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 1333 } 1334 1335 @Test testProcessStackEvent_CodecSelection_onConnectedState()1336 public void testProcessStackEvent_CodecSelection_onConnectedState() { 1337 initToConnectedState(); 1338 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1339 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 1340 1341 // Trigger a MSBC codec stack event. Expect to mAudioWbs = true. 1342 mHeadsetClientStateMachine.mAudioWbs = false; 1343 mHeadsetClientStateMachine.mAudioSWB = false; 1344 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1345 event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC; 1346 event.device = mTestDevice; 1347 sendMessage(StackEvent.STACK_EVENT, event); 1348 assertThat(mHeadsetClientStateMachine.mAudioWbs).isTrue(); 1349 assertThat(mHeadsetClientStateMachine.mAudioSWB).isFalse(); 1350 1351 // Trigger a LC3 codec stack event. Expect to mAudioSWB = true. 1352 mHeadsetClientStateMachine.mAudioWbs = false; 1353 mHeadsetClientStateMachine.mAudioSWB = false; 1354 event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1355 event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_LC3; 1356 event.device = mTestDevice; 1357 sendMessage(StackEvent.STACK_EVENT, event); 1358 assertThat(mHeadsetClientStateMachine.mAudioWbs).isFalse(); 1359 assertThat(mHeadsetClientStateMachine.mAudioSWB).isTrue(); 1360 } 1361 1362 @Test 1363 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_TransitionToDisconnected()1364 public void testDisconnectingState_TransitionToDisconnected() { 1365 initToDisconnectingState(); 1366 1367 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1368 event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED; 1369 event.device = mTestDevice; 1370 1371 sendMessage(StackEvent.STACK_EVENT, event); 1372 mTestLooper.dispatchAll(); 1373 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_DISCONNECTED)); 1374 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1375 .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); 1376 1377 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 1378 } 1379 1380 @Test 1381 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_ReceiveConnectMsg_DeferMessage()1382 public void testDisconnectingState_ReceiveConnectMsg_DeferMessage() { 1383 // case CONNECT: 1384 initToDisconnectingState(); 1385 assertThat( 1386 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1387 HeadsetClientStateMachine.CONNECT)) 1388 .isFalse(); 1389 sendMessage(HeadsetClientStateMachine.CONNECT); 1390 assertThat( 1391 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1392 HeadsetClientStateMachine.CONNECT)) 1393 .isTrue(); 1394 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1395 .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class); 1396 } 1397 1398 @Test 1399 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_ReceiveConnectAudioMsg_DeferMessage()1400 public void testDisconnectingState_ReceiveConnectAudioMsg_DeferMessage() { 1401 // case CONNECT_AUDIO: 1402 initToDisconnectingState(); 1403 assertThat( 1404 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1405 HeadsetClientStateMachine.CONNECT_AUDIO)) 1406 .isFalse(); 1407 sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); 1408 assertThat( 1409 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1410 HeadsetClientStateMachine.CONNECT_AUDIO)) 1411 .isTrue(); 1412 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1413 .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class); 1414 } 1415 1416 @Test 1417 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_ReceiveDisconnectMsg_DeferMessage()1418 public void testDisconnectingState_ReceiveDisconnectMsg_DeferMessage() { 1419 // case DISCONNECT: 1420 initToDisconnectingState(); 1421 sendMessage(HeadsetClientStateMachine.DISCONNECT); 1422 assertThat( 1423 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1424 HeadsetClientStateMachine.DISCONNECT)) 1425 .isTrue(); 1426 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1427 .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class); 1428 } 1429 1430 @Test 1431 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_ReceiveDisconnectAudioMsg_DeferMessage()1432 public void testDisconnectingState_ReceiveDisconnectAudioMsg_DeferMessage() { 1433 // case DISCONNECT_AUDIO: 1434 initToDisconnectingState(); 1435 assertThat( 1436 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1437 HeadsetClientStateMachine.DISCONNECT_AUDIO)) 1438 .isFalse(); 1439 sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 1440 assertThat( 1441 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1442 HeadsetClientStateMachine.DISCONNECT_AUDIO)) 1443 .isTrue(); 1444 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1445 .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class); 1446 } 1447 1448 @Test 1449 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_ReceiveUnknownMsg_NotHandled()1450 public void testDisconnectingState_ReceiveUnknownMsg_NotHandled() { 1451 initToDisconnectingState(); 1452 sendMessage(HeadsetClientStateMachine.NO_ACTION); 1453 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1454 .isInstanceOf(HeadsetClientStateMachine.Disconnecting.class); 1455 } 1456 1457 @Test 1458 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testAudioOnState_ReceiveDisconnectMsg_DeferMessage()1459 public void testAudioOnState_ReceiveDisconnectMsg_DeferMessage() { 1460 initToAudioOnState(); 1461 assertThat( 1462 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1463 HeadsetClientStateMachine.DISCONNECT)) 1464 .isFalse(); 1465 sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice); 1466 assertThat( 1467 mHeadsetClientStateMachine.doesSuperHaveDeferredMessages( 1468 HeadsetClientStateMachine.DISCONNECT)) 1469 .isTrue(); 1470 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1471 .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); 1472 } 1473 1474 @Test 1475 @EnableFlags(Flags.FLAG_HFP_CLIENT_DISCONNECTING_STATE) testDisconnectingState_DisconnectingTimeout_TransitionToDisconnected()1476 public void testDisconnectingState_DisconnectingTimeout_TransitionToDisconnected() { 1477 initToDisconnectingState(); 1478 // Trigger timeout 1479 mTestLooper.moveTimeForward(HeadsetClientStateMachine.DISCONNECTING_TIMEOUT_MS); 1480 mTestLooper.dispatchAll(); 1481 verifySendBroadcastMultiplePermissions(hasExtra(EXTRA_STATE, STATE_DISCONNECTED)); 1482 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1483 .isInstanceOf(HeadsetClientStateMachine.Disconnected.class); 1484 1485 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(false)); 1486 } 1487 1488 /** 1489 * Allow/disallow connection to any device 1490 * 1491 * @param allow if true, connection is allowed 1492 */ allowConnection(boolean allow)1493 private void allowConnection(boolean allow) { 1494 mHeadsetClientStateMachine.allowConnect = allow; 1495 } 1496 initToConnectingState()1497 private void initToConnectingState() { 1498 doReturn(true).when(mNativeInterface).connect(any(BluetoothDevice.class)); 1499 sendMessageAndVerifyTransition( 1500 mHeadsetClientStateMachine.obtainMessage( 1501 HeadsetClientStateMachine.CONNECT, mTestDevice), 1502 HeadsetClientStateMachine.Connecting.class); 1503 } 1504 initToConnectedState()1505 private void initToConnectedState() { 1506 String atCommand = "+ANDROID: (SINKAUDIOPOLICY)"; 1507 initToConnectingState(); 1508 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); 1509 event.valueString = atCommand; 1510 event.device = mTestDevice; 1511 sendMessage(StackEvent.STACK_EVENT, event); 1512 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1513 .isInstanceOf(HeadsetClientStateMachine.Connected.class); 1514 verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true)); 1515 } 1516 initToAudioOnState()1517 private void initToAudioOnState() { 1518 mHeadsetClientStateMachine.setAudioRouteAllowed(true); 1519 initToConnectedState(); 1520 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1521 event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED; 1522 event.device = mTestDevice; 1523 sendMessage(StackEvent.STACK_EVENT, event); 1524 assertThat(mHeadsetClientStateMachine.getCurrentState()) 1525 .isInstanceOf(HeadsetClientStateMachine.AudioOn.class); 1526 } 1527 initToDisconnectingState()1528 private void initToDisconnectingState() { 1529 initToConnectedState(); 1530 sendMessageAndVerifyTransition( 1531 mHeadsetClientStateMachine.obtainMessage( 1532 HeadsetClientStateMachine.DISCONNECT, mTestDevice), 1533 HeadsetClientStateMachine.Disconnecting.class); 1534 } 1535 verifySendBroadcastMultiplePermissions(Matcher<Intent>.... matchers)1536 private void verifySendBroadcastMultiplePermissions(Matcher<Intent>... matchers) { 1537 mInOrder.verify(mHeadsetClientService) 1538 .sendBroadcastMultiplePermissions( 1539 MockitoHamcrest.argThat(AllOf.allOf(matchers)), 1540 any(String[].class), 1541 any(BroadcastOptions.class)); 1542 } 1543 verifySendBroadcast(Matcher<Intent>.... matchers)1544 private void verifySendBroadcast(Matcher<Intent>... matchers) { 1545 mInOrder.verify(mHeadsetClientService) 1546 .sendBroadcast( 1547 MockitoHamcrest.argThat(AllOf.allOf(matchers)), 1548 anyString(), 1549 any(Bundle.class)); 1550 } 1551 sendMessage(Message msg)1552 private void sendMessage(Message msg) { 1553 mHeadsetClientStateMachine.sendMessage(msg); 1554 mTestLooper.dispatchAll(); 1555 } 1556 sendMessage(int what)1557 private void sendMessage(int what) { 1558 sendMessage(mHeadsetClientStateMachine.obtainMessage(what)); 1559 } 1560 sendMessage(int what, Object obj)1561 private void sendMessage(int what, Object obj) { 1562 sendMessage(mHeadsetClientStateMachine.obtainMessage(what, obj)); 1563 } 1564 sendMessage(int what, int arg1, int arg2)1565 private void sendMessage(int what, int arg1, int arg2) { 1566 sendMessage(mHeadsetClientStateMachine.obtainMessage(what, arg1, arg2)); 1567 } 1568 sendMessage(int what, int arg1, int arg2, Object obj)1569 private void sendMessage(int what, int arg1, int arg2, Object obj) { 1570 sendMessage(mHeadsetClientStateMachine.obtainMessage(what, arg1, arg2, obj)); 1571 } 1572 sendMessageAndVerifyTransition(Message msg, Class<T> type)1573 private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) { 1574 int previousState = mHeadsetClientStateMachine.getConnectionState(mTestDevice); 1575 1576 sendMessage(msg); 1577 1578 int newState = mHeadsetClientStateMachine.getConnectionState(mTestDevice); 1579 verifySendBroadcastMultiplePermissions( 1580 hasExtra(EXTRA_PREVIOUS_STATE, previousState), hasExtra(EXTRA_STATE, newState)); 1581 1582 assertThat(mHeadsetClientStateMachine.getCurrentState()).isInstanceOf(type); 1583 } 1584 1585 public static class TestHeadsetClientStateMachine extends HeadsetClientStateMachine { 1586 1587 Boolean allowConnect = null; 1588 boolean mForceSetAudioPolicyProperty = false; 1589 TestHeadsetClientStateMachine( AdapterService adapterService, HeadsetClientService context, HeadsetService headsetService, Looper looper, NativeInterface nativeInterface)1590 TestHeadsetClientStateMachine( 1591 AdapterService adapterService, 1592 HeadsetClientService context, 1593 HeadsetService headsetService, 1594 Looper looper, 1595 NativeInterface nativeInterface) { 1596 super(adapterService, context, headsetService, looper, nativeInterface); 1597 } 1598 doesSuperHaveDeferredMessages(int what)1599 public boolean doesSuperHaveDeferredMessages(int what) { 1600 return super.hasDeferredMessages(what); 1601 } 1602 1603 @Override okToConnect(BluetoothDevice device)1604 public boolean okToConnect(BluetoothDevice device) { 1605 return allowConnect != null ? allowConnect : super.okToConnect(device); 1606 } 1607 1608 @Override getConnectingTimePolicyProperty()1609 public int getConnectingTimePolicyProperty() { 1610 return 2; 1611 } 1612 1613 @Override getInBandRingtonePolicyProperty()1614 public int getInBandRingtonePolicyProperty() { 1615 return 1; 1616 } 1617 setForceSetAudioPolicyProperty(boolean flag)1618 void setForceSetAudioPolicyProperty(boolean flag) { 1619 mForceSetAudioPolicyProperty = flag; 1620 } 1621 1622 @Override getForceSetAudioPolicyProperty()1623 boolean getForceSetAudioPolicyProperty() { 1624 return mForceSetAudioPolicyProperty; 1625 } 1626 } 1627 } 1628