BluetoothPhoneServiceImpl.java revision 240656f19c078b4f703d0502ce29114de0d45a50
1/* 2 * Copyright (C) 2014 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 17package com.android.server.telecom; 18 19import android.bluetooth.BluetoothAdapter; 20import android.bluetooth.BluetoothHeadset; 21import android.bluetooth.BluetoothProfile; 22import android.bluetooth.IBluetoothHeadsetPhone; 23import android.content.BroadcastReceiver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.net.Uri; 28import android.os.Binder; 29import android.os.IBinder; 30import android.os.RemoteException; 31import android.telecom.Connection; 32import android.telecom.PhoneAccount; 33import android.telephony.PhoneNumberUtils; 34import android.telephony.TelephonyManager; 35import android.text.TextUtils; 36 37import com.android.internal.annotations.VisibleForTesting; 38import com.android.server.telecom.CallsManager.CallsManagerListener; 39 40import java.util.Collection; 41import java.util.HashMap; 42import java.util.List; 43import java.util.Map; 44 45/** 46 * Bluetooth headset manager for Telecom. This class shares the call state with the bluetooth device 47 * and accepts call-related commands to perform on behalf of the BT device. 48 */ 49public class BluetoothPhoneServiceImpl { 50 51 public interface BluetoothPhoneServiceImplFactory { 52 BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context, 53 TelecomSystem.SyncRoot lock, CallsManager callsManager, 54 PhoneAccountRegistrar phoneAccountRegistrar); 55 } 56 57 private static final String TAG = "BluetoothPhoneService"; 58 59 // match up with bthf_call_state_t of bt_hf.h 60 private static final int CALL_STATE_ACTIVE = 0; 61 private static final int CALL_STATE_HELD = 1; 62 private static final int CALL_STATE_DIALING = 2; 63 private static final int CALL_STATE_ALERTING = 3; 64 private static final int CALL_STATE_INCOMING = 4; 65 private static final int CALL_STATE_WAITING = 5; 66 private static final int CALL_STATE_IDLE = 6; 67 68 // match up with bthf_call_state_t of bt_hf.h 69 // Terminate all held or set UDUB("busy") to a waiting call 70 private static final int CHLD_TYPE_RELEASEHELD = 0; 71 // Terminate all active calls and accepts a waiting/held call 72 private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1; 73 // Hold all active calls and accepts a waiting/held call 74 private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2; 75 // Add all held calls to a conference 76 private static final int CHLD_TYPE_ADDHELDTOCONF = 3; 77 78 private int mNumActiveCalls = 0; 79 private int mNumHeldCalls = 0; 80 private int mBluetoothCallState = CALL_STATE_IDLE; 81 private String mRingingAddress = null; 82 private int mRingingAddressType = 0; 83 private Call mOldHeldCall = null; 84 85 /** 86 * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the 87 * bluetooth headset code uses to control call. 88 */ 89 @VisibleForTesting 90 public final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() { 91 @Override 92 public boolean answerCall() throws RemoteException { 93 synchronized (mLock) { 94 enforceModifyPermission(); 95 96 long token = Binder.clearCallingIdentity(); 97 try { 98 Log.i(TAG, "BT - answering call"); 99 Call call = mCallsManager.getRingingCall(); 100 if (call != null) { 101 mCallsManager.answerCall(call, call.getVideoState()); 102 return true; 103 } 104 return false; 105 } finally { 106 Binder.restoreCallingIdentity(token); 107 } 108 109 } 110 } 111 112 @Override 113 public boolean hangupCall() throws RemoteException { 114 synchronized (mLock) { 115 enforceModifyPermission(); 116 117 long token = Binder.clearCallingIdentity(); 118 try { 119 Log.i(TAG, "BT - hanging up call"); 120 Call call = mCallsManager.getForegroundCall(); 121 if (call != null) { 122 mCallsManager.disconnectCall(call); 123 return true; 124 } 125 return false; 126 } finally { 127 Binder.restoreCallingIdentity(token); 128 } 129 } 130 } 131 132 @Override 133 public boolean sendDtmf(int dtmf) throws RemoteException { 134 synchronized (mLock) { 135 enforceModifyPermission(); 136 137 long token = Binder.clearCallingIdentity(); 138 try { 139 Log.i(TAG, "BT - sendDtmf %c", Log.DEBUG ? dtmf : '.'); 140 Call call = mCallsManager.getForegroundCall(); 141 if (call != null) { 142 // TODO: Consider making this a queue instead of starting/stopping 143 // in quick succession. 144 mCallsManager.playDtmfTone(call, (char) dtmf); 145 mCallsManager.stopDtmfTone(call); 146 return true; 147 } 148 return false; 149 } finally { 150 Binder.restoreCallingIdentity(token); 151 } 152 } 153 } 154 155 @Override 156 public String getNetworkOperator() throws RemoteException { 157 synchronized (mLock) { 158 enforceModifyPermission(); 159 160 long token = Binder.clearCallingIdentity(); 161 try { 162 Log.i(TAG, "getNetworkOperator"); 163 PhoneAccount account = getBestPhoneAccount(); 164 if (account != null) { 165 return account.getLabel().toString(); 166 } else { 167 // Finally, just get the network name from telephony. 168 return TelephonyManager.from(mContext) 169 .getNetworkOperatorName(); 170 } 171 } finally { 172 Binder.restoreCallingIdentity(token); 173 } 174 } 175 } 176 177 @Override 178 public String getSubscriberNumber() throws RemoteException { 179 synchronized (mLock) { 180 enforceModifyPermission(); 181 182 long token = Binder.clearCallingIdentity(); 183 try { 184 Log.i(TAG, "getSubscriberNumber"); 185 String address = null; 186 PhoneAccount account = getBestPhoneAccount(); 187 if (account != null) { 188 Uri addressUri = account.getAddress(); 189 if (addressUri != null) { 190 address = addressUri.getSchemeSpecificPart(); 191 } 192 } 193 if (TextUtils.isEmpty(address)) { 194 address = TelephonyManager.from(mContext).getLine1Number(); 195 if (address == null) address = ""; 196 } 197 return address; 198 } finally { 199 Binder.restoreCallingIdentity(token); 200 } 201 } 202 } 203 204 @Override 205 public boolean listCurrentCalls() throws RemoteException { 206 synchronized (mLock) { 207 enforceModifyPermission(); 208 209 long token = Binder.clearCallingIdentity(); 210 try { 211 // only log if it is after we recently updated the headset state or else it can 212 // clog the android log since this can be queried every second. 213 boolean logQuery = mHeadsetUpdatedRecently; 214 mHeadsetUpdatedRecently = false; 215 216 if (logQuery) { 217 Log.i(TAG, "listcurrentCalls"); 218 } 219 220 sendListOfCalls(logQuery); 221 return true; 222 } finally { 223 Binder.restoreCallingIdentity(token); 224 } 225 } 226 } 227 228 @Override 229 public boolean queryPhoneState() throws RemoteException { 230 synchronized (mLock) { 231 enforceModifyPermission(); 232 233 long token = Binder.clearCallingIdentity(); 234 try { 235 Log.i(TAG, "queryPhoneState"); 236 updateHeadsetWithCallState(true /* force */); 237 return true; 238 } finally { 239 Binder.restoreCallingIdentity(token); 240 } 241 } 242 } 243 244 @Override 245 public boolean processChld(int chld) throws RemoteException { 246 synchronized (mLock) { 247 enforceModifyPermission(); 248 249 long token = Binder.clearCallingIdentity(); 250 try { 251 Log.i(TAG, "processChld %d", chld); 252 return BluetoothPhoneServiceImpl.this.processChld(chld); 253 } finally { 254 Binder.restoreCallingIdentity(token); 255 } 256 } 257 } 258 259 @Override 260 public void updateBtHandsfreeAfterRadioTechnologyChange() throws RemoteException { 261 Log.d(TAG, "RAT change - deprecated"); 262 // deprecated 263 } 264 265 @Override 266 public void cdmaSetSecondCallState(boolean state) throws RemoteException { 267 Log.d(TAG, "cdma 1 - deprecated"); 268 // deprecated 269 } 270 271 @Override 272 public void cdmaSwapSecondCallState() throws RemoteException { 273 Log.d(TAG, "cdma 2 - deprecated"); 274 // deprecated 275 } 276 }; 277 278 /** 279 * Listens to call changes from the CallsManager and calls into methods to update the bluetooth 280 * headset with the new states. 281 */ 282 @VisibleForTesting 283 public CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() { 284 @Override 285 public void onCallAdded(Call call) { 286 updateHeadsetWithCallState(false /* force */); 287 } 288 289 @Override 290 public void onCallRemoved(Call call) { 291 mClccIndexMap.remove(call); 292 updateHeadsetWithCallState(false /* force */); 293 } 294 295 @Override 296 public void onCallStateChanged(Call call, int oldState, int newState) { 297 // If a call is being put on hold because of a new connecting call, ignore the 298 // CONNECTING since the BT state update needs to send out the numHeld = 1 + dialing 299 // state atomically. 300 // When the call later transitions to DIALING/DISCONNECTED we will then send out the 301 // aggregated update. 302 if (oldState == CallState.ACTIVE && newState == CallState.ON_HOLD) { 303 for (Call otherCall : mCallsManager.getCalls()) { 304 if (otherCall.getState() == CallState.CONNECTING) { 305 return; 306 } 307 } 308 } 309 310 // To have an active call and another dialing at the same time is an invalid BT 311 // state. We can assume that the active call will be automatically held which will 312 // send another update at which point we will be in the right state. 313 if (mCallsManager.getActiveCall() != null 314 && oldState == CallState.CONNECTING && newState == CallState.DIALING) { 315 return; 316 } 317 updateHeadsetWithCallState(false /* force */); 318 } 319 320 @Override 321 public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) { 322 // The BluetoothPhoneService does not need to respond to changes in foreground calls, 323 // which are always accompanied by call state changes anyway. 324 } 325 326 @Override 327 public void onIsConferencedChanged(Call call) { 328 /* 329 * Filter certain onIsConferencedChanged callbacks. Unfortunately this needs to be done 330 * because conference change events are not atomic and multiple callbacks get fired 331 * when two calls are conferenced together. This confuses updateHeadsetWithCallState 332 * if it runs in the middle of two calls being conferenced and can cause spurious and 333 * incorrect headset state updates. One of the scenarios is described below for CDMA 334 * conference calls. 335 * 336 * 1) Call 1 and Call 2 are being merged into conference Call 3. 337 * 2) Call 1 has its parent set to Call 3, but Call 2 does not have a parent yet. 338 * 3) updateHeadsetWithCallState now thinks that there are two active calls (Call 2 and 339 * Call 3) when there is actually only one active call (Call 3). 340 */ 341 if (call.getParentCall() != null) { 342 // If this call is newly conferenced, ignore the callback. We only care about the 343 // one sent for the parent conference call. 344 Log.d(this, "Ignoring onIsConferenceChanged from child call with new parent"); 345 return; 346 } 347 if (call.getChildCalls().size() == 1) { 348 // If this is a parent call with only one child, ignore the callback as well since 349 // the minimum number of child calls to start a conference call is 2. We expect 350 // this to be called again when the parent call has another child call added. 351 Log.d(this, "Ignoring onIsConferenceChanged from parent with only one child call"); 352 return; 353 } 354 updateHeadsetWithCallState(false /* force */); 355 } 356 }; 357 358 /** 359 * Listens to connections and disconnections of bluetooth headsets. We need to save the current 360 * bluetooth headset so that we know where to send call updates. 361 */ 362 @VisibleForTesting 363 public BluetoothProfile.ServiceListener mProfileListener = 364 new BluetoothProfile.ServiceListener() { 365 @Override 366 public void onServiceConnected(int profile, BluetoothProfile proxy) { 367 synchronized (mLock) { 368 setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy)); 369 } 370 } 371 372 @Override 373 public void onServiceDisconnected(int profile) { 374 synchronized (mLock) { 375 mBluetoothHeadset = null; 376 } 377 } 378 }; 379 380 /** 381 * Receives events for global state changes of the bluetooth adapter. 382 */ 383 private final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() { 384 @Override 385 public void onReceive(Context context, Intent intent) { 386 synchronized (mLock) { 387 int state = intent 388 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 389 Log.d(TAG, "Bluetooth Adapter state: %d", state); 390 if (state == BluetoothAdapter.STATE_ON) { 391 try { 392 mBinder.queryPhoneState(); 393 } catch (RemoteException e) { 394 // Remote exception not expected 395 } 396 } 397 } 398 } 399 }; 400 401 private BluetoothAdapter mBluetoothAdapter; 402 private BluetoothHeadsetProxy mBluetoothHeadset; 403 404 // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls). 405 private Map<Call, Integer> mClccIndexMap = new HashMap<>(); 406 407 private boolean mHeadsetUpdatedRecently = false; 408 409 private final Context mContext; 410 private final TelecomSystem.SyncRoot mLock; 411 private final CallsManager mCallsManager; 412 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 413 414 public IBinder getBinder() { 415 return mBinder; 416 } 417 418 public BluetoothPhoneServiceImpl( 419 Context context, 420 TelecomSystem.SyncRoot lock, 421 CallsManager callsManager, 422 PhoneAccountRegistrar phoneAccountRegistrar) { 423 Log.d(this, "onCreate"); 424 425 mContext = context; 426 mLock = lock; 427 mCallsManager = callsManager; 428 mPhoneAccountRegistrar = phoneAccountRegistrar; 429 430 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 431 if (mBluetoothAdapter == null) { 432 Log.d(this, "BluetoothPhoneService shutting down, no BT Adapter found."); 433 return; 434 } 435 mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET); 436 437 IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); 438 context.registerReceiver(mBluetoothAdapterReceiver, intentFilter); 439 440 mCallsManager.addListener(mCallsManagerListener); 441 updateHeadsetWithCallState(false /* force */); 442 } 443 444 @VisibleForTesting 445 public void setBluetoothHeadset(BluetoothHeadsetProxy bluetoothHeadset) { 446 mBluetoothHeadset = bluetoothHeadset; 447 } 448 449 private boolean processChld(int chld) { 450 Call activeCall = mCallsManager.getActiveCall(); 451 Call ringingCall = mCallsManager.getRingingCall(); 452 Call heldCall = mCallsManager.getHeldCall(); 453 454 // TODO: Keeping as Log.i for now. Move to Log.d after L release if BT proves stable. 455 Log.i(TAG, "Active: %s\nRinging: %s\nHeld: %s", activeCall, ringingCall, heldCall); 456 457 if (chld == CHLD_TYPE_RELEASEHELD) { 458 if (ringingCall != null) { 459 mCallsManager.rejectCall(ringingCall, false, null); 460 return true; 461 } else if (heldCall != null) { 462 mCallsManager.disconnectCall(heldCall); 463 return true; 464 } 465 } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) { 466 if (activeCall != null) { 467 mCallsManager.disconnectCall(activeCall); 468 if (ringingCall != null) { 469 mCallsManager.answerCall(ringingCall, ringingCall.getVideoState()); 470 } else if (heldCall != null) { 471 mCallsManager.unholdCall(heldCall); 472 } 473 return true; 474 } 475 } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) { 476 if (activeCall != null && activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) { 477 activeCall.swapConference(); 478 Log.i(TAG, "CDMA calls in conference swapped, updating headset"); 479 updateHeadsetWithCallState(true /* force */); 480 return true; 481 } else if (ringingCall != null) { 482 mCallsManager.answerCall(ringingCall, ringingCall.getVideoState()); 483 return true; 484 } else if (heldCall != null) { 485 // CallsManager will hold any active calls when unhold() is called on a 486 // currently-held call. 487 mCallsManager.unholdCall(heldCall); 488 return true; 489 } else if (activeCall != null && activeCall.can(Connection.CAPABILITY_HOLD)) { 490 mCallsManager.holdCall(activeCall); 491 return true; 492 } 493 } else if (chld == CHLD_TYPE_ADDHELDTOCONF) { 494 if (activeCall != null) { 495 if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) { 496 activeCall.mergeConference(); 497 return true; 498 } else { 499 List<Call> conferenceable = activeCall.getConferenceableCalls(); 500 if (!conferenceable.isEmpty()) { 501 mCallsManager.conference(activeCall, conferenceable.get(0)); 502 return true; 503 } 504 } 505 } 506 } 507 return false; 508 } 509 510 private void enforceModifyPermission() { 511 mContext.enforceCallingOrSelfPermission( 512 android.Manifest.permission.MODIFY_PHONE_STATE, null); 513 } 514 515 private void sendListOfCalls(boolean shouldLog) { 516 Collection<Call> mCalls = mCallsManager.getCalls(); 517 for (Call call : mCalls) { 518 // We don't send the parent conference call to the bluetooth device. 519 // We do, however want to send conferences that have no children to the bluetooth 520 // device (e.g. IMS Conference). 521 if (!call.isConference() || 522 (call.isConference() && call 523 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN))) { 524 sendClccForCall(call, shouldLog); 525 } 526 } 527 sendClccEndMarker(); 528 } 529 530 /** 531 * Sends a single clcc (C* List Current Calls) event for the specified call. 532 */ 533 private void sendClccForCall(Call call, boolean shouldLog) { 534 boolean isForeground = mCallsManager.getForegroundCall() == call; 535 int state = convertCallState(call.getState(), isForeground); 536 boolean isPartOfConference = false; 537 boolean isConferenceWithNoChildren = call.isConference() && call 538 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN); 539 540 if (state == CALL_STATE_IDLE) { 541 return; 542 } 543 544 Call conferenceCall = call.getParentCall(); 545 if (conferenceCall != null) { 546 isPartOfConference = true; 547 548 // Run some alternative states for Conference-level merge/swap support. 549 // Basically, if call supports swapping or merging at the conference-level, then we need 550 // to expose the calls as having distinct states (ACTIVE vs CAPABILITY_HOLD) or the 551 // functionality won't show up on the bluetooth device. 552 553 // Before doing any special logic, ensure that we are dealing with an ACTIVE call and 554 // that the conference itself has a notion of the current "active" child call. 555 Call activeChild = conferenceCall.getConferenceLevelActiveCall(); 556 if (state == CALL_STATE_ACTIVE && activeChild != null) { 557 // Reevaluate state if we can MERGE or if we can SWAP without previously having 558 // MERGED. 559 boolean shouldReevaluateState = 560 conferenceCall.can(Connection.CAPABILITY_MERGE_CONFERENCE) || 561 (conferenceCall.can(Connection.CAPABILITY_SWAP_CONFERENCE) && 562 !conferenceCall.wasConferencePreviouslyMerged()); 563 564 if (shouldReevaluateState) { 565 isPartOfConference = false; 566 if (call == activeChild) { 567 state = CALL_STATE_ACTIVE; 568 } else { 569 // At this point we know there is an "active" child and we know that it is 570 // not this call, so set it to HELD instead. 571 state = CALL_STATE_HELD; 572 } 573 } 574 } 575 } else if (isConferenceWithNoChildren) { 576 // Handle the special case of an IMS conference call without conference event package 577 // support. The call will be marked as a conference, but the conference will not have 578 // child calls where conference event packages are not used by the carrier. 579 isPartOfConference = true; 580 } 581 582 int index = getIndexForCall(call); 583 int direction = call.isIncoming() ? 1 : 0; 584 final Uri addressUri; 585 if (call.getGatewayInfo() != null) { 586 addressUri = call.getGatewayInfo().getOriginalAddress(); 587 } else { 588 addressUri = call.getHandle(); 589 } 590 String address = addressUri == null ? null : addressUri.getSchemeSpecificPart(); 591 int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address); 592 593 if (shouldLog) { 594 Log.i(this, "sending clcc for call %d, %d, %d, %b, %s, %d", 595 index, direction, state, isPartOfConference, Log.piiHandle(address), 596 addressType); 597 } 598 599 if (mBluetoothHeadset != null) { 600 mBluetoothHeadset.clccResponse( 601 index, direction, state, 0, isPartOfConference, address, addressType); 602 } 603 } 604 605 private void sendClccEndMarker() { 606 // End marker is recognized with an index value of 0. All other parameters are ignored. 607 if (mBluetoothHeadset != null) { 608 mBluetoothHeadset.clccResponse(0 /* index */, 0, 0, 0, false, null, 0); 609 } 610 } 611 612 /** 613 * Returns the caches index for the specified call. If no such index exists, then an index is 614 * given (smallest number starting from 1 that isn't already taken). 615 */ 616 private int getIndexForCall(Call call) { 617 if (mClccIndexMap.containsKey(call)) { 618 return mClccIndexMap.get(call); 619 } 620 621 int i = 1; // Indexes for bluetooth clcc are 1-based. 622 while (mClccIndexMap.containsValue(i)) { 623 i++; 624 } 625 626 // NOTE: Indexes are removed in {@link #onCallRemoved}. 627 mClccIndexMap.put(call, i); 628 return i; 629 } 630 631 /** 632 * Sends an update of the current call state to the current Headset. 633 * 634 * @param force {@code true} if the headset state should be sent regardless if no changes to the 635 * state have occurred, {@code false} if the state should only be sent if the state has 636 * changed. 637 */ 638 private void updateHeadsetWithCallState(boolean force) { 639 Call activeCall = mCallsManager.getActiveCall(); 640 Call ringingCall = mCallsManager.getRingingCall(); 641 Call heldCall = mCallsManager.getHeldCall(); 642 643 int bluetoothCallState = getBluetoothCallStateForUpdate(); 644 645 String ringingAddress = null; 646 int ringingAddressType = 128; 647 if (ringingCall != null && ringingCall.getHandle() != null) { 648 ringingAddress = ringingCall.getHandle().getSchemeSpecificPart(); 649 if (ringingAddress != null) { 650 ringingAddressType = PhoneNumberUtils.toaFromString(ringingAddress); 651 } 652 } 653 if (ringingAddress == null) { 654 ringingAddress = ""; 655 } 656 657 int numActiveCalls = activeCall == null ? 0 : 1; 658 int numHeldCalls = mCallsManager.getNumHeldCalls(); 659 // Intermediate state for GSM calls which are in the process of being swapped. 660 // TODO: Should we be hardcoding this value to 2 or should we check if all top level calls 661 // are held? 662 boolean callsPendingSwitch = (numHeldCalls == 2); 663 664 // For conference calls which support swapping the active call within the conference 665 // (namely CDMA calls) we need to expose that as a held call in order for the BT device 666 // to show "swap" and "merge" functionality. 667 boolean ignoreHeldCallChange = false; 668 if (activeCall != null && activeCall.isConference() && 669 !activeCall.can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) { 670 if (activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) { 671 // Indicate that BT device should show SWAP command by indicating that there is a 672 // call on hold, but only if the conference wasn't previously merged. 673 numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1; 674 } else if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) { 675 numHeldCalls = 1; // Merge is available, so expose via numHeldCalls. 676 } 677 678 for (Call childCall : activeCall.getChildCalls()) { 679 // Held call has changed due to it being combined into a CDMA conference. Keep 680 // track of this and ignore any future update since it doesn't really count as 681 // a call change. 682 if (mOldHeldCall == childCall) { 683 ignoreHeldCallChange = true; 684 break; 685 } 686 } 687 } 688 689 if (mBluetoothHeadset != null && 690 (force || 691 (!callsPendingSwitch && 692 (numActiveCalls != mNumActiveCalls || 693 numHeldCalls != mNumHeldCalls || 694 bluetoothCallState != mBluetoothCallState || 695 !TextUtils.equals(ringingAddress, mRingingAddress) || 696 ringingAddressType != mRingingAddressType || 697 (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) { 698 699 // If the call is transitioning into the alerting state, send DIALING first. 700 // Some devices expect to see a DIALING state prior to seeing an ALERTING state 701 // so we need to send it first. 702 boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState && 703 bluetoothCallState == CALL_STATE_ALERTING; 704 705 mOldHeldCall = heldCall; 706 mNumActiveCalls = numActiveCalls; 707 mNumHeldCalls = numHeldCalls; 708 mBluetoothCallState = bluetoothCallState; 709 mRingingAddress = ringingAddress; 710 mRingingAddressType = ringingAddressType; 711 712 if (sendDialingFirst) { 713 // Log in full to make logs easier to debug. 714 Log.i(TAG, "updateHeadsetWithCallState " + 715 "numActive %s, " + 716 "numHeld %s, " + 717 "callState %s, " + 718 "ringing number %s, " + 719 "ringing type %s", 720 mNumActiveCalls, 721 mNumHeldCalls, 722 CALL_STATE_DIALING, 723 Log.pii(mRingingAddress), 724 mRingingAddressType); 725 mBluetoothHeadset.phoneStateChanged( 726 mNumActiveCalls, 727 mNumHeldCalls, 728 CALL_STATE_DIALING, 729 mRingingAddress, 730 mRingingAddressType); 731 } 732 733 Log.i(TAG, "updateHeadsetWithCallState " + 734 "numActive %s, " + 735 "numHeld %s, " + 736 "callState %s, " + 737 "ringing number %s, " + 738 "ringing type %s", 739 mNumActiveCalls, 740 mNumHeldCalls, 741 mBluetoothCallState, 742 Log.pii(mRingingAddress), 743 mRingingAddressType); 744 745 mBluetoothHeadset.phoneStateChanged( 746 mNumActiveCalls, 747 mNumHeldCalls, 748 mBluetoothCallState, 749 mRingingAddress, 750 mRingingAddressType); 751 752 mHeadsetUpdatedRecently = true; 753 } 754 } 755 756 private int getBluetoothCallStateForUpdate() { 757 CallsManager callsManager = mCallsManager; 758 Call ringingCall = mCallsManager.getRingingCall(); 759 Call dialingCall = mCallsManager.getOutgoingCall(); 760 761 // 762 // !! WARNING !! 763 // You will note that CALL_STATE_WAITING, CALL_STATE_HELD, and CALL_STATE_ACTIVE are not 764 // used in this version of the call state mappings. This is on purpose. 765 // phone_state_change() in btif_hf.c is not written to handle these states. Only with the 766 // listCalls*() method are WAITING and ACTIVE used. 767 // Using the unsupported states here caused problems with inconsistent state in some 768 // bluetooth devices (like not getting out of ringing state after answering a call). 769 // 770 int bluetoothCallState = CALL_STATE_IDLE; 771 if (ringingCall != null) { 772 bluetoothCallState = CALL_STATE_INCOMING; 773 } else if (dialingCall != null) { 774 bluetoothCallState = CALL_STATE_ALERTING; 775 } 776 return bluetoothCallState; 777 } 778 779 private int convertCallState(int callState, boolean isForegroundCall) { 780 switch (callState) { 781 case CallState.NEW: 782 case CallState.ABORTED: 783 case CallState.DISCONNECTED: 784 return CALL_STATE_IDLE; 785 786 case CallState.ACTIVE: 787 return CALL_STATE_ACTIVE; 788 789 case CallState.CONNECTING: 790 case CallState.SELECT_PHONE_ACCOUNT: 791 case CallState.DIALING: 792 // Yes, this is correctly returning ALERTING. 793 // "Dialing" for BT means that we have sent information to the service provider 794 // to place the call but there is no confirmation that the call is going through. 795 // When there finally is confirmation, the ringback is played which is referred to 796 // as an "alert" tone, thus, ALERTING. 797 // TODO: We should consider using the ALERTING terms in Telecom because that 798 // seems to be more industry-standard. 799 return CALL_STATE_ALERTING; 800 801 case CallState.ON_HOLD: 802 return CALL_STATE_HELD; 803 804 case CallState.RINGING: 805 if (isForegroundCall) { 806 return CALL_STATE_INCOMING; 807 } else { 808 return CALL_STATE_WAITING; 809 } 810 } 811 return CALL_STATE_IDLE; 812 } 813 814 /** 815 * Returns the best phone account to use for the given state of all calls. 816 * First, tries to return the phone account for the foreground call, second the default 817 * phone account for PhoneAccount.SCHEME_TEL. 818 */ 819 private PhoneAccount getBestPhoneAccount() { 820 if (mPhoneAccountRegistrar == null) { 821 return null; 822 } 823 824 Call call = mCallsManager.getForegroundCall(); 825 826 PhoneAccount account = null; 827 if (call != null) { 828 // First try to get the network name of the foreground call. 829 account = mPhoneAccountRegistrar.getPhoneAccountOfCurrentUser( 830 call.getTargetPhoneAccount()); 831 } 832 833 if (account == null) { 834 // Second, Try to get the label for the default Phone Account. 835 account = mPhoneAccountRegistrar.getPhoneAccountUnchecked( 836 mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser( 837 PhoneAccount.SCHEME_TEL)); 838 } 839 return account; 840 } 841} 842