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