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