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