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