HeadsetClientStateMachine.java revision b4db71d3822ccf252fe09a7490f532c3b4fadd0c
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 790 mCurrentDevice = null; 791 792 mCalls.clear(); 793 mCallsUpdate.clear(); 794 795 mPeerFeatures = 0; 796 mChldFeatures = 0; 797 798 removeMessages(QUERY_CURRENT_CALLS); 799 800 if (mPrevState == mConnecting) { 801 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 802 BluetoothProfile.STATE_CONNECTING); 803 } else if (mPrevState == mConnected || mPrevState == mAudioOn) { 804 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 805 BluetoothProfile.STATE_CONNECTED); 806 } else if (mPrevState != null) { // null is the default state before Disconnected 807 Log.e(TAG, "Connected: Illegal state transition from " + mPrevState.getName() 808 + " to Connecting, mCurrentDevice=" + mCurrentDevice); 809 } 810 } 811 812 @Override 813 public synchronized boolean processMessage(Message message) { 814 Log.d(TAG, "Disconnected process message: " + message.what); 815 816 if (mCurrentDevice != null) { 817 Log.e(TAG, "ERROR: current device not null in Disconnected"); 818 return NOT_HANDLED; 819 } 820 821 switch (message.what) { 822 case CONNECT: 823 BluetoothDevice device = (BluetoothDevice) message.obj; 824 if (!NativeInterface.connectNative(getByteAddress(device))) { 825 // No state transition is involved, fire broadcast immediately 826 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 827 BluetoothProfile.STATE_DISCONNECTED); 828 break; 829 } 830 mCurrentDevice = device; 831 transitionTo(mConnecting); 832 break; 833 case DISCONNECT: 834 // ignore 835 break; 836 case StackEvent.STACK_EVENT: 837 StackEvent event = (StackEvent) message.obj; 838 if (DBG) { 839 Log.d(TAG, "Stack event type: " + event.type); 840 } 841 switch (event.type) { 842 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 843 if (DBG) { 844 Log.d(TAG, "Disconnected: Connection " + event.device 845 + " state changed:" + event.valueInt); 846 } 847 processConnectionEvent(event.valueInt, event.device); 848 break; 849 default: 850 Log.e(TAG, "Disconnected: Unexpected stack event: " + event.type); 851 break; 852 } 853 break; 854 default: 855 return NOT_HANDLED; 856 } 857 return HANDLED; 858 } 859 860 // in Disconnected state 861 private void processConnectionEvent(int state, BluetoothDevice device) { 862 switch (state) { 863 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 864 Log.w(TAG, "HFPClient Connecting from Disconnected state"); 865 if (okToConnect(device)) { 866 Log.i(TAG, "Incoming AG accepted"); 867 mCurrentDevice = device; 868 transitionTo(mConnecting); 869 } else { 870 Log.i(TAG, "Incoming AG rejected. priority=" + mService.getPriority(device) 871 + " bondState=" + device.getBondState()); 872 // reject the connection and stay in Disconnected state 873 // itself 874 NativeInterface.disconnectNative(getByteAddress(device)); 875 // the other profile connection should be initiated 876 AdapterService adapterService = AdapterService.getAdapterService(); 877 // No state transition is involved, fire broadcast immediately 878 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 879 BluetoothProfile.STATE_DISCONNECTED); 880 } 881 break; 882 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 883 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 884 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 885 default: 886 Log.i(TAG, "ignoring state: " + state); 887 break; 888 } 889 } 890 891 @Override 892 public void exit() { 893 if (DBG) { 894 Log.d(TAG, "Exit Disconnected: " + getCurrentMessage().what); 895 } 896 mPrevState = this; 897 } 898 } 899 900 class Connecting extends State { 901 @Override 902 public void enter() { 903 if (DBG) { 904 Log.d(TAG, "Enter Connecting: " + getCurrentMessage().what); 905 } 906 // This message is either consumed in processMessage or 907 // removed in exit. It is safe to send a CONNECTING_TIMEOUT here since 908 // the only transition is when connection attempt is initiated. 909 sendMessageDelayed(CONNECTING_TIMEOUT, CONNECTING_TIMEOUT_MS); 910 if (mPrevState == mDisconnected) { 911 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTING, 912 BluetoothProfile.STATE_DISCONNECTED); 913 } else { 914 String prevStateName = mPrevState == null ? "null" : mPrevState.getName(); 915 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName 916 + " to Connecting, mCurrentDevice=" + mCurrentDevice); 917 } 918 } 919 920 @Override 921 public synchronized boolean processMessage(Message message) { 922 if (DBG) { 923 Log.d(TAG, "Connecting process message: " + message.what); 924 } 925 926 switch (message.what) { 927 case CONNECT: 928 case CONNECT_AUDIO: 929 case DISCONNECT: 930 deferMessage(message); 931 break; 932 case StackEvent.STACK_EVENT: 933 StackEvent event = (StackEvent) message.obj; 934 if (DBG) { 935 Log.d(TAG, "Connecting: event type: " + event.type); 936 } 937 switch (event.type) { 938 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 939 if (DBG) { 940 Log.d(TAG, 941 "Connecting: Connection " + event.device + " state changed:" 942 + event.valueInt); 943 } 944 processConnectionEvent(event.valueInt, event.valueInt2, event.valueInt3, 945 event.device); 946 break; 947 case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 948 case StackEvent.EVENT_TYPE_NETWORK_STATE: 949 case StackEvent.EVENT_TYPE_ROAMING_STATE: 950 case StackEvent.EVENT_TYPE_NETWORK_SIGNAL: 951 case StackEvent.EVENT_TYPE_BATTERY_LEVEL: 952 case StackEvent.EVENT_TYPE_CALL: 953 case StackEvent.EVENT_TYPE_CALLSETUP: 954 case StackEvent.EVENT_TYPE_CALLHELD: 955 case StackEvent.EVENT_TYPE_RESP_AND_HOLD: 956 case StackEvent.EVENT_TYPE_CLIP: 957 case StackEvent.EVENT_TYPE_CALL_WAITING: 958 case StackEvent.EVENT_TYPE_VOLUME_CHANGED: 959 deferMessage(message); 960 break; 961 case StackEvent.EVENT_TYPE_CMD_RESULT: 962 case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO: 963 case StackEvent.EVENT_TYPE_CURRENT_CALLS: 964 case StackEvent.EVENT_TYPE_OPERATOR_NAME: 965 default: 966 Log.e(TAG, "Connecting: ignoring stack event: " + event.type); 967 break; 968 } 969 break; 970 case CONNECTING_TIMEOUT: 971 // We timed out trying to connect, transition to disconnected. 972 Log.w(TAG, "Connection timeout for " + mCurrentDevice); 973 transitionTo(mDisconnected); 974 break; 975 976 default: 977 Log.w(TAG, "Message not handled " + message); 978 return NOT_HANDLED; 979 } 980 return HANDLED; 981 } 982 983 // in Connecting state 984 private void processConnectionEvent(int state, int peerFeat, int chldFeat, 985 BluetoothDevice device) { 986 switch (state) { 987 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 988 transitionTo(mDisconnected); 989 break; 990 991 case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED: 992 Log.d(TAG, "HFPClient Connected from Connecting state"); 993 994 mPeerFeatures = peerFeat; 995 mChldFeatures = chldFeat; 996 997 // We do not support devices which do not support enhanced call status (ECS). 998 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECS) == 0) { 999 NativeInterface.disconnectNative(getByteAddress(device)); 1000 return; 1001 } 1002 1003 // Send AT+NREC to remote if supported by audio 1004 if (HeadsetClientHalConstants.HANDSFREECLIENT_NREC_SUPPORTED && ( 1005 (mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECNR) 1006 == HeadsetClientHalConstants.PEER_FEAT_ECNR)) { 1007 if (NativeInterface.sendATCmdNative(getByteAddress(mCurrentDevice), 1008 HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_NREC, 1, 0, 1009 null)) { 1010 addQueuedAction(DISABLE_NREC); 1011 } else { 1012 Log.e(TAG, "Failed to send NREC"); 1013 } 1014 } 1015 1016 int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1017 deferMessage( 1018 obtainMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, amVol, 0)); 1019 // Mic is either in ON state (full volume) or OFF state. There is no way in 1020 // Android to change the MIC volume. 1021 deferMessage(obtainMessage(HeadsetClientStateMachine.SET_MIC_VOLUME, 1022 sAudioManager.isMicrophoneMute() ? 0 : 15, 0)); 1023 // query subscriber info 1024 deferMessage(obtainMessage(HeadsetClientStateMachine.SUBSCRIBER_INFO)); 1025 transitionTo(mConnected); 1026 break; 1027 1028 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED: 1029 if (!mCurrentDevice.equals(device)) { 1030 Log.w(TAG, "incoming connection event, device: " + device); 1031 // No state transition is involved, fire broadcast immediately 1032 broadcastConnectionState(mCurrentDevice, 1033 BluetoothProfile.STATE_DISCONNECTED, 1034 BluetoothProfile.STATE_CONNECTING); 1035 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 1036 BluetoothProfile.STATE_DISCONNECTED); 1037 1038 mCurrentDevice = device; 1039 } 1040 break; 1041 case HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING: 1042 /* outgoing connecting started */ 1043 if (DBG) { 1044 Log.d(TAG, "outgoing connection started, ignore"); 1045 } 1046 break; 1047 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTING: 1048 default: 1049 Log.e(TAG, "Incorrect state: " + state); 1050 break; 1051 } 1052 } 1053 1054 @Override 1055 public void exit() { 1056 if (DBG) { 1057 Log.d(TAG, "Exit Connecting: " + getCurrentMessage().what); 1058 } 1059 removeMessages(CONNECTING_TIMEOUT); 1060 mPrevState = this; 1061 } 1062 } 1063 1064 class Connected extends State { 1065 int mCommandedSpeakerVolume = -1; 1066 1067 @Override 1068 public void enter() { 1069 if (DBG) { 1070 Log.d(TAG, "Enter Connected: " + getCurrentMessage().what); 1071 } 1072 mAudioWbs = false; 1073 mCommandedSpeakerVolume = -1; 1074 if (mPrevState == mConnecting) { 1075 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 1076 BluetoothProfile.STATE_CONNECTING); 1077 } else if (mPrevState != mAudioOn) { 1078 String prevStateName = mPrevState == null ? "null" : mPrevState.getName(); 1079 Log.e(TAG, "Connected: Illegal state transition from " + prevStateName 1080 + " to Connecting, mCurrentDevice=" + mCurrentDevice); 1081 } 1082 } 1083 1084 @Override 1085 public synchronized boolean processMessage(Message message) { 1086 if (DBG) { 1087 Log.d(TAG, "Connected process message: " + message.what); 1088 } 1089 if (DBG) { 1090 if (mCurrentDevice == null) { 1091 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1092 return NOT_HANDLED; 1093 } 1094 } 1095 1096 switch (message.what) { 1097 case CONNECT: 1098 BluetoothDevice device = (BluetoothDevice) message.obj; 1099 if (mCurrentDevice.equals(device)) { 1100 // already connected to this device, do nothing 1101 break; 1102 } 1103 NativeInterface.connectNative(getByteAddress(device)); 1104 break; 1105 case DISCONNECT: 1106 BluetoothDevice dev = (BluetoothDevice) message.obj; 1107 if (!mCurrentDevice.equals(dev)) { 1108 break; 1109 } 1110 if (NativeInterface.disconnectNative(getByteAddress(dev))) { 1111 // No state transition is involved, fire broadcast immediately 1112 broadcastConnectionState(dev, BluetoothProfile.STATE_DISCONNECTING, 1113 BluetoothProfile.STATE_CONNECTED); 1114 } else { 1115 Log.e(TAG, "disconnectNative failed for " + dev); 1116 } 1117 break; 1118 1119 case CONNECT_AUDIO: 1120 if (!NativeInterface.connectAudioNative(getByteAddress(mCurrentDevice))) { 1121 Log.e(TAG, "ERROR: Couldn't connect Audio for device " + mCurrentDevice); 1122 // No state transition is involved, fire broadcast immediately 1123 broadcastAudioState(mCurrentDevice, 1124 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1125 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED); 1126 } else { // We have successfully sent a connect request! 1127 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING; 1128 } 1129 break; 1130 1131 case DISCONNECT_AUDIO: 1132 if (!NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1133 Log.e(TAG, "ERROR: Couldn't disconnect Audio for device " + mCurrentDevice); 1134 } 1135 break; 1136 1137 case VOICE_RECOGNITION_START: 1138 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STOPPED) { 1139 if (NativeInterface.startVoiceRecognitionNative( 1140 getByteAddress(mCurrentDevice))) { 1141 addQueuedAction(VOICE_RECOGNITION_START); 1142 } else { 1143 Log.e(TAG, "ERROR: Couldn't start voice recognition"); 1144 } 1145 } 1146 break; 1147 1148 case VOICE_RECOGNITION_STOP: 1149 if (mVoiceRecognitionActive == HeadsetClientHalConstants.VR_STATE_STARTED) { 1150 if (NativeInterface.stopVoiceRecognitionNative( 1151 getByteAddress(mCurrentDevice))) { 1152 addQueuedAction(VOICE_RECOGNITION_STOP); 1153 } else { 1154 Log.e(TAG, "ERROR: Couldn't stop voice recognition"); 1155 } 1156 } 1157 break; 1158 1159 // Called only for Mute/Un-mute - Mic volume change is not allowed. 1160 case SET_MIC_VOLUME: 1161 break; 1162 case SET_SPEAKER_VOLUME: 1163 // This message should always contain the volume in AudioManager max normalized. 1164 int amVol = message.arg1; 1165 int hfVol = amToHfVol(amVol); 1166 if (amVol != mCommandedSpeakerVolume) { 1167 Log.d(TAG, "Volume" + amVol + ":" + mCommandedSpeakerVolume); 1168 // Volume was changed by a 3rd party 1169 mCommandedSpeakerVolume = -1; 1170 if (NativeInterface.setVolumeNative(getByteAddress(mCurrentDevice), 1171 HeadsetClientHalConstants.VOLUME_TYPE_SPK, hfVol)) { 1172 addQueuedAction(SET_SPEAKER_VOLUME); 1173 } 1174 } 1175 break; 1176 case DIAL_NUMBER: 1177 // Add the call as an outgoing call. 1178 BluetoothHeadsetClientCall c = (BluetoothHeadsetClientCall) message.obj; 1179 mCalls.put(HF_ORIGINATED_CALL_ID, c); 1180 1181 if (NativeInterface.dialNative(getByteAddress(mCurrentDevice), c.getNumber())) { 1182 addQueuedAction(DIAL_NUMBER, c.getNumber()); 1183 // Start looping on calling current calls. 1184 sendMessage(QUERY_CURRENT_CALLS); 1185 } else { 1186 Log.e(TAG, 1187 "ERROR: Cannot dial with a given number:" + (String) message.obj); 1188 // Set the call to terminated remove. 1189 c.setState(BluetoothHeadsetClientCall.CALL_STATE_TERMINATED); 1190 sendCallChangedIntent(c); 1191 mCalls.remove(HF_ORIGINATED_CALL_ID); 1192 } 1193 break; 1194 case ACCEPT_CALL: 1195 acceptCall(message.arg1); 1196 break; 1197 case REJECT_CALL: 1198 rejectCall(); 1199 break; 1200 case HOLD_CALL: 1201 holdCall(); 1202 break; 1203 case TERMINATE_CALL: 1204 terminateCall(); 1205 break; 1206 case ENTER_PRIVATE_MODE: 1207 enterPrivateMode(message.arg1); 1208 break; 1209 case EXPLICIT_CALL_TRANSFER: 1210 explicitCallTransfer(); 1211 break; 1212 case SEND_DTMF: 1213 if (NativeInterface.sendDtmfNative(getByteAddress(mCurrentDevice), 1214 (byte) message.arg1)) { 1215 addQueuedAction(SEND_DTMF); 1216 } else { 1217 Log.e(TAG, "ERROR: Couldn't send DTMF"); 1218 } 1219 break; 1220 case SUBSCRIBER_INFO: 1221 if (NativeInterface.retrieveSubscriberInfoNative( 1222 getByteAddress(mCurrentDevice))) { 1223 addQueuedAction(SUBSCRIBER_INFO); 1224 } else { 1225 Log.e(TAG, "ERROR: Couldn't retrieve subscriber info"); 1226 } 1227 break; 1228 case QUERY_CURRENT_CALLS: 1229 // Whenever the timer expires we query calls if there are outstanding requests 1230 // for query calls. 1231 long currentElapsed = SystemClock.elapsedRealtime(); 1232 if (mClccTimer < currentElapsed) { 1233 queryCallsStart(); 1234 mClccTimer = currentElapsed + QUERY_CURRENT_CALLS_WAIT_MILLIS; 1235 // Request satisfied, ignore all other call query messages. 1236 removeMessages(QUERY_CURRENT_CALLS); 1237 } else { 1238 // Replace all messages with one concrete message. 1239 removeMessages(QUERY_CURRENT_CALLS); 1240 sendMessageDelayed(QUERY_CURRENT_CALLS, QUERY_CURRENT_CALLS_WAIT_MILLIS); 1241 } 1242 break; 1243 case StackEvent.STACK_EVENT: 1244 Intent intent = null; 1245 StackEvent event = (StackEvent) message.obj; 1246 if (DBG) { 1247 Log.d(TAG, "Connected: event type: " + event.type); 1248 } 1249 1250 switch (event.type) { 1251 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 1252 if (DBG) { 1253 Log.d(TAG, "Connected: Connection state changed: " + event.device 1254 + ": " + event.valueInt); 1255 } 1256 processConnectionEvent(event.valueInt, event.device); 1257 break; 1258 case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 1259 if (DBG) { 1260 Log.d(TAG, "Connected: Audio state changed: " + event.device + ": " 1261 + event.valueInt); 1262 } 1263 processAudioEvent(event.valueInt, event.device); 1264 break; 1265 case StackEvent.EVENT_TYPE_NETWORK_STATE: 1266 if (DBG) { 1267 Log.d(TAG, "Connected: Network state: " + event.valueInt); 1268 } 1269 mIndicatorNetworkState = event.valueInt; 1270 1271 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1272 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, 1273 event.valueInt); 1274 1275 if (mIndicatorNetworkState 1276 == HeadsetClientHalConstants.NETWORK_STATE_NOT_AVAILABLE) { 1277 mOperatorName = null; 1278 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1279 mOperatorName); 1280 } 1281 1282 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1283 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1284 1285 if (mIndicatorNetworkState 1286 == HeadsetClientHalConstants.NETWORK_STATE_AVAILABLE) { 1287 if (NativeInterface.queryCurrentOperatorNameNative( 1288 getByteAddress(mCurrentDevice))) { 1289 addQueuedAction(QUERY_OPERATOR_NAME); 1290 } else { 1291 Log.e(TAG, "ERROR: Couldn't querry operator name"); 1292 } 1293 } 1294 break; 1295 case StackEvent.EVENT_TYPE_ROAMING_STATE: 1296 mIndicatorNetworkType = event.valueInt; 1297 1298 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1299 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, 1300 event.valueInt); 1301 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1302 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1303 break; 1304 case StackEvent.EVENT_TYPE_NETWORK_SIGNAL: 1305 mIndicatorNetworkSignal = event.valueInt; 1306 1307 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1308 intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, 1309 event.valueInt); 1310 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1311 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1312 break; 1313 case StackEvent.EVENT_TYPE_BATTERY_LEVEL: 1314 mIndicatorBatteryLevel = event.valueInt; 1315 1316 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1317 intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, 1318 event.valueInt); 1319 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1320 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1321 break; 1322 case StackEvent.EVENT_TYPE_OPERATOR_NAME: 1323 mOperatorName = event.valueString; 1324 1325 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1326 intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, 1327 event.valueString); 1328 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1329 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1330 break; 1331 case StackEvent.EVENT_TYPE_VR_STATE_CHANGED: 1332 if (mVoiceRecognitionActive != event.valueInt) { 1333 mVoiceRecognitionActive = event.valueInt; 1334 1335 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1336 intent.putExtra(BluetoothHeadsetClient.EXTRA_VOICE_RECOGNITION, 1337 mVoiceRecognitionActive); 1338 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1339 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1340 } 1341 break; 1342 case StackEvent.EVENT_TYPE_CALL: 1343 case StackEvent.EVENT_TYPE_CALLSETUP: 1344 case StackEvent.EVENT_TYPE_CALLHELD: 1345 case StackEvent.EVENT_TYPE_RESP_AND_HOLD: 1346 case StackEvent.EVENT_TYPE_CLIP: 1347 case StackEvent.EVENT_TYPE_CALL_WAITING: 1348 sendMessage(QUERY_CURRENT_CALLS); 1349 break; 1350 case StackEvent.EVENT_TYPE_CURRENT_CALLS: 1351 queryCallsUpdate(event.valueInt, event.valueInt3, event.valueString, 1352 event.valueInt4 1353 == HeadsetClientHalConstants.CALL_MPTY_TYPE_MULTI, 1354 event.valueInt2 1355 == HeadsetClientHalConstants.CALL_DIRECTION_OUTGOING); 1356 break; 1357 case StackEvent.EVENT_TYPE_VOLUME_CHANGED: 1358 if (event.valueInt == HeadsetClientHalConstants.VOLUME_TYPE_SPK) { 1359 mCommandedSpeakerVolume = hfToAmVol(event.valueInt2); 1360 Log.d(TAG, "AM volume set to " + mCommandedSpeakerVolume); 1361 sAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 1362 +mCommandedSpeakerVolume, AudioManager.FLAG_SHOW_UI); 1363 } else if (event.valueInt 1364 == HeadsetClientHalConstants.VOLUME_TYPE_MIC) { 1365 sAudioManager.setMicrophoneMute(event.valueInt2 == 0); 1366 } 1367 break; 1368 case StackEvent.EVENT_TYPE_CMD_RESULT: 1369 Pair<Integer, Object> queuedAction = mQueuedActions.poll(); 1370 1371 // should not happen but... 1372 if (queuedAction == null || queuedAction.first == NO_ACTION) { 1373 clearPendingAction(); 1374 break; 1375 } 1376 1377 if (DBG) { 1378 Log.d(TAG, "Connected: command result: " + event.valueInt 1379 + " queuedAction: " + queuedAction.first); 1380 } 1381 1382 switch (queuedAction.first) { 1383 case QUERY_CURRENT_CALLS: 1384 queryCallsDone(); 1385 break; 1386 case VOICE_RECOGNITION_START: 1387 if (event.valueInt == AT_OK) { 1388 mVoiceRecognitionActive = 1389 HeadsetClientHalConstants.VR_STATE_STARTED; 1390 } 1391 break; 1392 case VOICE_RECOGNITION_STOP: 1393 if (event.valueInt == AT_OK) { 1394 mVoiceRecognitionActive = 1395 HeadsetClientHalConstants.VR_STATE_STOPPED; 1396 } 1397 break; 1398 default: 1399 Log.w(TAG, "Unhandled AT OK " + event); 1400 break; 1401 } 1402 1403 break; 1404 case StackEvent.EVENT_TYPE_SUBSCRIBER_INFO: 1405 mSubscriberInfo = event.valueString; 1406 intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT); 1407 intent.putExtra(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, 1408 mSubscriberInfo); 1409 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, event.device); 1410 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1411 break; 1412 case StackEvent.EVENT_TYPE_RING_INDICATION: 1413 // Ringing is not handled at this indication and rather should be 1414 // implemented (by the client of this service). Use the 1415 // CALL_STATE_INCOMING (and similar) handle ringing. 1416 break; 1417 default: 1418 Log.e(TAG, "Unknown stack event: " + event.type); 1419 break; 1420 } 1421 1422 break; 1423 default: 1424 return NOT_HANDLED; 1425 } 1426 return HANDLED; 1427 } 1428 1429 // in Connected state 1430 private void processConnectionEvent(int state, BluetoothDevice device) { 1431 switch (state) { 1432 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1433 if (DBG) { 1434 Log.d(TAG, "Connected disconnects."); 1435 } 1436 // AG disconnects 1437 if (mCurrentDevice.equals(device)) { 1438 transitionTo(mDisconnected); 1439 } else { 1440 Log.e(TAG, "Disconnected from unknown device: " + device); 1441 } 1442 break; 1443 default: 1444 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1445 break; 1446 } 1447 } 1448 1449 // in Connected state 1450 private void processAudioEvent(int state, BluetoothDevice device) { 1451 // message from old device 1452 if (!mCurrentDevice.equals(device)) { 1453 Log.e(TAG, "Audio changed on disconnected device: " + device); 1454 return; 1455 } 1456 1457 switch (state) { 1458 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED_MSBC: 1459 mAudioWbs = true; 1460 // fall through 1461 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTED: 1462 // Audio state is split in two parts, the audio focus is maintained by the 1463 // entity exercising this service (typically the Telecom stack) and audio 1464 // routing is handled by the bluetooth stack itself. The only reason to do so is 1465 // because Bluetooth SCO connection from the HF role is not entirely supported 1466 // for routing and volume purposes. 1467 // NOTE: All calls here are routed via the setParameters which changes the 1468 // routing at the Audio HAL level. 1469 1470 if (mService.isScoRouted()) { 1471 StackEvent event = 1472 new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 1473 event.valueInt = state; 1474 event.device = device; 1475 sendMessageDelayed(StackEvent.STACK_EVENT, event, ROUTING_DELAY_MS); 1476 break; 1477 } 1478 1479 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTED; 1480 1481 // We need to set the volume after switching into HFP mode as some Audio HALs 1482 // reset the volume to a known-default on mode switch. 1483 final int amVol = sAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1484 final int hfVol = amToHfVol(amVol); 1485 1486 if (DBG) { 1487 Log.d(TAG, "hfp_enable=true mAudioWbs is " + mAudioWbs); 1488 } 1489 if (mAudioWbs) { 1490 if (DBG) { 1491 Log.d(TAG, "Setting sampling rate as 16000"); 1492 } 1493 sAudioManager.setParameters("hfp_set_sampling_rate=16000"); 1494 } else { 1495 if (DBG) { 1496 Log.d(TAG, "Setting sampling rate as 8000"); 1497 } 1498 sAudioManager.setParameters("hfp_set_sampling_rate=8000"); 1499 } 1500 if (DBG) { 1501 Log.d(TAG, "hf_volume " + hfVol); 1502 } 1503 routeHfpAudio(true); 1504 sAudioManager.setParameters("hfp_volume=" + hfVol); 1505 transitionTo(mAudioOn); 1506 break; 1507 1508 case HeadsetClientHalConstants.AUDIO_STATE_CONNECTING: 1509 // No state transition is involved, fire broadcast immediately 1510 broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_CONNECTING, 1511 mAudioState); 1512 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_CONNECTING; 1513 break; 1514 1515 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1516 // No state transition is involved, fire broadcast immediately 1517 broadcastAudioState(device, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1518 mAudioState); 1519 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1520 break; 1521 1522 default: 1523 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1524 break; 1525 } 1526 } 1527 1528 @Override 1529 public void exit() { 1530 if (DBG) { 1531 Log.d(TAG, "Exit Connected: " + getCurrentMessage().what); 1532 } 1533 mPrevState = this; 1534 } 1535 } 1536 1537 class AudioOn extends State { 1538 @Override 1539 public void enter() { 1540 if (DBG) { 1541 Log.d(TAG, "Enter AudioOn: " + getCurrentMessage().what); 1542 } 1543 broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_CONNECTED, 1544 BluetoothHeadsetClient.STATE_AUDIO_CONNECTING); 1545 } 1546 1547 @Override 1548 public synchronized boolean processMessage(Message message) { 1549 if (DBG) { 1550 Log.d(TAG, "AudioOn process message: " + message.what); 1551 } 1552 if (DBG) { 1553 if (mCurrentDevice == null) { 1554 Log.e(TAG, "ERROR: mCurrentDevice is null in Connected"); 1555 return NOT_HANDLED; 1556 } 1557 } 1558 1559 switch (message.what) { 1560 case DISCONNECT: 1561 BluetoothDevice device = (BluetoothDevice) message.obj; 1562 if (!mCurrentDevice.equals(device)) { 1563 break; 1564 } 1565 deferMessage(message); 1566 /* 1567 * fall through - disconnect audio first then expect 1568 * deferred DISCONNECT message in Connected state 1569 */ 1570 case DISCONNECT_AUDIO: 1571 /* 1572 * just disconnect audio and wait for 1573 * StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, that triggers State 1574 * Machines state changing 1575 */ 1576 if (NativeInterface.disconnectAudioNative(getByteAddress(mCurrentDevice))) { 1577 routeHfpAudio(false); 1578 } 1579 break; 1580 1581 case HOLD_CALL: 1582 holdCall(); 1583 break; 1584 1585 case StackEvent.STACK_EVENT: 1586 StackEvent event = (StackEvent) message.obj; 1587 if (DBG) { 1588 Log.d(TAG, "AudioOn: event type: " + event.type); 1589 } 1590 switch (event.type) { 1591 case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED: 1592 if (DBG) { 1593 Log.d(TAG, "AudioOn connection state changed" + event.device + ": " 1594 + event.valueInt); 1595 } 1596 processConnectionEvent(event.valueInt, event.device); 1597 break; 1598 case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED: 1599 if (DBG) { 1600 Log.d(TAG, "AudioOn audio state changed" + event.device + ": " 1601 + event.valueInt); 1602 } 1603 processAudioEvent(event.valueInt, event.device); 1604 break; 1605 default: 1606 return NOT_HANDLED; 1607 } 1608 break; 1609 default: 1610 return NOT_HANDLED; 1611 } 1612 return HANDLED; 1613 } 1614 1615 // in AudioOn state. Can AG disconnect RFCOMM prior to SCO? Handle this 1616 private void processConnectionEvent(int state, BluetoothDevice device) { 1617 switch (state) { 1618 case HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED: 1619 if (mCurrentDevice.equals(device)) { 1620 processAudioEvent(HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED, 1621 device); 1622 transitionTo(mDisconnected); 1623 } else { 1624 Log.e(TAG, "Disconnected from unknown device: " + device); 1625 } 1626 break; 1627 default: 1628 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 1629 break; 1630 } 1631 } 1632 1633 // in AudioOn state 1634 private void processAudioEvent(int state, BluetoothDevice device) { 1635 if (!mCurrentDevice.equals(device)) { 1636 Log.e(TAG, "Audio changed on disconnected device: " + device); 1637 return; 1638 } 1639 1640 switch (state) { 1641 case HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED: 1642 removeMessages(DISCONNECT_AUDIO); 1643 mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1644 // Audio focus may still be held by the entity controlling the actual call 1645 // (such as Telecom) and hence this will still keep the call around, there 1646 // is not much we can do here since dropping the call without user consent 1647 // even if the audio connection snapped may not be a good idea. 1648 routeHfpAudio(false); 1649 transitionTo(mConnected); 1650 break; 1651 1652 default: 1653 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 1654 break; 1655 } 1656 } 1657 1658 @Override 1659 public void exit() { 1660 if (DBG) { 1661 Log.d(TAG, "Exit AudioOn: " + getCurrentMessage().what); 1662 } 1663 mPrevState = this; 1664 broadcastAudioState(mCurrentDevice, BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, 1665 BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); 1666 } 1667 } 1668 1669 /** 1670 * @hide 1671 */ 1672 public synchronized int getConnectionState(BluetoothDevice device) { 1673 if (mCurrentDevice == null) { 1674 return BluetoothProfile.STATE_DISCONNECTED; 1675 } 1676 1677 if (!mCurrentDevice.equals(device)) { 1678 return BluetoothProfile.STATE_DISCONNECTED; 1679 } 1680 1681 IState currentState = getCurrentState(); 1682 if (currentState == mConnecting) { 1683 return BluetoothProfile.STATE_CONNECTING; 1684 } 1685 1686 if (currentState == mConnected || currentState == mAudioOn) { 1687 return BluetoothProfile.STATE_CONNECTED; 1688 } 1689 1690 Log.e(TAG, "Bad currentState: " + currentState); 1691 return BluetoothProfile.STATE_DISCONNECTED; 1692 } 1693 1694 private void broadcastAudioState(BluetoothDevice device, int newState, int prevState) { 1695 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED); 1696 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1697 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1698 if (newState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { 1699 intent.putExtra(BluetoothHeadsetClient.EXTRA_AUDIO_WBS, mAudioWbs); 1700 } 1701 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1702 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1703 if (DBG) { 1704 Log.d(TAG, "Audio state " + device + ": " + prevState + "->" + newState); 1705 } 1706 } 1707 1708 // This method does not check for error condition (newState == prevState) 1709 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 1710 if (DBG) { 1711 Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState); 1712 } 1713 /* 1714 * Notifying the connection state change of the profile before sending 1715 * the intent for connection state change, as it was causing a race 1716 * condition, with the UI not being updated with the correct connection 1717 * state. 1718 */ 1719 Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); 1720 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1721 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1722 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1723 1724 // add feature extras when connected 1725 if (newState == BluetoothProfile.STATE_CONNECTED) { 1726 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_3WAY) 1727 == HeadsetClientHalConstants.PEER_FEAT_3WAY) { 1728 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_3WAY_CALLING, true); 1729 } 1730 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_REJECT) 1731 == HeadsetClientHalConstants.PEER_FEAT_REJECT) { 1732 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_REJECT_CALL, true); 1733 } 1734 if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_ECC) 1735 == HeadsetClientHalConstants.PEER_FEAT_ECC) { 1736 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ECC, true); 1737 } 1738 1739 // add individual CHLD support extras 1740 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) 1741 == HeadsetClientHalConstants.CHLD_FEAT_HOLD_ACC) { 1742 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL, 1743 true); 1744 } 1745 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL) 1746 == HeadsetClientHalConstants.CHLD_FEAT_REL) { 1747 intent.putExtra( 1748 BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL, true); 1749 } 1750 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) 1751 == HeadsetClientHalConstants.CHLD_FEAT_REL_ACC) { 1752 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT, true); 1753 } 1754 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE) 1755 == HeadsetClientHalConstants.CHLD_FEAT_MERGE) { 1756 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE, true); 1757 } 1758 if ((mChldFeatures & HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) 1759 == HeadsetClientHalConstants.CHLD_FEAT_MERGE_DETACH) { 1760 intent.putExtra(BluetoothHeadsetClient.EXTRA_AG_FEATURE_MERGE_AND_DETACH, true); 1761 } 1762 } 1763 mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 1764 } 1765 1766 boolean isConnected() { 1767 IState currentState = getCurrentState(); 1768 return (currentState == mConnected || currentState == mAudioOn); 1769 } 1770 1771 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1772 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 1773 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 1774 int connectionState; 1775 synchronized (this) { 1776 for (BluetoothDevice device : bondedDevices) { 1777 ParcelUuid[] featureUuids = device.getUuids(); 1778 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.Handsfree_AG)) { 1779 continue; 1780 } 1781 connectionState = getConnectionState(device); 1782 for (int state : states) { 1783 if (connectionState == state) { 1784 deviceList.add(device); 1785 } 1786 } 1787 } 1788 } 1789 return deviceList; 1790 } 1791 1792 boolean okToConnect(BluetoothDevice device) { 1793 int priority = mService.getPriority(device); 1794 boolean ret = false; 1795 // check priority and accept or reject the connection. if priority is 1796 // undefined 1797 // it is likely that our SDP has not completed and peer is initiating 1798 // the 1799 // connection. Allow this connection, provided the device is bonded 1800 if ((BluetoothProfile.PRIORITY_OFF < priority) || ( 1801 (BluetoothProfile.PRIORITY_UNDEFINED == priority) && (device.getBondState() 1802 != BluetoothDevice.BOND_NONE))) { 1803 ret = true; 1804 } 1805 return ret; 1806 } 1807 1808 boolean isAudioOn() { 1809 return (getCurrentState() == mAudioOn); 1810 } 1811 1812 synchronized int getAudioState(BluetoothDevice device) { 1813 if (mCurrentDevice == null || !mCurrentDevice.equals(device)) { 1814 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1815 } 1816 return mAudioState; 1817 } 1818 1819 List<BluetoothDevice> getConnectedDevices() { 1820 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 1821 synchronized (this) { 1822 if (isConnected()) { 1823 devices.add(mCurrentDevice); 1824 } 1825 } 1826 return devices; 1827 } 1828 1829 private byte[] getByteAddress(BluetoothDevice device) { 1830 return Utils.getBytesFromAddress(device.getAddress()); 1831 } 1832 1833 public List<BluetoothHeadsetClientCall> getCurrentCalls() { 1834 return new ArrayList<BluetoothHeadsetClientCall>(mCalls.values()); 1835 } 1836 1837 public Bundle getCurrentAgEvents() { 1838 Bundle b = new Bundle(); 1839 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, mIndicatorNetworkState); 1840 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH, mIndicatorNetworkSignal); 1841 b.putInt(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, mIndicatorNetworkType); 1842 b.putInt(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, mIndicatorBatteryLevel); 1843 b.putString(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, mOperatorName); 1844 b.putString(BluetoothHeadsetClient.EXTRA_SUBSCRIBER_INFO, mSubscriberInfo); 1845 return b; 1846 } 1847} 1848