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