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