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