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 /** 18 * Bluetooth Headset Client StateMachine 19 * (Disconnected) 20 * | ^ ^ 21 * CONNECT | | | DISCONNECTED 22 * V | | 23 * (Connecting) | 24 * | | 25 * CONNECTED | | DISCONNECT 26 * V | 27 * (Connected) 28 * | ^ 29 * CONNECT_AUDIO | | DISCONNECT_AUDIO 30 * V | 31 * (AudioOn) 32 */ 33 34 package com.android.bluetooth.hfpclient; 35 36 import android.bluetooth.BluetoothAdapter; 37 import android.bluetooth.BluetoothDevice; 38 import android.bluetooth.BluetoothHeadsetClient; 39 import android.bluetooth.BluetoothHeadsetClientCall; 40 import android.bluetooth.BluetoothProfile; 41 import android.bluetooth.BluetoothUuid; 42 import android.bluetooth.hfp.BluetoothHfpProtoEnums; 43 import android.content.Intent; 44 import android.media.AudioAttributes; 45 import android.media.AudioFocusRequest; 46 import android.media.AudioManager; 47 import android.os.Bundle; 48 import android.os.Looper; 49 import android.os.Message; 50 import android.os.ParcelUuid; 51 import android.os.SystemClock; 52 import android.util.Log; 53 import android.util.Pair; 54 import android.util.StatsLog; 55 56 import com.android.bluetooth.BluetoothMetricsProto; 57 import com.android.bluetooth.R; 58 import com.android.bluetooth.Utils; 59 import com.android.bluetooth.btservice.AdapterService; 60 import com.android.bluetooth.btservice.MetricsLogger; 61 import com.android.bluetooth.btservice.ProfileService; 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.internal.util.IState; 64 import com.android.internal.util.State; 65 import com.android.internal.util.StateMachine; 66 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.HashSet; 70 import java.util.Hashtable; 71 import java.util.LinkedList; 72 import java.util.List; 73 import java.util.Queue; 74 import java.util.Set; 75 76 public class HeadsetClientStateMachine extends StateMachine { 77 private static final String TAG = "HeadsetClientStateMachine"; 78 private static final boolean DBG = false; 79 80 static final int NO_ACTION = 0; 81 static final int IN_BAND_RING_ENABLED = 1; 82 83 // external actions 84 public static final int AT_OK = 0; 85 public static final int CONNECT = 1; 86 public static final int DISCONNECT = 2; 87 public static final int CONNECT_AUDIO = 3; 88 public static final int DISCONNECT_AUDIO = 4; 89 public static final int VOICE_RECOGNITION_START = 5; 90 public static final int VOICE_RECOGNITION_STOP = 6; 91 public static final int SET_MIC_VOLUME = 7; 92 public static final int SET_SPEAKER_VOLUME = 8; 93 public static final int DIAL_NUMBER = 10; 94 public static final int ACCEPT_CALL = 12; 95 public static final int REJECT_CALL = 13; 96 public static final int HOLD_CALL = 14; 97 public static final int TERMINATE_CALL = 15; 98 public static final int ENTER_PRIVATE_MODE = 16; 99 public static final int SEND_DTMF = 17; 100 public static final int EXPLICIT_CALL_TRANSFER = 18; 101 public static final int DISABLE_NREC = 20; 102 103 // internal actions 104 private static final int QUERY_CURRENT_CALLS = 50; 105 private static final int QUERY_OPERATOR_NAME = 51; 106 private static final int SUBSCRIBER_INFO = 52; 107 private static final int CONNECTING_TIMEOUT = 53; 108 109 // special action to handle terminating specific call from multiparty call 110 static final int TERMINATE_SPECIFIC_CALL = 53; 111 112 // Timeouts. 113 @VisibleForTesting 114 static final int CONNECTING_TIMEOUT_MS = 10000; // 10s 115 private static final int ROUTING_DELAY_MS = 250; 116 117 private static final int MAX_HFP_SCO_VOICE_CALL_VOLUME = 15; // HFP 1.5 spec. 118 private static final int MIN_HFP_SCO_VOICE_CALL_VOLUME = 1; // HFP 1.5 spec. 119 120 static final int HF_ORIGINATED_CALL_ID = -1; 121 private static final long OUTGOING_TIMEOUT_MILLI = 10 * 1000; // 10 seconds 122 private static final long QUERY_CURRENT_CALLS_WAIT_MILLIS = 2 * 1000; // 2 seconds 123 124 // Keep track of audio routing across all devices. 125 private static boolean sAudioIsRouted = false; 126 127 private final Disconnected mDisconnected; 128 private final Connecting mConnecting; 129 private final Connected mConnected; 130 private final AudioOn mAudioOn; 131 private State mPrevState; 132 private long mClccTimer = 0; 133 134 private final HeadsetClientService mService; 135 136 // Set of calls that represent the accurate state of calls that exists on AG and the calls that 137 // are currently in process of being notified to the AG from HF. 138 private final Hashtable<Integer, BluetoothHeadsetClientCall> mCalls = new Hashtable<>(); 139 // Set of calls received from AG via the AT+CLCC command. We use this map to update the mCalls 140 // which is eventually used to inform the telephony stack of any changes to call on HF. 141 private final Hashtable<Integer, BluetoothHeadsetClientCall> mCallsUpdate = new Hashtable<>(); 142 143 private int mIndicatorNetworkState; 144 private int mIndicatorNetworkType; 145 private int mIndicatorNetworkSignal; 146 private int mIndicatorBatteryLevel; 147 private boolean mInBandRing; 148 149 private String mOperatorName; 150 private String mSubscriberInfo; 151 152 private static int sMaxAmVcVol; 153 private static int sMinAmVcVol; 154 155 // queue of send actions (pair action, action_data) 156 private Queue<Pair<Integer, Object>> mQueuedActions; 157 158 // last executed command, before action is complete e.g. waiting for some 159 // indicator 160 private Pair<Integer, Object> mPendingAction; 161 162 private int mAudioState; 163 private boolean mAudioWbs; 164 private int mVoiceRecognitionActive; 165 private final BluetoothAdapter mAdapter; 166 167 // currently connected device 168 private BluetoothDevice mCurrentDevice = null; 169 170 // general peer features and call handling features 171 private int mPeerFeatures; 172 private int mChldFeatures; 173 174 // This is returned when requesting focus from AudioManager 175 private AudioFocusRequest mAudioFocusRequest; 176 177 private AudioManager mAudioManager; 178 179 // Accessor for the states, useful for reusing the state machines getDisconnectedState()180 public IState getDisconnectedState() { 181 return mDisconnected; 182 } 183 184 // Get if in band ring is currently enabled on device. getInBandRing()185 public boolean getInBandRing() { 186 return mInBandRing; 187 } 188 dump(StringBuilder sb)189 public void dump(StringBuilder sb) { 190 if (mCurrentDevice == null) return; 191 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice.getAddress() + "(" 192 + mCurrentDevice.getName() + ") " + this.toString()); 193 ProfileService.println(sb, "mAudioState: " + mAudioState); 194 ProfileService.println(sb, "mAudioWbs: " + mAudioWbs); 195 ProfileService.println(sb, "mIndicatorNetworkState: " + mIndicatorNetworkState); 196 ProfileService.println(sb, "mIndicatorNetworkType: " + mIndicatorNetworkType); 197 ProfileService.println(sb, "mIndicatorNetworkSignal: " + mIndicatorNetworkSignal); 198 ProfileService.println(sb, "mIndicatorBatteryLevel: " + mIndicatorBatteryLevel); 199 ProfileService.println(sb, "mOperatorName: " + mOperatorName); 200 ProfileService.println(sb, "mSubscriberInfo: " + mSubscriberInfo); 201 202 ProfileService.println(sb, "mCalls:"); 203 if (mCalls != null) { 204 for (BluetoothHeadsetClientCall call : mCalls.values()) { 205 ProfileService.println(sb, " " + call); 206 } 207 } 208 209 ProfileService.println(sb, "mCallsUpdate:"); 210 if (mCallsUpdate != null) { 211 for (BluetoothHeadsetClientCall call : mCallsUpdate.values()) { 212 ProfileService.println(sb, " " + call); 213 } 214 } 215 } 216 clearPendingAction()217 private void clearPendingAction() { 218 mPendingAction = new Pair<Integer, Object>(NO_ACTION, 0); 219 } 220 addQueuedAction(int action)221 private void addQueuedAction(int action) { 222 addQueuedAction(action, 0); 223 } 224 addQueuedAction(int action, Object data)225 private void addQueuedAction(int action, Object data) { 226 mQueuedActions.add(new Pair<Integer, Object>(action, data)); 227 } 228 addQueuedAction(int action, int data)229 private void addQueuedAction(int action, int data) { 230 mQueuedActions.add(new Pair<Integer, Object>(action, data)); 231 } 232 getCall(int... states)233 private BluetoothHeadsetClientCall getCall(int... states) { 234 if (DBG) { 235 Log.d(TAG, "getFromCallsWithStates states:" + Arrays.toString(states)); 236 } 237 for (BluetoothHeadsetClientCall c : mCalls.values()) { 238 for (int s : states) { 239 if (c.getState() == s) { 240 return c; 241 } 242 } 243 } 244 return null; 245 } 246 callsInState(int state)247 private int callsInState(int state) { 248 int i = 0; 249 for (BluetoothHeadsetClientCall c : mCalls.values()) { 250 if (c.getState() == state) { 251 i++; 252 } 253 } 254 255 return i; 256 } 257 sendCallChangedIntent(BluetoothHeadsetClientCall c)258 private void sendCallChangedIntent(BluetoothHeadsetClientCall c) { 259 if (DBG) { 260 Log.d(TAG, "sendCallChangedIntent " + c); 261 } 262 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED); 263 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 264 intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c); 265 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 266 } 267 queryCallsStart()268 private boolean queryCallsStart() { 269 if (DBG) { 270 Log.d(TAG, "queryCallsStart"); 271 } 272 clearPendingAction(); 273 NativeInterface.queryCurrentCallsNative(getByteAddress(mCurrentDevice)); 274 addQueuedAction(QUERY_CURRENT_CALLS, 0); 275 return true; 276 } 277 queryCallsDone()278 private void queryCallsDone() { 279 if (DBG) { 280 Log.d(TAG, "queryCallsDone"); 281 } 282 // mCalls has two types of calls: 283 // (a) Calls that are received from AG of a previous iteration of queryCallsStart() 284 // (b) Calls that are outgoing initiated from HF 285 // mCallsUpdate has all calls received from queryCallsUpdate() in current iteration of 286 // queryCallsStart(). 287 // 288 // We use the following steps to make sure that calls are update correctly. 289 // 290 // If there are no calls initiated from HF (i.e. ID = -1) then: 291 // 1. All IDs which are common in mCalls & mCallsUpdate are updated and the upper layers are 292 // informed of the change calls (if any changes). 293 // 2. All IDs that are in mCalls but *not* in mCallsUpdate will be removed from mCalls and 294 // the calls should be terminated 295 // 3. All IDs that are new in mCallsUpdated should be added as new calls to mCalls. 296 // 297 // If there is an outgoing HF call, it is important to associate that call with one of the 298 // mCallsUpdated calls hence, 299 // 1. If from the above procedure we get N extra calls (i.e. {3}): 300 // choose the first call as the one to associate with the HF call. 301 302 // Create set of IDs for added calls, removed calls and consitent calls. 303 // WARN!!! Java Map -> Set has association hence changes to Set are reflected in the Map 304 // itself (i.e. removing an element from Set removes it from the Map hence use copy). 305 Set<Integer> currCallIdSet = new HashSet<Integer>(); 306 currCallIdSet.addAll(mCalls.keySet()); 307 // Remove the entry for unassigned call. 308 currCallIdSet.remove(HF_ORIGINATED_CALL_ID); 309 310 Set<Integer> newCallIdSet = new HashSet<Integer>(); 311 newCallIdSet.addAll(mCallsUpdate.keySet()); 312 313 // Added. 314 Set<Integer> callAddedIds = new HashSet<Integer>(); 315 callAddedIds.addAll(newCallIdSet); 316 callAddedIds.removeAll(currCallIdSet); 317 318 // Removed. 319 Set<Integer> callRemovedIds = new HashSet<Integer>(); 320 callRemovedIds.addAll(currCallIdSet); 321 callRemovedIds.removeAll(newCallIdSet); 322 323 // Retained. 324 Set<Integer> callRetainedIds = new HashSet<Integer>(); 325 callRetainedIds.addAll(currCallIdSet); 326 callRetainedIds.retainAll(newCallIdSet); 327 328 if (DBG) { 329 Log.d(TAG, "currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet 330 + " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds 331 + " callRetainedIds " + callRetainedIds); 332 } 333 334 // First thing is to try to associate the outgoing HF with a valid call. 335 Integer hfOriginatedAssoc = -1; 336 if (mCalls.containsKey(HF_ORIGINATED_CALL_ID)) { 337 BluetoothHeadsetClientCall c = mCalls.get(HF_ORIGINATED_CALL_ID); 338 long cCreationElapsed = c.getCreationElapsedMilli(); 339 if (callAddedIds.size() > 0) { 340 if (DBG) { 341 Log.d(TAG, "Associating the first call with HF originated call"); 342 } 343 hfOriginatedAssoc = (Integer) callAddedIds.toArray()[0]; 344 mCalls.put(hfOriginatedAssoc, mCalls.get(HF_ORIGINATED_CALL_ID)); 345 mCalls.remove(HF_ORIGINATED_CALL_ID); 346 347 // Adjust this call in above sets. 348 callAddedIds.remove(hfOriginatedAssoc); 349 callRetainedIds.add(hfOriginatedAssoc); 350 } else if (SystemClock.elapsedRealtime() - cCreationElapsed > OUTGOING_TIMEOUT_MILLI) { 351 Log.w(TAG, "Outgoing call did not see a response, clear the calls and send CHUP"); 352 // We send a terminate because we are in a bad state and trying to 353 // recover. 354 terminateCall(); 355 356 // Clean out the state for outgoing call. 357 for (Integer idx : mCalls.keySet()) { 358 BluetoothHeadsetClientCall c1 = mCalls.get(idx); 359 c1.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 360 sendCallChangedIntent(c1); 361 } 362 mCalls.clear(); 363 364 // We return here, if there's any update to the phone we should get a 365 // follow up by getting some call indicators and hence update the calls. 366 return; 367 } 368 } 369 370 if (DBG) { 371 Log.d(TAG, "ADJUST: currCallIdSet " + mCalls.keySet() + " newCallIdSet " + newCallIdSet 372 + " callAddedIds " + callAddedIds + " callRemovedIds " + callRemovedIds 373 + " callRetainedIds " + callRetainedIds); 374 } 375 376 // Terminate & remove the calls that are done. 377 for (Integer idx : callRemovedIds) { 378 BluetoothHeadsetClientCall c = mCalls.remove(idx); 379 c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 380 sendCallChangedIntent(c); 381 } 382 383 // Add the new calls. 384 for (Integer idx : callAddedIds) { 385 BluetoothHeadsetClientCall c = mCallsUpdate.get(idx); 386 mCalls.put(idx, c); 387 sendCallChangedIntent(c); 388 } 389 390 // Update the existing calls. 391 for (Integer idx : callRetainedIds) { 392 BluetoothHeadsetClientCall cOrig = mCalls.get(idx); 393 BluetoothHeadsetClientCall cUpdate = mCallsUpdate.get(idx); 394 395 // Update the necessary fields. 396 cOrig.setNumber(cUpdate.getNumber()); 397 cOrig.setState(cUpdate.getState()); 398 cOrig.setMultiParty(cUpdate.isMultiParty()); 399 400 // Send update with original object (UUID, idx). 401 sendCallChangedIntent(cOrig); 402 } 403 404 if (mCalls.size() > 0) { 405 if (mService.getResources().getBoolean(R.bool.hfp_clcc_poll_during_call)) { 406 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 407 } else { 408 if (getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING) != null) { 409 Log.d(TAG, "Still have incoming call; polling"); 410 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 411 } else { 412 removeMessages(QUERY_CURRENT_CALLS); 413 } 414 } 415 } 416 417 mCallsUpdate.clear(); 418 } 419 queryCallsUpdate(int id, int state, String number, boolean multiParty, boolean outgoing)420 private void queryCallsUpdate(int id, int state, String number, boolean multiParty, 421 boolean outgoing) { 422 if (DBG) { 423 Log.d(TAG, "queryCallsUpdate: " + id); 424 } 425 mCallsUpdate.put(id, 426 new BluetoothHeadsetClientCall(mCurrentDevice, id, state, number, multiParty, 427 outgoing, mInBandRing)); 428 } 429 acceptCall(int flag)430 private void acceptCall(int flag) { 431 int action = -1; 432 433 if (DBG) { 434 Log.d(TAG, "acceptCall: (" + flag + ")"); 435 } 436 437 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING, 438 BluetoothHeadsetClientCall.CALL_STATE_WAITING); 439 if (c == null) { 440 c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD, 441 BluetoothHeadsetClientCall.CALL_STATE_HELD); 442 443 if (c == null) { 444 return; 445 } 446 } 447 448 if (DBG) { 449 Log.d(TAG, "Call to accept: " + c); 450 } 451 switch (c.getState()) { 452 case BluetoothHeadsetClientCall.CALL_STATE_INCOMING: 453 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 454 return; 455 } 456 action = HeadsetClientHalConstants.CALL_ACTION_ATA; 457 break; 458 case BluetoothHeadsetClientCall.CALL_STATE_WAITING: 459 if (callsInState(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) == 0) { 460 // if no active calls present only plain accept is allowed 461 if (flag != BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 462 return; 463 } 464 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 465 break; 466 } 467 468 // if active calls are present then we have the option to either terminate the 469 // existing call or hold the existing call. We hold the other call by default. 470 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD 471 || flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 472 if (DBG) { 473 Log.d(TAG, "Accepting call with accept and hold"); 474 } 475 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 476 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) { 477 if (DBG) { 478 Log.d(TAG, "Accepting call with accept and reject"); 479 } 480 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; 481 } else { 482 Log.e(TAG, "Aceept call with invalid flag: " + flag); 483 return; 484 } 485 break; 486 case BluetoothHeadsetClientCall.CALL_STATE_HELD: 487 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) { 488 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 489 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_TERMINATE) { 490 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_1; 491 } else if (getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE) != null) { 492 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_3; 493 } else if (flag == BluetoothHeadsetClient.CALL_ACCEPT_NONE) { 494 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 495 } else { 496 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 497 } 498 break; 499 case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: 500 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_1; 501 break; 502 case BluetoothHeadsetClientCall.CALL_STATE_ALERTING: 503 case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE: 504 case BluetoothHeadsetClientCall.CALL_STATE_DIALING: 505 default: 506 return; 507 } 508 509 if (flag == BluetoothHeadsetClient.CALL_ACCEPT_HOLD) { 510 // When unholding a call over Bluetooth make sure to route audio. 511 routeHfpAudio(true); 512 } 513 514 if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) { 515 addQueuedAction(ACCEPT_CALL, action); 516 } else { 517 Log.e(TAG, "ERROR: Couldn't accept a call, action:" + action); 518 } 519 } 520 rejectCall()521 private void rejectCall() { 522 int action; 523 524 if (DBG) { 525 Log.d(TAG, "rejectCall"); 526 } 527 528 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING, 529 BluetoothHeadsetClientCall.CALL_STATE_WAITING, 530 BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD, 531 BluetoothHeadsetClientCall.CALL_STATE_HELD); 532 if (c == null) { 533 if (DBG) { 534 Log.d(TAG, "No call to reject, returning."); 535 } 536 return; 537 } 538 539 switch (c.getState()) { 540 case BluetoothHeadsetClientCall.CALL_STATE_INCOMING: 541 action = HeadsetClientHalConstants.CALL_ACTION_CHUP; 542 break; 543 case BluetoothHeadsetClientCall.CALL_STATE_WAITING: 544 case BluetoothHeadsetClientCall.CALL_STATE_HELD: 545 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0; 546 break; 547 case BluetoothHeadsetClientCall.CALL_STATE_HELD_BY_RESPONSE_AND_HOLD: 548 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_2; 549 break; 550 case BluetoothHeadsetClientCall.CALL_STATE_ACTIVE: 551 case BluetoothHeadsetClientCall.CALL_STATE_DIALING: 552 case BluetoothHeadsetClientCall.CALL_STATE_ALERTING: 553 default: 554 return; 555 } 556 557 if (DBG) { 558 Log.d(TAG, "Reject call action " + action); 559 } 560 if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) { 561 addQueuedAction(REJECT_CALL, action); 562 } else { 563 Log.e(TAG, "ERROR: Couldn't reject a call, action:" + action); 564 } 565 } 566 holdCall()567 private void holdCall() { 568 int action; 569 570 if (DBG) { 571 Log.d(TAG, "holdCall"); 572 } 573 574 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_INCOMING); 575 if (c != null) { 576 action = HeadsetClientHalConstants.CALL_ACTION_BTRH_0; 577 } else { 578 c = getCall(BluetoothHeadsetClientCall.CALL_STATE_ACTIVE); 579 if (c == null) { 580 return; 581 } 582 583 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_2; 584 } 585 586 if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) { 587 addQueuedAction(HOLD_CALL, action); 588 } else { 589 Log.e(TAG, "ERROR: Couldn't hold a call, action:" + action); 590 } 591 } 592 terminateCall()593 private void terminateCall() { 594 if (DBG) { 595 Log.d(TAG, "terminateCall"); 596 } 597 598 int action = HeadsetClientHalConstants.CALL_ACTION_CHUP; 599 600 BluetoothHeadsetClientCall c = getCall(BluetoothHeadsetClientCall.CALL_STATE_DIALING, 601 BluetoothHeadsetClientCall.CALL_STATE_ALERTING, 602 BluetoothHeadsetClientCall.CALL_STATE_ACTIVE); 603 if (c == null) { 604 // If the call being terminated is currently held, switch the action to CHLD_0 605 c = getCall(BluetoothHeadsetClientCall.CALL_STATE_HELD); 606 action = HeadsetClientHalConstants.CALL_ACTION_CHLD_0; 607 } 608 if (c != null) { 609 if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), action, 0)) { 610 addQueuedAction(TERMINATE_CALL, action); 611 } else { 612 Log.e(TAG, "ERROR: Couldn't terminate outgoing call"); 613 } 614 } 615 } 616 enterPrivateMode(int idx)617 private void enterPrivateMode(int idx) { 618 if (DBG) { 619 Log.d(TAG, "enterPrivateMode: " + idx); 620 } 621 622 BluetoothHeadsetClientCall c = mCalls.get(idx); 623 624 if (c == null || c.getState() != BluetoothHeadsetClientCall.CALL_STATE_ACTIVE 625 || !c.isMultiParty()) { 626 return; 627 } 628 629 if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), 630 HeadsetClientHalConstants.CALL_ACTION_CHLD_2X, idx)) { 631 addQueuedAction(ENTER_PRIVATE_MODE, c); 632 } else { 633 Log.e(TAG, "ERROR: Couldn't enter private " + " id:" + idx); 634 } 635 } 636 explicitCallTransfer()637 private void explicitCallTransfer() { 638 if (DBG) { 639 Log.d(TAG, "explicitCallTransfer"); 640 } 641 642 // can't transfer call if there is not enough call parties 643 if (mCalls.size() < 2) { 644 return; 645 } 646 647 if (NativeInterface.handleCallActionNative(getByteAddress(mCurrentDevice), 648 HeadsetClientHalConstants.CALL_ACTION_CHLD_4, -1)) { 649 addQueuedAction(EXPLICIT_CALL_TRANSFER); 650 } else { 651 Log.e(TAG, "ERROR: Couldn't transfer call"); 652 } 653 } 654 getCurrentAgFeatures()655 public Bundle getCurrentAgFeatures() { 656 Bundle b = new Bundle(); 657 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) 658 == HeadsetClientHalConstants.PEER_FEAT_3WAY) { 659 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true); 660 } 661 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) 662 == HeadsetClientHalConstants.PEER_FEAT_VREC) { 663 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true); 664 } 665 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) 666 == HeadsetClientHalConstants.PEER_FEAT_REJECT) { 667 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true); 668 } 669 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) 670 == HeadsetClientHalConstants.PEER_FEAT_ECC) { 671 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true); 672 } 673 674 // add individual CHLD support extras 675 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) 676 == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) { 677 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, true); 678 } 679 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) 680 == HeadsetClientHalConstants.CHLD_FEAT_REL) { 681 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, 682 true); 683 } 684 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) 685 == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) { 686 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true); 687 } 688 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) 689 == HeadsetClientHalConstants.CHLD_FEAT_MERGE) { 690 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true); 691 } 692 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) 693 == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) { 694 b.putBoolean(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true); 695 } 696 697 return b; 698 } 699 HeadsetClientStateMachine(HeadsetClientService context, Looper looper)700 HeadsetClientStateMachine(HeadsetClientService context, Looper looper) { 701 super(TAG, looper); 702 mService = context; 703 mAudioManager = mService.getAudioManager(); 704 705 mAdapter = BluetoothAdapter.getDefaultAdapter(); 706 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 707 mAudioWbs = false; 708 mVoiceRecognitionActive = HeadsetClientHalConstants.VR_STATE_STOPPED; 709 710 mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE; 711 mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME; 712 mIndicatorNetworkSignal = 0; 713 mIndicatorBatteryLevel = 0; 714 715 sMaxAmVcVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); 716 sMinAmVcVol = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL); 717 718 mOperatorName = null; 719 mSubscriberInfo = null; 720 721 mQueuedActions = new LinkedList<Pair<Integer, Object>>(); 722 clearPendingAction(); 723 724 mCalls.clear(); 725 mCallsUpdate.clear(); 726 727 mDisconnected = new Disconnected(); 728 mConnecting = new Connecting(); 729 mConnected = new Connected(); 730 mAudioOn = new AudioOn(); 731 732 addState(mDisconnected); 733 addState(mConnecting); 734 addState(mConnected); 735 addState(mAudioOn, mConnected); 736 737 setInitialState(mDisconnected); 738 } 739 make(HeadsetClientService context, Looper l)740 static HeadsetClientStateMachine make(HeadsetClientService context, Looper l) { 741 if (DBG) { 742 Log.d(TAG, "make"); 743 } 744 HeadsetClientStateMachine hfcsm = new HeadsetClientStateMachine(context, l); 745 hfcsm.start(); 746 return hfcsm; 747 } 748 routeHfpAudio(boolean enable)749 synchronized void routeHfpAudio(boolean enable) { 750 if (mAudioManager == null) { 751 Log.e(TAG, "AudioManager is null!"); 752 return; 753 } 754 if (DBG) { 755 Log.d(TAG, "hfp_enable=" + enable); 756 } 757 if (enable && !sAudioIsRouted) { 758 mAudioManager.setParameters("hfp_enable=true"); 759 } else if (!enable) { 760 mAudioManager.setParameters("hfp_enable=false"); 761 } 762 sAudioIsRouted = enable; 763 } 764 requestAudioFocus()765 private AudioFocusRequest requestAudioFocus() { 766 AudioAttributes streamAttributes = 767 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) 768 .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) 769 .build(); 770 AudioFocusRequest focusRequest = 771 new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) 772 .setAudioAttributes(streamAttributes) 773 .build(); 774 int focusRequestStatus = mAudioManager.requestAudioFocus(focusRequest); 775 if (DBG) { 776 String s = (focusRequestStatus == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) 777 ? "AudioFocus granted" : "AudioFocus NOT granted"; 778 Log.d(TAG, "AudioManager requestAudioFocus returned: " + s); 779 } 780 return focusRequest; 781 } 782 doQuit()783 public void doQuit() { 784 Log.d(TAG, "doQuit"); 785 if (mCurrentDevice != null) { 786 NativeInterface.disconnectNative(getByteAddress(mCurrentDevice)); 787 } 788 routeHfpAudio(false); 789 returnAudioFocusIfNecessary(); 790 quitNow(); 791 } 792 returnAudioFocusIfNecessary()793 private void returnAudioFocusIfNecessary() { 794 if (mAudioFocusRequest == null) return; 795 mAudioManager.abandonAudioFocusRequest(mAudioFocusRequest); 796 mAudioFocusRequest = null; 797 } 798 hfToAmVol(int hfVol)799 static int hfToAmVol(int hfVol) { 800 int amRange = sMaxAmVcVol - sMinAmVcVol; 801 int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; 802 int amOffset = (amRange * (hfVol - MIN_HFP_SCO_VOICE_CALL_VOLUME)) / hfRange; 803 int amVol = sMinAmVcVol + amOffset; 804 Log.d(TAG, "HF -> AM " + hfVol + " " + amVol); 805 return amVol; 806 } 807 amToHfVol(int amVol)808 static int amToHfVol(int amVol) { 809 int amRange = (sMaxAmVcVol > sMinAmVcVol) ? (sMaxAmVcVol - sMinAmVcVol) : 1; 810 int hfRange = MAX_HFP_SCO_VOICE_CALL_VOLUME - MIN_HFP_SCO_VOICE_CALL_VOLUME; 811 int hfOffset = (hfRange * (amVol - sMinAmVcVol)) / amRange; 812 int hfVol = MIN_HFP_SCO_VOICE_CALL_VOLUME + hfOffset; 813 Log.d(TAG, "AM -> HF " + amVol + " " + hfVol); 814 return hfVol; 815 } 816 817 class Disconnected extends State { 818 @Override enter()819 public void enter() { 820 Log.d(TAG, "Enter Disconnected: " + getCurrentMessage().what); 821 822 // cleanup 823 mIndicatorNetworkState = HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE; 824 mIndicatorNetworkType = HeadsetClientHalConstants.SERVICE_TYPE_HOME; 825 mIndicatorNetworkSignal = 0; 826 mIndicatorBatteryLevel = 0; 827 mInBandRing = false; 828 829 mAudioWbs = false; 830 831 // will be set on connect 832 833 mOperatorName = null; 834 mSubscriberInfo = null; 835 836 mQueuedActions = new LinkedList<Pair<Integer, Object>>(); 837 clearPendingAction(); 838 839 mCalls.clear(); 840 mCallsUpdate.clear(); 841 842 mPeerFeatures = 0; 843 mChldFeatures = 0; 844 845 removeMessages(QUERY_CURRENT_CALLS); 846 847 if (mPrevState == mConnecting) { 848 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 849 BluetoothProfile.STATE_CONNECTING); 850 } else if (mPrevState == mConnected || mPrevState == mAudioOn) { 851 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 852 BluetoothProfile.STATE_CONNECTED); 853 } else if (mPrevState != null) { // null is the default state before Disconnected 854 Log.e(TAG, "Connected: Illegal state transition from " + mPrevState.getName() 855 + " to Connecting, mCurrentDevice=" + mCurrentDevice); 856 } 857 mCurrentDevice = null; 858 } 859 860 @Override processMessage(Message message)861 public synchronized boolean processMessage(Message message) { 862 Log.d(TAG, "Disconnected process message: " + message.what); 863 864 if (mCurrentDevice != null) { 865 Log.e(TAG, "ERROR: current device not null in Disconnected"); 866 return NOT_HANDLED; 867 } 868 869 switch (message.what) { 870 case CONNECT: 871 BluetoothDevice device = (BluetoothDevice) message.obj; 872 if (!NativeInterface.connectNative(getByteAddress(device))) { 873 // No state transition is involved, fire broadcast immediately 874 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 875 BluetoothProfile.STATE_DISCONNECTED); 876 break; 877 } 878 mCurrentDevice = device; 879 transitionTo(mConnecting); 880 break; 881 case DISCONNECT: 882 // ignore 883 break; 884 case StackEvent.STACK_EVENT: 885 StackEvent event = (StackEvent) message.obj; 886 if (DBG) { 887 Log.d(TAG, "Stack event type: " + event.type); 888 } 889 switch (event.type) { 890 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 891 if (DBG) { 892 Log.d(TAG, "Disconnected: Connection " + event.device 893 + " state changed:" + event.valueInt); 894 } 895 processConnectionEvent(event.valueInt, event.device); 896 break; 897 default: 898 Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type); 899 break; 900 } 901 break; 902 default: 903 return NOT_HANDLED; 904 } 905 return HANDLED; 906 } 907 908 // in Disconnected state processConnectionEvent(int state, BluetoothDevice device)909 private void processConnectionEvent(int state, BluetoothDevice device) { 910 switch (state) { 911 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 912 Log.w(TAG, "HFPClient Connecting from Disconnected state"); 913 if (okToConnect(device)) { 914 Log.i(TAG, "Incoming AG accepted"); 915 mCurrentDevice = device; 916 transitionTo(mConnecting); 917 } else { 918 Log.i(TAG, "Incoming AG rejected. priority=" + mService.getPriority(device) 919 + " bondState=" + device.getBondState()); 920 // reject the connection and stay in Disconnected state 921 // itself 922 NativeInterface.disconnectNative(getByteAddress(device)); 923 // the other profile connection should be initiated 924 AdapterService adapterService = AdapterService.getAdapterService(); 925 // No state transition is involved, fire broadcast immediately 926 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 927 BluetoothProfile.STATE_DISCONNECTED); 928 } 929 break; 930 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 931 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 932 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 933 default: 934 Log.i(TAG, "ignoring state: " + state); 935 break; 936 } 937 } 938 939 @Override exit()940 public void exit() { 941 if (DBG) { 942 Log.d(TAG, "Exit Disconnected: " + getCurrentMessage().what); 943 } 944 mPrevState = this; 945 } 946 } 947 948 class Connecting extends State { 949 @Override enter()950 public void enter() { 951 if (DBG) { 952 Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what); 953 } 954 // This message is either consumed in processMessage or 955 // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since 956 // the only transition is when connection attempt is initiated. 957 sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS); 958 if (mPrevState == mDisconnected) { 959 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTING, 960 BluetoothProfile.STATE_DISCONNECTED); 961 } else { 962 String prevStateName = mPrevState == null ? "null" : mPrevState.getName(); 963 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName 964 + " to Connecting, mCurrentDevice=" + mCurrentDevice); 965 } 966 } 967 968 @Override processMessage(Message message)969 public synchronized boolean processMessage(Message message) { 970 if (DBG) { 971 Log.d(TAG, "Connecting process message: " + message.what); 972 } 973 974 switch (message.what) { 975 case CONNECT: 976 case CONNECT_AUDIO: 977 case DISCONNECT: 978 deferMessage(message); 979 break; 980 case StackEvent.STACK_EVENT: 981 StackEvent event = (StackEvent) message.obj; 982 if (DBG) { 983 Log.d(TAG, "Connecting: event type: " + event.type); 984 } 985 switch (event.type) { 986 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 987 if (DBG) { 988 Log.d(TAG, 989 "Connecting: Connection " + event.device + " state changed:" 990 + event.valueInt); 991 } 992 processConnectionEvent(event.valueInt, event.valueInt2, event.valueInt3, 993 event.device); 994 break; 995 case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 996 case StackEvent.EVENT_TYPE_NETWORK_STATE: 997 case StackEvent.EVENT_TYPE_ROAMING_STATE: 998 case StackEvent.EVENT_TYPE_NETWORK_SIGNAL: 999 case StackEvent.EVENT_TYPE_BATTERY_LEVEL: 1000 case StackEvent.EVENT_TYPE_CALL: 1001 case StackEvent.EVENT_TYPE_CALLSETUP: 1002 case StackEvent.EVENT_TYPE_CALLHELD: 1003 case StackEvent.EVENT_TYPE_RESP_AND_HOLD: 1004 case StackEvent.EVENT_TYPE_CLIP: 1005 case StackEvent.EVENT_TYPE_CALL_WAITING: 1006 case StackEvent.EVENT_TYPE_VOLUME_CHANGED: 1007 deferMessage(message); 1008 break; 1009 case StackEvent.EVENT_TYPE_CMD_RESULT: 1010 case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO: 1011 case StackEvent.EVENT_TYPE_CURRENT_CALLS: 1012 case StackEvent.EVENT_TYPE_OPERATOR_NAME: 1013 default: 1014 Log.e(TAG, "Connecting: ignoring stack event: " + event.type); 1015 break; 1016 } 1017 break; 1018 case CONNECTING_TIMEOUT: 1019 // We timed out trying to connect, transition to disconnected. 1020 Log.w(TAG, "Connection timeout for " + mCurrentDevice); 1021 transitionTo(mDisconnected); 1022 break; 1023 1024 default: 1025 Log.w(TAG, "Message not handled " + message); 1026 return NOT_HANDLED; 1027 } 1028 return HANDLED; 1029 } 1030 1031 // in Connecting state processConnectionEvent(int state, int peerFeat, int chldFeat, BluetoothDevice device)1032 private void processConnectionEvent(int state, int peerFeat, int chldFeat, 1033 BluetoothDevice device) { 1034 switch (state) { 1035 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1036 transitionTo(mDisconnected); 1037 break; 1038 1039 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED: 1040 Log.d(TAG, "HFPClient Connected from Connecting state"); 1041 1042 mPeerFeatures = peerFeat; 1043 mChldFeatures = chldFeat; 1044 1045 // We do not support devices which do not support enhanced call status (ECS). 1046 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) { 1047 NativeInterface.disconnectNative(getByteAddress(device)); 1048 return; 1049 } 1050 1051 // Send AT+NREC to remote if supported by audio 1052 if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED && ( 1053 (mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR) 1054 == HeadsetClientHalConstants.PEER_FEAT_ECNR)) { 1055 if (NativeInterface.sendATCmdNative(getByteAddress(mCurrentDevice), 1056 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC, 1, 0, 1057 null)) { 1058 addQueuedAction(DISABLE_NREC); 1059 } else { 1060 Log.e(TAG, "Failed to send NREC"); 1061 } 1062 } 1063 1064 int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1065 deferMessage( 1066 obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0)); 1067 // Mic is either in ON state (full volume) or OFF state. There is no way in 1068 // Android to change the MIC volume. 1069 deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME, 1070 mAudioManager.isMicrophoneMute() ? 0 : 15, 0)); 1071 // query subscriber info 1072 deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO)); 1073 transitionTo(mConnected); 1074 break; 1075 1076 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 1077 if (!mCurrentDevice.equals(device)) { 1078 Log.w(TAG, "incoming connection event, device: " + device); 1079 // No state transition is involved, fire broadcast immediately 1080 broadcastConnectionState(mCurrentDevice, 1081 BluetoothProfile.STATE_DISCONNECTED, 1082 BluetoothProfile.STATE_CONNECTING); 1083 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1084 BluetoothProfile.STATE_DISCONNECTED); 1085 1086 mCurrentDevice = device; 1087 } 1088 break; 1089 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 1090 /* outgoing connecting started */ 1091 if (DBG) { 1092 Log.d(TAG, "outgoing connection started, ignore"); 1093 } 1094 break; 1095 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 1096 default: 1097 Log.e(TAG, "Incorrect state: " + state); 1098 break; 1099 } 1100 } 1101 1102 @Override exit()1103 public void exit() { 1104 if (DBG) { 1105 Log.d(TAG, "Exit Connecting: " + getCurrentMessage().what); 1106 } 1107 removeMessages(CONNECTING_TIMEOUT); 1108 mPrevState = this; 1109 } 1110 } 1111 1112 class Connected extends State { 1113 int mCommandedSpeakerVolume = -1; 1114 1115 @Override enter()1116 public void enter() { 1117 if (DBG) { 1118 Log.d(TAG, "Enter Connected: " + getCurrentMessage().what); 1119 } 1120 mAudioWbs = false; 1121 mCommandedSpeakerVolume = -1; 1122 if (mPrevState == mConnecting) { 1123 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1124 BluetoothProfile.STATE_CONNECTING); 1125 MetricsLogger.logProfileConnectionEvent( 1126 BluetoothMetricsProto.ProfileId.HEADSET_CLIENT); 1127 } else if (mPrevState != mAudioOn) { 1128 String prevStateName = mPrevState == null ? "null" : mPrevState.getName(); 1129 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName 1130 + " to Connecting, mCurrentDevice=" + mCurrentDevice); 1131 } 1132 } 1133 1134 @Override processMessage(Message message)1135 public synchronized boolean processMessage(Message message) { 1136 if (DBG) { 1137 Log.d(TAG, "Connected process message: " + message.what); 1138 } 1139 if (DBG) { 1140 if (mCurrentDevice == null) { 1141 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1142 return NOT_HANDLED; 1143 } 1144 } 1145 1146 switch (message.what) { 1147 case CONNECT: 1148 BluetoothDevice device = (BluetoothDevice) message.obj; 1149 if (mCurrentDevice.equals(device)) { 1150 // already connected to this device, do nothing 1151 break; 1152 } 1153 NativeInterface.connectNative(getByteAddress(device)); 1154 break; 1155 case DISCONNECT: 1156 BluetoothDevice dev = (BluetoothDevice) message.obj; 1157 if (!mCurrentDevice.equals(dev)) { 1158 break; 1159 } 1160 if (!NativeInterface.disconnectNative(getByteAddress(dev))) { 1161 Log.e(TAG, "disconnectNative failed for " + dev); 1162 } 1163 break; 1164 1165 case CONNECT_AUDIO: 1166 if (!NativeInterface.connectAudioNative(getByteAddress(mCurrentDevice))) { 1167 Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice); 1168 // No state transition is involved, fire broadcast immediately 1169 broadcastAudioState(mCurrentDevice, 1170 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1171 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); 1172 } else { // We have successfully sent a connect request! 1173 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING; 1174 } 1175 break; 1176 1177 case DISCONNECT_AUDIO: 1178 if (!NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1179 Log.e(TAG, "ERROR: Couldn't disconnect Audio for device " + mCurrentDevice); 1180 } 1181 break; 1182 1183 case VOICE_RECOGNITION_START: 1184 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) { 1185 if (NativeInterface.startVoiceRecognitionNative( 1186 getByteAddress(mCurrentDevice))) { 1187 addQueuedAction(VOICE_RECOGNITION_START); 1188 } else { 1189 Log.e(TAG, "ERROR: Couldn't start voice recognition"); 1190 } 1191 } 1192 break; 1193 1194 case VOICE_RECOGNITION_STOP: 1195 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) { 1196 if (NativeInterface.stopVoiceRecognitionNative( 1197 getByteAddress(mCurrentDevice))) { 1198 addQueuedAction(VOICE_RECOGNITION_STOP); 1199 } else { 1200 Log.e(TAG, "ERROR: Couldn't stop voice recognition"); 1201 } 1202 } 1203 break; 1204 1205 // Called only for Mute/Un-mute - Mic volume change is not allowed. 1206 case SET_MIC_VOLUME: 1207 break; 1208 case SET_SPEAKER_VOLUME: 1209 // This message should always contain the volume in AudioManager max normalized. 1210 int amVol = message.arg1; 1211 int hfVol = amToHfVol(amVol); 1212 if (amVol != mCommandedSpeakerVolume) { 1213 Log.d(TAG, "Volume" + amVol + ":" + mCommandedSpeakerVolume); 1214 // Volume was changed by a 3rd party 1215 mCommandedSpeakerVolume = -1; 1216 if (NativeInterface.setVolumeNative(getByteAddress(mCurrentDevice), 1217 HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) { 1218 addQueuedAction(SET_SPEAKER_VOLUME); 1219 } 1220 } 1221 break; 1222 case DIAL_NUMBER: 1223 // Add the call as an outgoing call. 1224 BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj; 1225 mCalls.put(HF_ORIGINATED_CALL_ID, c); 1226 1227 if (NativeInterface.dialNative(getByteAddress(mCurrentDevice), c.getNumber())) { 1228 addQueuedAction(DIAL_NUMBER, c.getNumber()); 1229 // Start looping on calling current calls. 1230 sendMessage(QUERY_CURRENT_CALLS); 1231 } else { 1232 Log.e(TAG, 1233 "ERROR: Cannot dial with a given number:" + (String) message.obj); 1234 // Set the call to terminated remove. 1235 c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 1236 sendCallChangedIntent(c); 1237 mCalls.remove(HF_ORIGINATED_CALL_ID); 1238 } 1239 break; 1240 case ACCEPT_CALL: 1241 acceptCall(message.arg1); 1242 break; 1243 case REJECT_CALL: 1244 rejectCall(); 1245 break; 1246 case HOLD_CALL: 1247 holdCall(); 1248 break; 1249 case TERMINATE_CALL: 1250 terminateCall(); 1251 break; 1252 case ENTER_PRIVATE_MODE: 1253 enterPrivateMode(message.arg1); 1254 break; 1255 case EXPLICIT_CALL_TRANSFER: 1256 explicitCallTransfer(); 1257 break; 1258 case SEND_DTMF: 1259 if (NativeInterface.sendDtmfNative(getByteAddress(mCurrentDevice), 1260 (byte) message.arg1)) { 1261 addQueuedAction(SEND_DTMF); 1262 } else { 1263 Log.e(TAG, "ERROR: Couldn't send DTMF"); 1264 } 1265 break; 1266 case SUBSCRIBER_INFO: 1267 if (NativeInterface.retrieveSubscriberInfoNative( 1268 getByteAddress(mCurrentDevice))) { 1269 addQueuedAction(SUBSCRIBER_INFO); 1270 } else { 1271 Log.e(TAG, "ERROR: Couldn't retrieve subscriber info"); 1272 } 1273 break; 1274 case QUERY_CURRENT_CALLS: 1275 removeMessages(QUERY_CURRENT_CALLS); 1276 if (mCalls.size() > 0) { 1277 // If there are ongoing calls periodically check their status. 1278 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 1279 } 1280 queryCallsStart(); 1281 break; 1282 case StackEvent.STACK_EVENT: 1283 Intent intent = null; 1284 StackEvent event = (StackEvent) message.obj; 1285 if (DBG) { 1286 Log.d(TAG, "Connected: event type: " + event.type); 1287 } 1288 1289 switch (event.type) { 1290 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 1291 if (DBG) { 1292 Log.d(TAG, "Connected: Connection state changed: " + event.device 1293 + ": " + event.valueInt); 1294 } 1295 processConnectionEvent(event.valueInt, event.device); 1296 break; 1297 case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 1298 if (DBG) { 1299 Log.d(TAG, "Connected: Audio state changed: " + event.device + ": " 1300 + event.valueInt); 1301 } 1302 processAudioEvent(event.valueInt, event.device); 1303 break; 1304 case StackEvent.EVENT_TYPE_NETWORK_STATE: 1305 if (DBG) { 1306 Log.d(TAG, "Connected: Network state: " + event.valueInt); 1307 } 1308 mIndicatorNetworkState = event.valueInt; 1309 1310 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1311 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, 1312 event.valueInt); 1313 1314 if (mIndicatorNetworkState 1315 == HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 1316 mOperatorName = null; 1317 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1318 mOperatorName); 1319 } 1320 1321 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1322 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1323 1324 if (mIndicatorNetworkState 1325 == HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) { 1326 if (NativeInterface.queryCurrentOperatorNameNative( 1327 getByteAddress(mCurrentDevice))) { 1328 addQueuedAction(QUERY_OPERATOR_NAME); 1329 } else { 1330 Log.e(TAG, "ERROR: Couldn't querry operator name"); 1331 } 1332 } 1333 break; 1334 case StackEvent.EVENT_TYPE_ROAMING_STATE: 1335 mIndicatorNetworkType = event.valueInt; 1336 1337 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1338 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, 1339 event.valueInt); 1340 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1341 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1342 break; 1343 case StackEvent.EVENT_TYPE_NETWORK_SIGNAL: 1344 mIndicatorNetworkSignal = event.valueInt; 1345 1346 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1347 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, 1348 event.valueInt); 1349 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1350 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1351 break; 1352 case StackEvent.EVENT_TYPE_BATTERY_LEVEL: 1353 mIndicatorBatteryLevel = event.valueInt; 1354 1355 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1356 intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, 1357 event.valueInt); 1358 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1359 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1360 break; 1361 case StackEvent.EVENT_TYPE_OPERATOR_NAME: 1362 mOperatorName = event.valueString; 1363 1364 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1365 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1366 event.valueString); 1367 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1368 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1369 break; 1370 case StackEvent.EVENT_TYPE_VR_STATE_CHANGED: 1371 if (mVoiceRecognitionActive != event.valueInt) { 1372 mVoiceRecognitionActive = event.valueInt; 1373 1374 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1375 intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 1376 mVoiceRecognitionActive); 1377 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1378 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1379 } 1380 break; 1381 case StackEvent.EVENT_TYPE_CALL: 1382 case StackEvent.EVENT_TYPE_CALLSETUP: 1383 case StackEvent.EVENT_TYPE_CALLHELD: 1384 case StackEvent.EVENT_TYPE_RESP_AND_HOLD: 1385 case StackEvent.EVENT_TYPE_CLIP: 1386 case StackEvent.EVENT_TYPE_CALL_WAITING: 1387 sendMessage(QUERY_CURRENT_CALLS); 1388 break; 1389 case StackEvent.EVENT_TYPE_CURRENT_CALLS: 1390 queryCallsUpdate(event.valueInt, event.valueInt3, event.valueString, 1391 event.valueInt4 1392 == HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI, 1393 event.valueInt2 1394 == HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING); 1395 break; 1396 case StackEvent.EVENT_TYPE_VOLUME_CHANGED: 1397 if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) { 1398 mCommandedSpeakerVolume = hfToAmVol(event.valueInt2); 1399 Log.d(TAG, "AM volume set to " + mCommandedSpeakerVolume); 1400 mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 1401 +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI); 1402 } else if (event.valueInt 1403 == HeadsetClientHalConstants.VOLUME_TYPE_MIC) { 1404 mAudioManager.setMicrophoneMute(event.valueInt2 == 0); 1405 } 1406 break; 1407 case StackEvent.EVENT_TYPE_CMD_RESULT: 1408 Pair<Integer, Object> queuedAction = mQueuedActions.poll(); 1409 1410 // should not happen but... 1411 if (queuedAction == null || queuedAction.first == NO_ACTION) { 1412 clearPendingAction(); 1413 break; 1414 } 1415 1416 if (DBG) { 1417 Log.d(TAG, "Connected: command result: " + event.valueInt 1418 + " queuedAction: " + queuedAction.first); 1419 } 1420 1421 switch (queuedAction.first) { 1422 case QUERY_CURRENT_CALLS: 1423 queryCallsDone(); 1424 break; 1425 case VOICE_RECOGNITION_START: 1426 if (event.valueInt == AT_OK) { 1427 mVoiceRecognitionActive = 1428 HeadsetClientHalConstants.VR_STATE_STARTED; 1429 } 1430 break; 1431 case VOICE_RECOGNITION_STOP: 1432 if (event.valueInt == AT_OK) { 1433 mVoiceRecognitionActive = 1434 HeadsetClientHalConstants.VR_STATE_STOPPED; 1435 } 1436 break; 1437 default: 1438 Log.w(TAG, "Unhandled AT OK " + event); 1439 break; 1440 } 1441 1442 break; 1443 case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO: 1444 mSubscriberInfo = event.valueString; 1445 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1446 intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, 1447 mSubscriberInfo); 1448 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1449 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1450 break; 1451 case StackEvent.EVENT_TYPE_IN_BAND_RINGTONE: 1452 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1453 mInBandRing = event.valueInt == IN_BAND_RING_ENABLED; 1454 intent.putExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING, 1455 event.valueInt); 1456 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1457 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1458 if (DBG) { 1459 Log.d(TAG, 1460 event.device.toString() + "onInBandRing" + event.valueInt); 1461 } 1462 break; 1463 case StackEvent.EVENT_TYPE_RING_INDICATION: 1464 // Ringing is not handled at this indication and rather should be 1465 // implemented (by the client of this service). Use the 1466 // CALL_STATE_INCOMING (and similar) handle ringing. 1467 break; 1468 default: 1469 Log.e(TAG, "Unknown stack event: " + event.type); 1470 break; 1471 } 1472 1473 break; 1474 default: 1475 return NOT_HANDLED; 1476 } 1477 return HANDLED; 1478 } 1479 1480 // in Connected state processConnectionEvent(int state, BluetoothDevice device)1481 private void processConnectionEvent(int state, BluetoothDevice device) { 1482 switch (state) { 1483 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1484 if (DBG) { 1485 Log.d(TAG, "Connected disconnects."); 1486 } 1487 // AG disconnects 1488 if (mCurrentDevice.equals(device)) { 1489 transitionTo(mDisconnected); 1490 } else { 1491 Log.e(TAG, "Disconnected from unknown device: " + device); 1492 } 1493 break; 1494 default: 1495 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1496 break; 1497 } 1498 } 1499 1500 // in Connected state processAudioEvent(int state, BluetoothDevice device)1501 private void processAudioEvent(int state, BluetoothDevice device) { 1502 // message from old device 1503 if (!mCurrentDevice.equals(device)) { 1504 Log.e(TAG, "Audio changed on disconnected device: " + device); 1505 return; 1506 } 1507 1508 switch (state) { 1509 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC: 1510 mAudioWbs = true; 1511 // fall through 1512 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED: 1513 // Audio state is split in two parts, the audio focus is maintained by the 1514 // entity exercising this service (typically the Telecom stack) and audio 1515 // routing is handled by the bluetooth stack itself. The only reason to do so is 1516 // because Bluetooth SCO connection from the HF role is not entirely supported 1517 // for routing and volume purposes. 1518 // NOTE: All calls here are routed via the setParameters which changes the 1519 // routing at the Audio HAL level. 1520 1521 if (mService.isScoRouted()) { 1522 StackEvent event = 1523 new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1524 event.valueInt = state; 1525 event.device = device; 1526 sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS); 1527 break; 1528 } 1529 1530 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED; 1531 1532 // We need to set the volume after switching into HFP mode as some Audio HALs 1533 // reset the volume to a known-default on mode switch. 1534 final int amVol = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1535 final int hfVol = amToHfVol(amVol); 1536 1537 if (DBG) { 1538 Log.d(TAG, "hfp_enable=true mAudioWbs is " + mAudioWbs); 1539 } 1540 if (mAudioWbs) { 1541 if (DBG) { 1542 Log.d(TAG, "Setting sampling rate as 16000"); 1543 } 1544 mAudioManager.setParameters("hfp_set_sampling_rate=16000"); 1545 } else { 1546 if (DBG) { 1547 Log.d(TAG, "Setting sampling rate as 8000"); 1548 } 1549 mAudioManager.setParameters("hfp_set_sampling_rate=8000"); 1550 } 1551 if (DBG) { 1552 Log.d(TAG, "hf_volume " + hfVol); 1553 } 1554 routeHfpAudio(true); 1555 mAudioFocusRequest = requestAudioFocus(); 1556 mAudioManager.setParameters("hfp_volume=" + hfVol); 1557 transitionTo(mAudioOn); 1558 break; 1559 1560 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING: 1561 // No state transition is involved, fire broadcast immediately 1562 broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING, 1563 mAudioState); 1564 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING; 1565 break; 1566 1567 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1568 // No state transition is involved, fire broadcast immediately 1569 broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1570 mAudioState); 1571 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1572 break; 1573 1574 default: 1575 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1576 break; 1577 } 1578 } 1579 1580 @Override exit()1581 public void exit() { 1582 if (DBG) { 1583 Log.d(TAG, "Exit Connected: " + getCurrentMessage().what); 1584 } 1585 mPrevState = this; 1586 } 1587 } 1588 1589 class AudioOn extends State { 1590 @Override enter()1591 public void enter() { 1592 if (DBG) { 1593 Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what); 1594 } 1595 broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED, 1596 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 1597 } 1598 1599 @Override processMessage(Message message)1600 public synchronized boolean processMessage(Message message) { 1601 if (DBG) { 1602 Log.d(TAG, "AudioOn process message: " + message.what); 1603 } 1604 if (DBG) { 1605 if (mCurrentDevice == null) { 1606 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1607 return NOT_HANDLED; 1608 } 1609 } 1610 1611 switch (message.what) { 1612 case DISCONNECT: 1613 BluetoothDevice device = (BluetoothDevice) message.obj; 1614 if (!mCurrentDevice.equals(device)) { 1615 break; 1616 } 1617 deferMessage(message); 1618 /* 1619 * fall through - disconnect audio first then expect 1620 * deferred DISCONNECT message in Connected state 1621 */ 1622 case DISCONNECT_AUDIO: 1623 /* 1624 * just disconnect audio and wait for 1625 * StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State 1626 * Machines state changing 1627 */ 1628 if (NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1629 routeHfpAudio(false); 1630 returnAudioFocusIfNecessary(); 1631 } 1632 break; 1633 1634 case HOLD_CALL: 1635 holdCall(); 1636 break; 1637 1638 case StackEvent.STACK_EVENT: 1639 StackEvent event = (StackEvent) message.obj; 1640 if (DBG) { 1641 Log.d(TAG, "AudioOn: event type: " + event.type); 1642 } 1643 switch (event.type) { 1644 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 1645 if (DBG) { 1646 Log.d(TAG, "AudioOn connection state changed" + event.device + ": " 1647 + event.valueInt); 1648 } 1649 processConnectionEvent(event.valueInt, event.device); 1650 break; 1651 case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 1652 if (DBG) { 1653 Log.d(TAG, "AudioOn audio state changed" + event.device + ": " 1654 + event.valueInt); 1655 } 1656 processAudioEvent(event.valueInt, event.device); 1657 break; 1658 default: 1659 return NOT_HANDLED; 1660 } 1661 break; 1662 default: 1663 return NOT_HANDLED; 1664 } 1665 return HANDLED; 1666 } 1667 1668 // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this processConnectionEvent(int state, BluetoothDevice device)1669 private void processConnectionEvent(int state, BluetoothDevice device) { 1670 switch (state) { 1671 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1672 if (mCurrentDevice.equals(device)) { 1673 processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED, 1674 device); 1675 transitionTo(mDisconnected); 1676 } else { 1677 Log.e(TAG, "Disconnected from unknown device: " + device); 1678 } 1679 break; 1680 default: 1681 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1682 break; 1683 } 1684 } 1685 1686 // in AudioOn state processAudioEvent(int state, BluetoothDevice device)1687 private void processAudioEvent(int state, BluetoothDevice device) { 1688 if (!mCurrentDevice.equals(device)) { 1689 Log.e(TAG, "Audio changed on disconnected device: " + device); 1690 return; 1691 } 1692 1693 switch (state) { 1694 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1695 removeMessages(DISCONNECT_AUDIO); 1696 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1697 // Audio focus may still be held by the entity controlling the actual call 1698 // (such as Telecom) and hence this will still keep the call around, there 1699 // is not much we can do here since dropping the call without user consent 1700 // even if the audio connection snapped may not be a good idea. 1701 routeHfpAudio(false); 1702 returnAudioFocusIfNecessary(); 1703 transitionTo(mConnected); 1704 break; 1705 1706 default: 1707 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1708 break; 1709 } 1710 } 1711 1712 @Override exit()1713 public void exit() { 1714 if (DBG) { 1715 Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what); 1716 } 1717 mPrevState = this; 1718 broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1719 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); 1720 } 1721 } 1722 1723 /** 1724 * @hide 1725 */ getConnectionState(BluetoothDevice device)1726 public synchronized int getConnectionState(BluetoothDevice device) { 1727 if (mCurrentDevice == null) { 1728 return BluetoothProfile.STATE_DISCONNECTED; 1729 } 1730 1731 if (!mCurrentDevice.equals(device)) { 1732 return BluetoothProfile.STATE_DISCONNECTED; 1733 } 1734 1735 IState currentState = getCurrentState(); 1736 if (currentState == mConnecting) { 1737 return BluetoothProfile.STATE_CONNECTING; 1738 } 1739 1740 if (currentState == mConnected || currentState == mAudioOn) { 1741 return BluetoothProfile.STATE_CONNECTED; 1742 } 1743 1744 Log.e(TAG, "Bad currentState: " + currentState); 1745 return BluetoothProfile.STATE_DISCONNECTED; 1746 } 1747 broadcastAudioState(BluetoothDevice device, int newState, int prevState)1748 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1749 StatsLog.write(StatsLog.BLUETOOTH_SCO_CONNECTION_STATE_CHANGED, 1750 AdapterService.getAdapterService().obfuscateAddress(device), 1751 getConnectionStateFromAudioState(newState), mAudioWbs 1752 ? BluetoothHfpProtoEnums.SCO_CODEC_MSBC 1753 : BluetoothHfpProtoEnums.SCO_CODEC_CVSD); 1754 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED); 1755 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1756 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1757 if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { 1758 intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs); 1759 } 1760 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1761 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1762 if (DBG) { 1763 Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState); 1764 } 1765 } 1766 1767 // This method does not check for error condition (newState == prevState) broadcastConnectionState(BluetoothDevice device, int newState, int prevState)1768 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1769 if (DBG) { 1770 Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState); 1771 } 1772 /* 1773 * Notifying the connection state change of the profile before sending 1774 * the intent for connection state change, as it was causing a race 1775 * condition, with the UI not being updated with the correct connection 1776 * state. 1777 */ 1778 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); 1779 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1780 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1781 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1782 1783 // add feature extras when connected 1784 if (newState == BluetoothProfile.STATE_CONNECTED) { 1785 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) 1786 == HeadsetClientHalConstants.PEER_FEAT_3WAY) { 1787 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true); 1788 } 1789 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_VREC) 1790 == HeadsetClientHalConstants.PEER_FEAT_VREC) { 1791 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_VOICE_RECOGNITION, true); 1792 } 1793 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) 1794 == HeadsetClientHalConstants.PEER_FEAT_REJECT) { 1795 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true); 1796 } 1797 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) 1798 == HeadsetClientHalConstants.PEER_FEAT_ECC) { 1799 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true); 1800 } 1801 1802 // add individual CHLD support extras 1803 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) 1804 == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) { 1805 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, 1806 true); 1807 } 1808 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) 1809 == HeadsetClientHalConstants.CHLD_FEAT_REL) { 1810 intent.putExtra( 1811 BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true); 1812 } 1813 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) 1814 == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) { 1815 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true); 1816 } 1817 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) 1818 == HeadsetClientHalConstants.CHLD_FEAT_MERGE) { 1819 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true); 1820 } 1821 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) 1822 == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) { 1823 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true); 1824 } 1825 } 1826 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1827 } 1828 isConnected()1829 boolean isConnected() { 1830 IState currentState = getCurrentState(); 1831 return (currentState == mConnected || currentState == mAudioOn); 1832 } 1833 getDevicesMatchingConnectionStates(int[] states)1834 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1835 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 1836 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 1837 int connectionState; 1838 synchronized (this) { 1839 for (BluetoothDevice device : bondedDevices) { 1840 ParcelUuid[] featureUuids = device.getUuids(); 1841 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.Handsfree_AG)) { 1842 continue; 1843 } 1844 connectionState = getConnectionState(device); 1845 for (int state : states) { 1846 if (connectionState == state) { 1847 deviceList.add(device); 1848 } 1849 } 1850 } 1851 } 1852 return deviceList; 1853 } 1854 okToConnect(BluetoothDevice device)1855 boolean okToConnect(BluetoothDevice device) { 1856 int priority = mService.getPriority(device); 1857 boolean ret = false; 1858 // check priority and accept or reject the connection. if priority is 1859 // undefined 1860 // it is likely that our SDP has not completed and peer is initiating 1861 // the 1862 // connection. Allow this connection, provided the device is bonded 1863 if ((BluetoothProfile.PRIORITY_OFF < priority) || ( 1864 (BluetoothProfile.PRIORITY_UNDEFINED == priority) && (device.getBondState() 1865 != BluetoothDevice.BOND_NONE))) { 1866 ret = true; 1867 } 1868 return ret; 1869 } 1870 isAudioOn()1871 boolean isAudioOn() { 1872 return (getCurrentState() == mAudioOn); 1873 } 1874 getAudioState(BluetoothDevice device)1875 synchronized int getAudioState(BluetoothDevice device) { 1876 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 1877 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1878 } 1879 return mAudioState; 1880 } 1881 getConnectedDevices()1882 List<BluetoothDevice> getConnectedDevices() { 1883 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 1884 synchronized (this) { 1885 if (isConnected()) { 1886 devices.add(mCurrentDevice); 1887 } 1888 } 1889 return devices; 1890 } 1891 getByteAddress(BluetoothDevice device)1892 private byte[] getByteAddress(BluetoothDevice device) { 1893 return Utils.getBytesFromAddress(device.getAddress()); 1894 } 1895 getCurrentCalls()1896 public List<BluetoothHeadsetClientCall> getCurrentCalls() { 1897 return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values()); 1898 } 1899 getCurrentAgEvents()1900 public Bundle getCurrentAgEvents() { 1901 Bundle b = new Bundle(); 1902 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState); 1903 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal); 1904 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType); 1905 b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel); 1906 b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName); 1907 b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo); 1908 return b; 1909 } 1910 getConnectionStateFromAudioState(int audioState)1911 private static int getConnectionStateFromAudioState(int audioState) { 1912 switch (audioState) { 1913 case BluetoothHeadsetClient.STATE_AUDIO_CONNECTED: 1914 return BluetoothAdapter.STATE_CONNECTED; 1915 case BluetoothHeadsetClient.STATE_AUDIO_CONNECTING: 1916 return BluetoothAdapter.STATE_CONNECTING; 1917 case BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED: 1918 return BluetoothAdapter.STATE_DISCONNECTED; 1919 } 1920 return BluetoothAdapter.STATE_DISCONNECTED; 1921 } 1922 } 1923