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