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