blob: f29a5e3f1cd6b1d9ec6f4a2339a226e7cb5fa2ca [file] [log] [blame]
Santos Cordon68d1a6b2014-09-19 12:25:58 -07001/*
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
Santos Cordon68d1a6b2014-09-19 12:25:58 -070019import 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;
Santos Cordonebf2d0f2015-05-15 10:28:29 -070028import android.os.Binder;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070029import android.os.IBinder;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070030import android.os.RemoteException;
Ihab Awad07bc5ee2014-11-12 13:42:52 -080031import android.telecom.Connection;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070032import android.telecom.PhoneAccount;
Hall Liu3cab92a2016-07-19 14:00:05 -070033import android.telecom.VideoProfile;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070034import android.telephony.PhoneNumberUtils;
35import android.telephony.TelephonyManager;
36import android.text.TextUtils;
37
Brad Ebinger53855132015-10-30 10:58:19 -070038import com.android.internal.annotations.VisibleForTesting;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070039import com.android.server.telecom.CallsManager.CallsManagerListener;
40
Santos Cordon33fff492014-09-25 14:57:01 -070041import java.util.Collection;
42import java.util.HashMap;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070043import java.util.List;
Santos Cordon33fff492014-09-25 14:57:01 -070044import java.util.Map;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070045
46/**
47 * Bluetooth headset manager for Telecom. This class shares the call state with the bluetooth device
48 * and accepts call-related commands to perform on behalf of the BT device.
49 */
Hall Liub3979ee2015-11-11 16:21:25 -080050public class BluetoothPhoneServiceImpl {
51
52 public interface BluetoothPhoneServiceImplFactory {
53 BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
54 TelecomSystem.SyncRoot lock, CallsManager callsManager,
55 PhoneAccountRegistrar phoneAccountRegistrar);
56 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -070057
58 private static final String TAG = "BluetoothPhoneService";
59
Santos Cordon68d1a6b2014-09-19 12:25:58 -070060 // match up with bthf_call_state_t of bt_hf.h
61 private static final int CALL_STATE_ACTIVE = 0;
62 private static final int CALL_STATE_HELD = 1;
63 private static final int CALL_STATE_DIALING = 2;
64 private static final int CALL_STATE_ALERTING = 3;
65 private static final int CALL_STATE_INCOMING = 4;
66 private static final int CALL_STATE_WAITING = 5;
67 private static final int CALL_STATE_IDLE = 6;
68
69 // match up with bthf_call_state_t of bt_hf.h
70 // Terminate all held or set UDUB("busy") to a waiting call
71 private static final int CHLD_TYPE_RELEASEHELD = 0;
72 // Terminate all active calls and accepts a waiting/held call
73 private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
74 // Hold all active calls and accepts a waiting/held call
75 private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
76 // Add all held calls to a conference
77 private static final int CHLD_TYPE_ADDHELDTOCONF = 3;
78
Santos Cordona0b46122014-09-29 14:20:21 -070079 private int mNumActiveCalls = 0;
80 private int mNumHeldCalls = 0;
81 private int mBluetoothCallState = CALL_STATE_IDLE;
82 private String mRingingAddress = null;
83 private int mRingingAddressType = 0;
Santos Cordonc0ca76e2014-10-21 15:54:19 -070084 private Call mOldHeldCall = null;
Santos Cordona0b46122014-09-29 14:20:21 -070085
Santos Cordon68d1a6b2014-09-19 12:25:58 -070086 /**
87 * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
88 * bluetooth headset code uses to control call.
89 */
Brad Ebinger53855132015-10-30 10:58:19 -070090 @VisibleForTesting
91 public final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -070092 @Override
93 public boolean answerCall() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -070094 synchronized (mLock) {
95 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -080096 Log.startSession("BPSI.aC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -070097 long token = Binder.clearCallingIdentity();
98 try {
99 Log.i(TAG, "BT - answering call");
100 Call call = mCallsManager.getRingingCall();
101 if (call != null) {
Hall Liu3cab92a2016-07-19 14:00:05 -0700102 mCallsManager.answerCall(call, VideoProfile.STATE_AUDIO_ONLY);
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700103 return true;
104 }
105 return false;
106 } finally {
107 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800108 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700109 }
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700110
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700111 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700112 }
113
114 @Override
115 public boolean hangupCall() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700116 synchronized (mLock) {
117 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800118 Log.startSession("BPSI.hC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700119 long token = Binder.clearCallingIdentity();
120 try {
121 Log.i(TAG, "BT - hanging up call");
122 Call call = mCallsManager.getForegroundCall();
123 if (call != null) {
124 mCallsManager.disconnectCall(call);
125 return true;
126 }
127 return false;
128 } finally {
129 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800130 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700131 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700132 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700133 }
134
135 @Override
136 public boolean sendDtmf(int dtmf) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700137 synchronized (mLock) {
138 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800139 Log.startSession("BPSI.sD");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700140 long token = Binder.clearCallingIdentity();
141 try {
142 Log.i(TAG, "BT - sendDtmf %c", Log.DEBUG ? dtmf : '.');
143 Call call = mCallsManager.getForegroundCall();
144 if (call != null) {
145 // TODO: Consider making this a queue instead of starting/stopping
146 // in quick succession.
147 mCallsManager.playDtmfTone(call, (char) dtmf);
148 mCallsManager.stopDtmfTone(call);
149 return true;
150 }
151 return false;
152 } finally {
153 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800154 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700155 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700156 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700157 }
158
159 @Override
160 public String getNetworkOperator() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700161 synchronized (mLock) {
162 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800163 Log.startSession("BPSI.gNO");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700164 long token = Binder.clearCallingIdentity();
165 try {
166 Log.i(TAG, "getNetworkOperator");
167 PhoneAccount account = getBestPhoneAccount();
Santos Cordon22403a72016-04-12 16:51:55 -0700168 if (account != null && account.getLabel() != null) {
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700169 return account.getLabel().toString();
170 } else {
171 // Finally, just get the network name from telephony.
172 return TelephonyManager.from(mContext)
173 .getNetworkOperatorName();
174 }
175 } finally {
176 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800177 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700178 }
179 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700180 }
181
182 @Override
183 public String getSubscriberNumber() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700184 synchronized (mLock) {
185 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800186 Log.startSession("BPSI.gSN");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700187 long token = Binder.clearCallingIdentity();
188 try {
189 Log.i(TAG, "getSubscriberNumber");
190 String address = null;
191 PhoneAccount account = getBestPhoneAccount();
192 if (account != null) {
193 Uri addressUri = account.getAddress();
194 if (addressUri != null) {
195 address = addressUri.getSchemeSpecificPart();
196 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700197 }
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700198 if (TextUtils.isEmpty(address)) {
199 address = TelephonyManager.from(mContext).getLine1Number();
Andre Eisenbach31092622015-06-04 18:56:42 -0700200 if (address == null) address = "";
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700201 }
202 return address;
203 } finally {
204 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800205 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700206 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700207 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700208 }
209
210 @Override
211 public boolean listCurrentCalls() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700212 synchronized (mLock) {
213 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800214 Log.startSession("BPSI.lCC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700215 long token = Binder.clearCallingIdentity();
216 try {
217 // only log if it is after we recently updated the headset state or else it can
218 // clog the android log since this can be queried every second.
219 boolean logQuery = mHeadsetUpdatedRecently;
220 mHeadsetUpdatedRecently = false;
221
222 if (logQuery) {
223 Log.i(TAG, "listcurrentCalls");
224 }
225
226 sendListOfCalls(logQuery);
227 return true;
228 } finally {
229 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800230 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700231 }
Santos Cordon88a4a602014-09-29 19:32:21 -0700232 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700233 }
234
235 @Override
236 public boolean queryPhoneState() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700237 synchronized (mLock) {
238 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800239 Log.startSession("BPSI.qPS");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700240 long token = Binder.clearCallingIdentity();
241 try {
242 Log.i(TAG, "queryPhoneState");
243 updateHeadsetWithCallState(true /* force */);
244 return true;
245 } finally {
246 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800247 Log.endSession();
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700248 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700249 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700250 }
251
252 @Override
253 public boolean processChld(int chld) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700254 synchronized (mLock) {
255 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800256 Log.startSession("BPSI.pC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700257 long token = Binder.clearCallingIdentity();
258 try {
259 Log.i(TAG, "processChld %d", chld);
260 return BluetoothPhoneServiceImpl.this.processChld(chld);
261 } finally {
262 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800263 Log.endSession();
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700264 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700265 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700266 }
267
268 @Override
269 public void updateBtHandsfreeAfterRadioTechnologyChange() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700270 Log.d(TAG, "RAT change - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700271 // deprecated
272 }
273
274 @Override
275 public void cdmaSetSecondCallState(boolean state) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700276 Log.d(TAG, "cdma 1 - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700277 // deprecated
278 }
279
280 @Override
281 public void cdmaSwapSecondCallState() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700282 Log.d(TAG, "cdma 2 - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700283 // deprecated
284 }
285 };
286
287 /**
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700288 * Listens to call changes from the CallsManager and calls into methods to update the bluetooth
289 * headset with the new states.
290 */
Brad Ebinger53855132015-10-30 10:58:19 -0700291 @VisibleForTesting
292 public CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700293 @Override
294 public void onCallAdded(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700295 if (call.isExternalCall()) {
296 return;
297 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700298 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700299 }
300
301 @Override
302 public void onCallRemoved(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700303 if (call.isExternalCall()) {
304 return;
305 }
Santos Cordon33fff492014-09-25 14:57:01 -0700306 mClccIndexMap.remove(call);
Tyler Gunncd685e52014-10-10 11:48:25 -0700307 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700308 }
309
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700310 /**
311 * Where a call which was external becomes a regular call, or a regular call becomes
312 * external, treat as an add or remove, respectively.
313 *
314 * @param call The call.
315 * @param isExternalCall {@code True} if the call became external, {@code false} otherwise.
316 */
317 @Override
318 public void onExternalCallChanged(Call call, boolean isExternalCall) {
319 if (isExternalCall) {
320 onCallRemoved(call);
321 } else {
322 onCallAdded(call);
323 }
324 }
325
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700326 @Override
327 public void onCallStateChanged(Call call, int oldState, int newState) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700328 if (call.isExternalCall()) {
329 return;
330 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700331 // If a call is being put on hold because of a new connecting call, ignore the
332 // CONNECTING since the BT state update needs to send out the numHeld = 1 + dialing
333 // state atomically.
334 // When the call later transitions to DIALING/DISCONNECTED we will then send out the
335 // aggregated update.
336 if (oldState == CallState.ACTIVE && newState == CallState.ON_HOLD) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800337 for (Call otherCall : mCallsManager.getCalls()) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700338 if (otherCall.getState() == CallState.CONNECTING) {
339 return;
340 }
341 }
342 }
343
344 // To have an active call and another dialing at the same time is an invalid BT
345 // state. We can assume that the active call will be automatically held which will
346 // send another update at which point we will be in the right state.
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800347 if (mCallsManager.getActiveCall() != null
Tyler Gunn1e37be52016-07-11 08:54:23 -0700348 && oldState == CallState.CONNECTING &&
349 (newState == CallState.DIALING || newState == CallState.PULLING)) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700350 return;
351 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700352 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700353 }
354
355 @Override
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700356 public void onIsConferencedChanged(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700357 if (call.isExternalCall()) {
358 return;
359 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700360 /*
361 * Filter certain onIsConferencedChanged callbacks. Unfortunately this needs to be done
362 * because conference change events are not atomic and multiple callbacks get fired
363 * when two calls are conferenced together. This confuses updateHeadsetWithCallState
364 * if it runs in the middle of two calls being conferenced and can cause spurious and
365 * incorrect headset state updates. One of the scenarios is described below for CDMA
366 * conference calls.
367 *
368 * 1) Call 1 and Call 2 are being merged into conference Call 3.
369 * 2) Call 1 has its parent set to Call 3, but Call 2 does not have a parent yet.
370 * 3) updateHeadsetWithCallState now thinks that there are two active calls (Call 2 and
371 * Call 3) when there is actually only one active call (Call 3).
372 */
373 if (call.getParentCall() != null) {
374 // If this call is newly conferenced, ignore the callback. We only care about the
375 // one sent for the parent conference call.
376 Log.d(this, "Ignoring onIsConferenceChanged from child call with new parent");
377 return;
378 }
379 if (call.getChildCalls().size() == 1) {
380 // If this is a parent call with only one child, ignore the callback as well since
381 // the minimum number of child calls to start a conference call is 2. We expect
382 // this to be called again when the parent call has another child call added.
383 Log.d(this, "Ignoring onIsConferenceChanged from parent with only one child call");
384 return;
385 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700386 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700387 }
388 };
389
390 /**
391 * Listens to connections and disconnections of bluetooth headsets. We need to save the current
392 * bluetooth headset so that we know where to send call updates.
393 */
Brad Ebinger53855132015-10-30 10:58:19 -0700394 @VisibleForTesting
395 public BluetoothProfile.ServiceListener mProfileListener =
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700396 new BluetoothProfile.ServiceListener() {
Brad Ebinger53855132015-10-30 10:58:19 -0700397 @Override
398 public void onServiceConnected(int profile, BluetoothProfile proxy) {
399 synchronized (mLock) {
400 setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy));
401 }
402 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700403
Brad Ebinger53855132015-10-30 10:58:19 -0700404 @Override
405 public void onServiceDisconnected(int profile) {
406 synchronized (mLock) {
407 mBluetoothHeadset = null;
408 }
409 }
410 };
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700411
412 /**
413 * Receives events for global state changes of the bluetooth adapter.
414 */
Brad Ebinger71286022015-12-09 17:13:27 -0800415 @VisibleForTesting
416 public final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700417 @Override
418 public void onReceive(Context context, Intent intent) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700419 synchronized (mLock) {
420 int state = intent
421 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
422 Log.d(TAG, "Bluetooth Adapter state: %d", state);
423 if (state == BluetoothAdapter.STATE_ON) {
424 try {
425 mBinder.queryPhoneState();
426 } catch (RemoteException e) {
427 // Remote exception not expected
428 }
429 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700430 }
431 }
432 };
433
Hall Liu7948f5b2016-03-15 17:39:28 -0700434 private BluetoothAdapterProxy mBluetoothAdapter;
Brad Ebinger53855132015-10-30 10:58:19 -0700435 private BluetoothHeadsetProxy mBluetoothHeadset;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700436
Santos Cordon33fff492014-09-25 14:57:01 -0700437 // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
438 private Map<Call, Integer> mClccIndexMap = new HashMap<>();
439
Santos Cordon88a4a602014-09-29 19:32:21 -0700440 private boolean mHeadsetUpdatedRecently = false;
441
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700442 private final Context mContext;
443 private final TelecomSystem.SyncRoot mLock;
444 private final CallsManager mCallsManager;
445 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700446
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800447 public IBinder getBinder() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700448 return mBinder;
449 }
450
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800451 public BluetoothPhoneServiceImpl(
452 Context context,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700453 TelecomSystem.SyncRoot lock,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800454 CallsManager callsManager,
Hall Liu7948f5b2016-03-15 17:39:28 -0700455 BluetoothAdapterProxy bluetoothAdapter,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800456 PhoneAccountRegistrar phoneAccountRegistrar) {
457 Log.d(this, "onCreate");
458
459 mContext = context;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700460 mLock = lock;
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800461 mCallsManager = callsManager;
462 mPhoneAccountRegistrar = phoneAccountRegistrar;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700463
Hall Liu7948f5b2016-03-15 17:39:28 -0700464 mBluetoothAdapter = bluetoothAdapter;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700465 if (mBluetoothAdapter == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800466 Log.d(this, "BluetoothPhoneService shutting down, no BT Adapter found.");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700467 return;
468 }
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800469 mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700470
471 IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800472 context.registerReceiver(mBluetoothAdapterReceiver, intentFilter);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700473
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800474 mCallsManager.addListener(mCallsManagerListener);
Tyler Gunncd685e52014-10-10 11:48:25 -0700475 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700476 }
477
Brad Ebinger53855132015-10-30 10:58:19 -0700478 @VisibleForTesting
479 public void setBluetoothHeadset(BluetoothHeadsetProxy bluetoothHeadset) {
480 mBluetoothHeadset = bluetoothHeadset;
481 }
482
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700483 private boolean processChld(int chld) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700484 Call activeCall = mCallsManager.getActiveCall();
485 Call ringingCall = mCallsManager.getRingingCall();
486 Call heldCall = mCallsManager.getHeldCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700487
Santos Cordon66fe8822014-10-10 16:10:58 -0700488 // TODO: Keeping as Log.i for now. Move to Log.d after L release if BT proves stable.
489 Log.i(TAG, "Active: %s\nRinging: %s\nHeld: %s", activeCall, ringingCall, heldCall);
Santos Cordon88a4a602014-09-29 19:32:21 -0700490
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700491 if (chld == CHLD_TYPE_RELEASEHELD) {
492 if (ringingCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700493 mCallsManager.rejectCall(ringingCall, false, null);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700494 return true;
495 } else if (heldCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700496 mCallsManager.disconnectCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700497 return true;
498 }
499 } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) {
500 if (activeCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700501 mCallsManager.disconnectCall(activeCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700502 if (ringingCall != null) {
Hall Liu3cab92a2016-07-19 14:00:05 -0700503 mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700504 } else if (heldCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700505 mCallsManager.unholdCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700506 }
507 return true;
508 }
509 } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800510 if (activeCall != null && activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700511 activeCall.swapConference();
Mallikarjuna GB0ae2df82015-06-04 18:20:21 +0530512 Log.i(TAG, "CDMA calls in conference swapped, updating headset");
513 updateHeadsetWithCallState(true /* force */);
Santos Cordon88a4a602014-09-29 19:32:21 -0700514 return true;
515 } else if (ringingCall != null) {
Hall Liu3cab92a2016-07-19 14:00:05 -0700516 mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700517 return true;
518 } else if (heldCall != null) {
519 // CallsManager will hold any active calls when unhold() is called on a
520 // currently-held call.
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700521 mCallsManager.unholdCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700522 return true;
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800523 } else if (activeCall != null && activeCall.can(Connection.CAPABILITY_HOLD)) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700524 mCallsManager.holdCall(activeCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700525 return true;
526 }
527 } else if (chld == CHLD_TYPE_ADDHELDTOCONF) {
528 if (activeCall != null) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800529 if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700530 activeCall.mergeConference();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700531 return true;
Santos Cordon88a4a602014-09-29 19:32:21 -0700532 } else {
533 List<Call> conferenceable = activeCall.getConferenceableCalls();
534 if (!conferenceable.isEmpty()) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700535 mCallsManager.conference(activeCall, conferenceable.get(0));
Santos Cordon88a4a602014-09-29 19:32:21 -0700536 return true;
Brad Ebinger53855132015-10-30 10:58:19 -0700537 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700538 }
539 }
540 }
541 return false;
542 }
543
544 private void enforceModifyPermission() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800545 mContext.enforceCallingOrSelfPermission(
546 android.Manifest.permission.MODIFY_PHONE_STATE, null);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700547 }
548
Santos Cordon88a4a602014-09-29 19:32:21 -0700549 private void sendListOfCalls(boolean shouldLog) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800550 Collection<Call> mCalls = mCallsManager.getCalls();
Santos Cordon33fff492014-09-25 14:57:01 -0700551 for (Call call : mCalls) {
552 // We don't send the parent conference call to the bluetooth device.
Tyler Gunn9365c272015-06-29 09:18:31 -0700553 // We do, however want to send conferences that have no children to the bluetooth
554 // device (e.g. IMS Conference).
555 if (!call.isConference() ||
556 (call.isConference() && call
557 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN))) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700558 sendClccForCall(call, shouldLog);
Santos Cordon33fff492014-09-25 14:57:01 -0700559 }
560 }
561 sendClccEndMarker();
562 }
563
564 /**
565 * Sends a single clcc (C* List Current Calls) event for the specified call.
566 */
Santos Cordon88a4a602014-09-29 19:32:21 -0700567 private void sendClccForCall(Call call, boolean shouldLog) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800568 boolean isForeground = mCallsManager.getForegroundCall() == call;
Santos Cordon33fff492014-09-25 14:57:01 -0700569 int state = convertCallState(call.getState(), isForeground);
Santos Cordon88a4a602014-09-29 19:32:21 -0700570 boolean isPartOfConference = false;
Tyler Gunn9365c272015-06-29 09:18:31 -0700571 boolean isConferenceWithNoChildren = call.isConference() && call
572 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
Nancy Chen05a9e402014-09-26 14:14:32 -0700573
574 if (state == CALL_STATE_IDLE) {
575 return;
576 }
577
Santos Cordon88a4a602014-09-29 19:32:21 -0700578 Call conferenceCall = call.getParentCall();
579 if (conferenceCall != null) {
580 isPartOfConference = true;
581
582 // Run some alternative states for Conference-level merge/swap support.
583 // Basically, if call supports swapping or merging at the conference-level, then we need
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800584 // to expose the calls as having distinct states (ACTIVE vs CAPABILITY_HOLD) or the
585 // functionality won't show up on the bluetooth device.
Santos Cordon88a4a602014-09-29 19:32:21 -0700586
587 // Before doing any special logic, ensure that we are dealing with an ACTIVE call and
588 // that the conference itself has a notion of the current "active" child call.
589 Call activeChild = conferenceCall.getConferenceLevelActiveCall();
590 if (state == CALL_STATE_ACTIVE && activeChild != null) {
591 // Reevaluate state if we can MERGE or if we can SWAP without previously having
592 // MERGED.
593 boolean shouldReevaluateState =
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800594 conferenceCall.can(Connection.CAPABILITY_MERGE_CONFERENCE) ||
595 (conferenceCall.can(Connection.CAPABILITY_SWAP_CONFERENCE) &&
Brad Ebinger53855132015-10-30 10:58:19 -0700596 !conferenceCall.wasConferencePreviouslyMerged());
Santos Cordon88a4a602014-09-29 19:32:21 -0700597
598 if (shouldReevaluateState) {
599 isPartOfConference = false;
600 if (call == activeChild) {
601 state = CALL_STATE_ACTIVE;
602 } else {
603 // At this point we know there is an "active" child and we know that it is
604 // not this call, so set it to HELD instead.
605 state = CALL_STATE_HELD;
606 }
607 }
608 }
Tyler Gunn9365c272015-06-29 09:18:31 -0700609 } else if (isConferenceWithNoChildren) {
610 // Handle the special case of an IMS conference call without conference event package
611 // support. The call will be marked as a conference, but the conference will not have
612 // child calls where conference event packages are not used by the carrier.
613 isPartOfConference = true;
Santos Cordon88a4a602014-09-29 19:32:21 -0700614 }
615
Nancy Chen05a9e402014-09-26 14:14:32 -0700616 int index = getIndexForCall(call);
617 int direction = call.isIncoming() ? 1 : 0;
Yorke Leefb70e1e2014-09-29 14:22:53 -0700618 final Uri addressUri;
619 if (call.getGatewayInfo() != null) {
620 addressUri = call.getGatewayInfo().getOriginalAddress();
621 } else {
622 addressUri = call.getHandle();
623 }
Santos Cordon33fff492014-09-25 14:57:01 -0700624 String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
625 int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
626
Santos Cordon88a4a602014-09-29 19:32:21 -0700627 if (shouldLog) {
628 Log.i(this, "sending clcc for call %d, %d, %d, %b, %s, %d",
629 index, direction, state, isPartOfConference, Log.piiHandle(address),
630 addressType);
631 }
Yorke Leee241cd62014-10-09 13:41:19 -0700632
633 if (mBluetoothHeadset != null) {
634 mBluetoothHeadset.clccResponse(
635 index, direction, state, 0, isPartOfConference, address, addressType);
636 }
Santos Cordon33fff492014-09-25 14:57:01 -0700637 }
638
639 private void sendClccEndMarker() {
640 // End marker is recognized with an index value of 0. All other parameters are ignored.
Yorke Leee241cd62014-10-09 13:41:19 -0700641 if (mBluetoothHeadset != null) {
642 mBluetoothHeadset.clccResponse(0 /* index */, 0, 0, 0, false, null, 0);
643 }
Santos Cordon33fff492014-09-25 14:57:01 -0700644 }
645
646 /**
647 * Returns the caches index for the specified call. If no such index exists, then an index is
648 * given (smallest number starting from 1 that isn't already taken).
649 */
650 private int getIndexForCall(Call call) {
651 if (mClccIndexMap.containsKey(call)) {
652 return mClccIndexMap.get(call);
653 }
654
655 int i = 1; // Indexes for bluetooth clcc are 1-based.
656 while (mClccIndexMap.containsValue(i)) {
657 i++;
658 }
659
660 // NOTE: Indexes are removed in {@link #onCallRemoved}.
661 mClccIndexMap.put(call, i);
662 return i;
663 }
664
Tyler Gunncd685e52014-10-10 11:48:25 -0700665 /**
666 * Sends an update of the current call state to the current Headset.
667 *
668 * @param force {@code true} if the headset state should be sent regardless if no changes to the
669 * state have occurred, {@code false} if the state should only be sent if the state has
670 * changed.
671 */
672 private void updateHeadsetWithCallState(boolean force) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700673 Call activeCall = mCallsManager.getActiveCall();
674 Call ringingCall = mCallsManager.getRingingCall();
675 Call heldCall = mCallsManager.getHeldCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700676
677 int bluetoothCallState = getBluetoothCallStateForUpdate();
678
679 String ringingAddress = null;
680 int ringingAddressType = 128;
Santos Cordonecaaeac2014-11-05 20:59:04 -0800681 if (ringingCall != null && ringingCall.getHandle() != null) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700682 ringingAddress = ringingCall.getHandle().getSchemeSpecificPart();
683 if (ringingAddress != null) {
684 ringingAddressType = PhoneNumberUtils.toaFromString(ringingAddress);
685 }
686 }
687 if (ringingAddress == null) {
688 ringingAddress = "";
689 }
690
Santos Cordona0b46122014-09-29 14:20:21 -0700691 int numActiveCalls = activeCall == null ? 0 : 1;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700692 int numHeldCalls = mCallsManager.getNumHeldCalls();
Roshan Pius7d7cf272015-08-28 11:10:56 -0700693 // Intermediate state for GSM calls which are in the process of being swapped.
694 // TODO: Should we be hardcoding this value to 2 or should we check if all top level calls
695 // are held?
696 boolean callsPendingSwitch = (numHeldCalls == 2);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700697
Santos Cordon88a4a602014-09-29 19:32:21 -0700698 // For conference calls which support swapping the active call within the conference
699 // (namely CDMA calls) we need to expose that as a held call in order for the BT device
700 // to show "swap" and "merge" functionality.
Yorke Lee720bcbe2014-10-22 18:09:15 -0700701 boolean ignoreHeldCallChange = false;
Tyler Gunn9365c272015-06-29 09:18:31 -0700702 if (activeCall != null && activeCall.isConference() &&
703 !activeCall.can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800704 if (activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700705 // Indicate that BT device should show SWAP command by indicating that there is a
706 // call on hold, but only if the conference wasn't previously merged.
707 numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1;
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800708 } else if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700709 numHeldCalls = 1; // Merge is available, so expose via numHeldCalls.
710 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700711
712 for (Call childCall : activeCall.getChildCalls()) {
713 // Held call has changed due to it being combined into a CDMA conference. Keep
714 // track of this and ignore any future update since it doesn't really count as
715 // a call change.
716 if (mOldHeldCall == childCall) {
717 ignoreHeldCallChange = true;
718 break;
719 }
720 }
Santos Cordon88a4a602014-09-29 19:32:21 -0700721 }
722
Santos Cordona0b46122014-09-29 14:20:21 -0700723 if (mBluetoothHeadset != null &&
Roshan Pius7d7cf272015-08-28 11:10:56 -0700724 (force ||
725 (!callsPendingSwitch &&
726 (numActiveCalls != mNumActiveCalls ||
Brad Ebinger53855132015-10-30 10:58:19 -0700727 numHeldCalls != mNumHeldCalls ||
728 bluetoothCallState != mBluetoothCallState ||
729 !TextUtils.equals(ringingAddress, mRingingAddress) ||
730 ringingAddressType != mRingingAddressType ||
731 (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700732
Santos Cordona0b46122014-09-29 14:20:21 -0700733 // If the call is transitioning into the alerting state, send DIALING first.
734 // Some devices expect to see a DIALING state prior to seeing an ALERTING state
735 // so we need to send it first.
736 boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState &&
737 bluetoothCallState == CALL_STATE_ALERTING;
738
Santos Cordonc0ca76e2014-10-21 15:54:19 -0700739 mOldHeldCall = heldCall;
Santos Cordona0b46122014-09-29 14:20:21 -0700740 mNumActiveCalls = numActiveCalls;
741 mNumHeldCalls = numHeldCalls;
742 mBluetoothCallState = bluetoothCallState;
743 mRingingAddress = ringingAddress;
744 mRingingAddressType = ringingAddressType;
745
746 if (sendDialingFirst) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700747 // Log in full to make logs easier to debug.
748 Log.i(TAG, "updateHeadsetWithCallState " +
749 "numActive %s, " +
750 "numHeld %s, " +
751 "callState %s, " +
752 "ringing number %s, " +
753 "ringing type %s",
754 mNumActiveCalls,
755 mNumHeldCalls,
756 CALL_STATE_DIALING,
757 Log.pii(mRingingAddress),
758 mRingingAddressType);
Santos Cordona0b46122014-09-29 14:20:21 -0700759 mBluetoothHeadset.phoneStateChanged(
760 mNumActiveCalls,
761 mNumHeldCalls,
762 CALL_STATE_DIALING,
763 mRingingAddress,
764 mRingingAddressType);
765 }
766
767 Log.i(TAG, "updateHeadsetWithCallState " +
768 "numActive %s, " +
769 "numHeld %s, " +
770 "callState %s, " +
771 "ringing number %s, " +
772 "ringing type %s",
773 mNumActiveCalls,
774 mNumHeldCalls,
775 mBluetoothCallState,
776 Log.pii(mRingingAddress),
777 mRingingAddressType);
778
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700779 mBluetoothHeadset.phoneStateChanged(
Santos Cordona0b46122014-09-29 14:20:21 -0700780 mNumActiveCalls,
781 mNumHeldCalls,
782 mBluetoothCallState,
783 mRingingAddress,
784 mRingingAddressType);
Santos Cordon88a4a602014-09-29 19:32:21 -0700785
786 mHeadsetUpdatedRecently = true;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700787 }
788 }
789
790 private int getBluetoothCallStateForUpdate() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800791 CallsManager callsManager = mCallsManager;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700792 Call ringingCall = mCallsManager.getRingingCall();
Roshan Pius7d7cf272015-08-28 11:10:56 -0700793 Call dialingCall = mCallsManager.getOutgoingCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700794
795 //
796 // !! WARNING !!
797 // You will note that CALL_STATE_WAITING, CALL_STATE_HELD, and CALL_STATE_ACTIVE are not
798 // used in this version of the call state mappings. This is on purpose.
799 // phone_state_change() in btif_hf.c is not written to handle these states. Only with the
800 // listCalls*() method are WAITING and ACTIVE used.
801 // Using the unsupported states here caused problems with inconsistent state in some
802 // bluetooth devices (like not getting out of ringing state after answering a call).
803 //
804 int bluetoothCallState = CALL_STATE_IDLE;
805 if (ringingCall != null) {
806 bluetoothCallState = CALL_STATE_INCOMING;
Nancy Chen05a9e402014-09-26 14:14:32 -0700807 } else if (dialingCall != null) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700808 bluetoothCallState = CALL_STATE_ALERTING;
809 }
810 return bluetoothCallState;
811 }
812
Santos Cordon33fff492014-09-25 14:57:01 -0700813 private int convertCallState(int callState, boolean isForegroundCall) {
814 switch (callState) {
815 case CallState.NEW:
816 case CallState.ABORTED:
817 case CallState.DISCONNECTED:
818 return CALL_STATE_IDLE;
819
820 case CallState.ACTIVE:
821 return CALL_STATE_ACTIVE;
822
Santos Cordond9f90062015-10-28 15:55:34 -0700823 case CallState.CONNECTING:
824 case CallState.SELECT_PHONE_ACCOUNT:
Santos Cordon33fff492014-09-25 14:57:01 -0700825 case CallState.DIALING:
Tyler Gunn1e37be52016-07-11 08:54:23 -0700826 case CallState.PULLING:
Santos Cordon33fff492014-09-25 14:57:01 -0700827 // Yes, this is correctly returning ALERTING.
828 // "Dialing" for BT means that we have sent information to the service provider
829 // to place the call but there is no confirmation that the call is going through.
830 // When there finally is confirmation, the ringback is played which is referred to
831 // as an "alert" tone, thus, ALERTING.
832 // TODO: We should consider using the ALERTING terms in Telecom because that
833 // seems to be more industry-standard.
834 return CALL_STATE_ALERTING;
835
836 case CallState.ON_HOLD:
837 return CALL_STATE_HELD;
838
839 case CallState.RINGING:
840 if (isForegroundCall) {
841 return CALL_STATE_INCOMING;
842 } else {
843 return CALL_STATE_WAITING;
844 }
845 }
846 return CALL_STATE_IDLE;
847 }
848
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700849 /**
850 * Returns the best phone account to use for the given state of all calls.
851 * First, tries to return the phone account for the foreground call, second the default
852 * phone account for PhoneAccount.SCHEME_TEL.
853 */
854 private PhoneAccount getBestPhoneAccount() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800855 if (mPhoneAccountRegistrar == null) {
Santos Cordon0b5cb4d2014-12-02 02:40:10 -0800856 return null;
857 }
858
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800859 Call call = mCallsManager.getForegroundCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700860
861 PhoneAccount account = null;
862 if (call != null) {
863 // First try to get the network name of the foreground call.
Tony Mak240656f2015-12-04 11:36:22 +0000864 account = mPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
Santos Cordon6a212642015-05-08 16:35:23 -0700865 call.getTargetPhoneAccount());
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700866 }
867
868 if (account == null) {
869 // Second, Try to get the label for the default Phone Account.
Tony Mak240656f2015-12-04 11:36:22 +0000870 account = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
871 mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
Brad Ebinger53855132015-10-30 10:58:19 -0700872 PhoneAccount.SCHEME_TEL));
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700873 }
874 return account;
875 }
876}