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