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