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