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