blob: 0ad5d4c6ce08d5ad83d207b8dc260f148d97b13e [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;
Brad Ebinger953e1af2016-10-05 15:45:22 -070032import android.telecom.Log;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070033import android.telecom.PhoneAccount;
Hall Liu3cab92a2016-07-19 14:00:05 -070034import android.telecom.VideoProfile;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070035import android.telephony.PhoneNumberUtils;
36import android.telephony.TelephonyManager;
37import android.text.TextUtils;
38
Brad Ebinger53855132015-10-30 10:58:19 -070039import com.android.internal.annotations.VisibleForTesting;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070040import com.android.server.telecom.CallsManager.CallsManagerListener;
41
Santos Cordon33fff492014-09-25 14:57:01 -070042import java.util.Collection;
43import java.util.HashMap;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070044import java.util.List;
Santos Cordon33fff492014-09-25 14:57:01 -070045import java.util.Map;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070046
47/**
48 * Bluetooth headset manager for Telecom. This class shares the call state with the bluetooth device
49 * and accepts call-related commands to perform on behalf of the BT device.
50 */
Hall Liub3979ee2015-11-11 16:21:25 -080051public class BluetoothPhoneServiceImpl {
52
53 public interface BluetoothPhoneServiceImplFactory {
54 BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
55 TelecomSystem.SyncRoot lock, CallsManager callsManager,
56 PhoneAccountRegistrar phoneAccountRegistrar);
57 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -070058
59 private static final String TAG = "BluetoothPhoneService";
60
Santos Cordon68d1a6b2014-09-19 12:25:58 -070061 // match up with bthf_call_state_t of bt_hf.h
62 private static final int CALL_STATE_ACTIVE = 0;
63 private static final int CALL_STATE_HELD = 1;
64 private static final int CALL_STATE_DIALING = 2;
65 private static final int CALL_STATE_ALERTING = 3;
66 private static final int CALL_STATE_INCOMING = 4;
67 private static final int CALL_STATE_WAITING = 5;
68 private static final int CALL_STATE_IDLE = 6;
69
70 // match up with bthf_call_state_t of bt_hf.h
71 // Terminate all held or set UDUB("busy") to a waiting call
72 private static final int CHLD_TYPE_RELEASEHELD = 0;
73 // Terminate all active calls and accepts a waiting/held call
74 private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
75 // Hold all active calls and accepts a waiting/held call
76 private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
77 // Add all held calls to a conference
78 private static final int CHLD_TYPE_ADDHELDTOCONF = 3;
79
Santos Cordona0b46122014-09-29 14:20:21 -070080 private int mNumActiveCalls = 0;
81 private int mNumHeldCalls = 0;
82 private int mBluetoothCallState = CALL_STATE_IDLE;
83 private String mRingingAddress = null;
84 private int mRingingAddressType = 0;
Santos Cordonc0ca76e2014-10-21 15:54:19 -070085 private Call mOldHeldCall = null;
Santos Cordona0b46122014-09-29 14:20:21 -070086
Santos Cordon68d1a6b2014-09-19 12:25:58 -070087 /**
88 * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
89 * bluetooth headset code uses to control call.
90 */
Brad Ebinger53855132015-10-30 10:58:19 -070091 @VisibleForTesting
92 public final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -070093 @Override
94 public boolean answerCall() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -070095 synchronized (mLock) {
96 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -080097 Log.startSession("BPSI.aC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -070098 long token = Binder.clearCallingIdentity();
99 try {
100 Log.i(TAG, "BT - answering call");
101 Call call = mCallsManager.getRingingCall();
102 if (call != null) {
Hall Liu3cab92a2016-07-19 14:00:05 -0700103 mCallsManager.answerCall(call, VideoProfile.STATE_AUDIO_ONLY);
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700104 return true;
105 }
106 return false;
107 } finally {
108 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800109 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700110 }
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700111
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700112 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700113 }
114
115 @Override
116 public boolean hangupCall() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700117 synchronized (mLock) {
118 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800119 Log.startSession("BPSI.hC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700120 long token = Binder.clearCallingIdentity();
121 try {
122 Log.i(TAG, "BT - hanging up call");
123 Call call = mCallsManager.getForegroundCall();
124 if (call != null) {
125 mCallsManager.disconnectCall(call);
126 return true;
127 }
128 return false;
129 } finally {
130 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800131 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700132 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700133 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700134 }
135
136 @Override
137 public boolean sendDtmf(int dtmf) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700138 synchronized (mLock) {
139 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800140 Log.startSession("BPSI.sD");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700141 long token = Binder.clearCallingIdentity();
142 try {
143 Log.i(TAG, "BT - sendDtmf %c", Log.DEBUG ? dtmf : '.');
144 Call call = mCallsManager.getForegroundCall();
145 if (call != null) {
146 // TODO: Consider making this a queue instead of starting/stopping
147 // in quick succession.
148 mCallsManager.playDtmfTone(call, (char) dtmf);
149 mCallsManager.stopDtmfTone(call);
150 return true;
151 }
152 return false;
153 } finally {
154 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800155 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700156 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700157 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700158 }
159
160 @Override
161 public String getNetworkOperator() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700162 synchronized (mLock) {
163 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800164 Log.startSession("BPSI.gNO");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700165 long token = Binder.clearCallingIdentity();
166 try {
167 Log.i(TAG, "getNetworkOperator");
168 PhoneAccount account = getBestPhoneAccount();
Santos Cordon22403a72016-04-12 16:51:55 -0700169 if (account != null && account.getLabel() != null) {
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700170 return account.getLabel().toString();
171 } else {
172 // Finally, just get the network name from telephony.
173 return TelephonyManager.from(mContext)
174 .getNetworkOperatorName();
175 }
176 } finally {
177 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800178 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700179 }
180 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700181 }
182
183 @Override
184 public String getSubscriberNumber() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700185 synchronized (mLock) {
186 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800187 Log.startSession("BPSI.gSN");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700188 long token = Binder.clearCallingIdentity();
189 try {
190 Log.i(TAG, "getSubscriberNumber");
191 String address = null;
192 PhoneAccount account = getBestPhoneAccount();
193 if (account != null) {
194 Uri addressUri = account.getAddress();
195 if (addressUri != null) {
196 address = addressUri.getSchemeSpecificPart();
197 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700198 }
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700199 if (TextUtils.isEmpty(address)) {
200 address = TelephonyManager.from(mContext).getLine1Number();
Andre Eisenbach31092622015-06-04 18:56:42 -0700201 if (address == null) address = "";
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700202 }
203 return address;
204 } finally {
205 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800206 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700207 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700208 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700209 }
210
211 @Override
212 public boolean listCurrentCalls() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700213 synchronized (mLock) {
214 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800215 Log.startSession("BPSI.lCC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700216 long token = Binder.clearCallingIdentity();
217 try {
218 // only log if it is after we recently updated the headset state or else it can
219 // clog the android log since this can be queried every second.
220 boolean logQuery = mHeadsetUpdatedRecently;
221 mHeadsetUpdatedRecently = false;
222
223 if (logQuery) {
224 Log.i(TAG, "listcurrentCalls");
225 }
226
227 sendListOfCalls(logQuery);
228 return true;
229 } finally {
230 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800231 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700232 }
Santos Cordon88a4a602014-09-29 19:32:21 -0700233 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700234 }
235
236 @Override
237 public boolean queryPhoneState() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700238 synchronized (mLock) {
239 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800240 Log.startSession("BPSI.qPS");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700241 long token = Binder.clearCallingIdentity();
242 try {
243 Log.i(TAG, "queryPhoneState");
244 updateHeadsetWithCallState(true /* force */);
245 return true;
246 } finally {
247 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800248 Log.endSession();
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700249 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700250 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700251 }
252
253 @Override
254 public boolean processChld(int chld) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700255 synchronized (mLock) {
256 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800257 Log.startSession("BPSI.pC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700258 long token = Binder.clearCallingIdentity();
259 try {
260 Log.i(TAG, "processChld %d", chld);
261 return BluetoothPhoneServiceImpl.this.processChld(chld);
262 } finally {
263 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800264 Log.endSession();
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700265 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700266 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700267 }
268
269 @Override
270 public void updateBtHandsfreeAfterRadioTechnologyChange() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700271 Log.d(TAG, "RAT change - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700272 // deprecated
273 }
274
275 @Override
276 public void cdmaSetSecondCallState(boolean state) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700277 Log.d(TAG, "cdma 1 - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700278 // deprecated
279 }
280
281 @Override
282 public void cdmaSwapSecondCallState() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700283 Log.d(TAG, "cdma 2 - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700284 // deprecated
285 }
286 };
287
288 /**
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700289 * Listens to call changes from the CallsManager and calls into methods to update the bluetooth
290 * headset with the new states.
291 */
Brad Ebinger53855132015-10-30 10:58:19 -0700292 @VisibleForTesting
293 public CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700294 @Override
295 public void onCallAdded(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700296 if (call.isExternalCall()) {
297 return;
298 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700299 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700300 }
301
302 @Override
303 public void onCallRemoved(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700304 if (call.isExternalCall()) {
305 return;
306 }
Santos Cordon33fff492014-09-25 14:57:01 -0700307 mClccIndexMap.remove(call);
Tyler Gunncd685e52014-10-10 11:48:25 -0700308 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700309 }
310
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700311 /**
312 * Where a call which was external becomes a regular call, or a regular call becomes
313 * external, treat as an add or remove, respectively.
314 *
315 * @param call The call.
316 * @param isExternalCall {@code True} if the call became external, {@code false} otherwise.
317 */
318 @Override
319 public void onExternalCallChanged(Call call, boolean isExternalCall) {
320 if (isExternalCall) {
321 onCallRemoved(call);
322 } else {
323 onCallAdded(call);
324 }
325 }
326
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700327 @Override
328 public void onCallStateChanged(Call call, int oldState, int newState) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700329 if (call.isExternalCall()) {
330 return;
331 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700332 // If a call is being put on hold because of a new connecting call, ignore the
333 // CONNECTING since the BT state update needs to send out the numHeld = 1 + dialing
334 // state atomically.
335 // When the call later transitions to DIALING/DISCONNECTED we will then send out the
336 // aggregated update.
337 if (oldState == CallState.ACTIVE && newState == CallState.ON_HOLD) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800338 for (Call otherCall : mCallsManager.getCalls()) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700339 if (otherCall.getState() == CallState.CONNECTING) {
340 return;
341 }
342 }
343 }
344
345 // To have an active call and another dialing at the same time is an invalid BT
346 // state. We can assume that the active call will be automatically held which will
347 // send another update at which point we will be in the right state.
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800348 if (mCallsManager.getActiveCall() != null
Tyler Gunn1e37be52016-07-11 08:54:23 -0700349 && oldState == CallState.CONNECTING &&
350 (newState == CallState.DIALING || newState == CallState.PULLING)) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700351 return;
352 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700353 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700354 }
355
356 @Override
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700357 public void onIsConferencedChanged(Call call) {
Tyler Gunnf15dc332016-06-07 16:01:41 -0700358 if (call.isExternalCall()) {
359 return;
360 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700361 /*
362 * Filter certain onIsConferencedChanged callbacks. Unfortunately this needs to be done
363 * because conference change events are not atomic and multiple callbacks get fired
364 * when two calls are conferenced together. This confuses updateHeadsetWithCallState
365 * if it runs in the middle of two calls being conferenced and can cause spurious and
366 * incorrect headset state updates. One of the scenarios is described below for CDMA
367 * conference calls.
368 *
369 * 1) Call 1 and Call 2 are being merged into conference Call 3.
370 * 2) Call 1 has its parent set to Call 3, but Call 2 does not have a parent yet.
371 * 3) updateHeadsetWithCallState now thinks that there are two active calls (Call 2 and
372 * Call 3) when there is actually only one active call (Call 3).
373 */
374 if (call.getParentCall() != null) {
375 // If this call is newly conferenced, ignore the callback. We only care about the
376 // one sent for the parent conference call.
377 Log.d(this, "Ignoring onIsConferenceChanged from child call with new parent");
378 return;
379 }
380 if (call.getChildCalls().size() == 1) {
381 // If this is a parent call with only one child, ignore the callback as well since
382 // the minimum number of child calls to start a conference call is 2. We expect
383 // this to be called again when the parent call has another child call added.
384 Log.d(this, "Ignoring onIsConferenceChanged from parent with only one child call");
385 return;
386 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700387 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700388 }
389 };
390
391 /**
392 * Listens to connections and disconnections of bluetooth headsets. We need to save the current
393 * bluetooth headset so that we know where to send call updates.
394 */
Brad Ebinger53855132015-10-30 10:58:19 -0700395 @VisibleForTesting
396 public BluetoothProfile.ServiceListener mProfileListener =
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700397 new BluetoothProfile.ServiceListener() {
Brad Ebinger53855132015-10-30 10:58:19 -0700398 @Override
399 public void onServiceConnected(int profile, BluetoothProfile proxy) {
400 synchronized (mLock) {
401 setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy));
402 }
403 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700404
Brad Ebinger53855132015-10-30 10:58:19 -0700405 @Override
406 public void onServiceDisconnected(int profile) {
407 synchronized (mLock) {
408 mBluetoothHeadset = null;
409 }
410 }
411 };
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700412
413 /**
414 * Receives events for global state changes of the bluetooth adapter.
415 */
Brad Ebinger71286022015-12-09 17:13:27 -0800416 @VisibleForTesting
417 public final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700418 @Override
419 public void onReceive(Context context, Intent intent) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700420 synchronized (mLock) {
421 int state = intent
422 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
423 Log.d(TAG, "Bluetooth Adapter state: %d", state);
424 if (state == BluetoothAdapter.STATE_ON) {
425 try {
426 mBinder.queryPhoneState();
427 } catch (RemoteException e) {
428 // Remote exception not expected
429 }
430 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700431 }
432 }
433 };
434
Hall Liu7948f5b2016-03-15 17:39:28 -0700435 private BluetoothAdapterProxy mBluetoothAdapter;
Brad Ebinger53855132015-10-30 10:58:19 -0700436 private BluetoothHeadsetProxy mBluetoothHeadset;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700437
Santos Cordon33fff492014-09-25 14:57:01 -0700438 // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
439 private Map<Call, Integer> mClccIndexMap = new HashMap<>();
440
Santos Cordon88a4a602014-09-29 19:32:21 -0700441 private boolean mHeadsetUpdatedRecently = false;
442
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700443 private final Context mContext;
444 private final TelecomSystem.SyncRoot mLock;
445 private final CallsManager mCallsManager;
446 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700447
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800448 public IBinder getBinder() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700449 return mBinder;
450 }
451
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800452 public BluetoothPhoneServiceImpl(
453 Context context,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700454 TelecomSystem.SyncRoot lock,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800455 CallsManager callsManager,
Hall Liu7948f5b2016-03-15 17:39:28 -0700456 BluetoothAdapterProxy bluetoothAdapter,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800457 PhoneAccountRegistrar phoneAccountRegistrar) {
458 Log.d(this, "onCreate");
459
460 mContext = context;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700461 mLock = lock;
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800462 mCallsManager = callsManager;
463 mPhoneAccountRegistrar = phoneAccountRegistrar;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700464
Hall Liu7948f5b2016-03-15 17:39:28 -0700465 mBluetoothAdapter = bluetoothAdapter;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700466 if (mBluetoothAdapter == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800467 Log.d(this, "BluetoothPhoneService shutting down, no BT Adapter found.");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700468 return;
469 }
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800470 mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700471
472 IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800473 context.registerReceiver(mBluetoothAdapterReceiver, intentFilter);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700474
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800475 mCallsManager.addListener(mCallsManagerListener);
Tyler Gunncd685e52014-10-10 11:48:25 -0700476 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700477 }
478
Brad Ebinger53855132015-10-30 10:58:19 -0700479 @VisibleForTesting
480 public void setBluetoothHeadset(BluetoothHeadsetProxy bluetoothHeadset) {
481 mBluetoothHeadset = bluetoothHeadset;
482 }
483
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700484 private boolean processChld(int chld) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700485 Call activeCall = mCallsManager.getActiveCall();
486 Call ringingCall = mCallsManager.getRingingCall();
487 Call heldCall = mCallsManager.getHeldCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700488
Santos Cordon66fe8822014-10-10 16:10:58 -0700489 // TODO: Keeping as Log.i for now. Move to Log.d after L release if BT proves stable.
490 Log.i(TAG, "Active: %s\nRinging: %s\nHeld: %s", activeCall, ringingCall, heldCall);
Santos Cordon88a4a602014-09-29 19:32:21 -0700491
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700492 if (chld == CHLD_TYPE_RELEASEHELD) {
493 if (ringingCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700494 mCallsManager.rejectCall(ringingCall, false, null);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700495 return true;
496 } else if (heldCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700497 mCallsManager.disconnectCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700498 return true;
499 }
500 } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) {
Satish Kodishalaf5bdfb52015-12-30 14:17:28 +0530501 if (activeCall == null && ringingCall == null && heldCall == null)
502 return false;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700503 if (activeCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700504 mCallsManager.disconnectCall(activeCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700505 if (ringingCall != null) {
Hall Liu3cab92a2016-07-19 14:00:05 -0700506 mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700507 } else if (heldCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700508 mCallsManager.unholdCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700509 }
510 return true;
511 }
Satish Kodishalaf5bdfb52015-12-30 14:17:28 +0530512 if (ringingCall != null) {
513 mCallsManager.answerCall(ringingCall, ringingCall.getVideoState());
514 } else if (heldCall != null) {
515 mCallsManager.unholdCall(heldCall);
516 }
517 return true;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700518 } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800519 if (activeCall != null && activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700520 activeCall.swapConference();
Mallikarjuna GB0ae2df82015-06-04 18:20:21 +0530521 Log.i(TAG, "CDMA calls in conference swapped, updating headset");
522 updateHeadsetWithCallState(true /* force */);
Santos Cordon88a4a602014-09-29 19:32:21 -0700523 return true;
524 } else if (ringingCall != null) {
Hall Liu3cab92a2016-07-19 14:00:05 -0700525 mCallsManager.answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700526 return true;
527 } else if (heldCall != null) {
528 // CallsManager will hold any active calls when unhold() is called on a
529 // currently-held call.
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700530 mCallsManager.unholdCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700531 return true;
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800532 } else if (activeCall != null && activeCall.can(Connection.CAPABILITY_HOLD)) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700533 mCallsManager.holdCall(activeCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700534 return true;
535 }
536 } else if (chld == CHLD_TYPE_ADDHELDTOCONF) {
537 if (activeCall != null) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800538 if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700539 activeCall.mergeConference();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700540 return true;
Santos Cordon88a4a602014-09-29 19:32:21 -0700541 } else {
542 List<Call> conferenceable = activeCall.getConferenceableCalls();
543 if (!conferenceable.isEmpty()) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700544 mCallsManager.conference(activeCall, conferenceable.get(0));
Santos Cordon88a4a602014-09-29 19:32:21 -0700545 return true;
Brad Ebinger53855132015-10-30 10:58:19 -0700546 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700547 }
548 }
549 }
550 return false;
551 }
552
553 private void enforceModifyPermission() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800554 mContext.enforceCallingOrSelfPermission(
555 android.Manifest.permission.MODIFY_PHONE_STATE, null);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700556 }
557
Santos Cordon88a4a602014-09-29 19:32:21 -0700558 private void sendListOfCalls(boolean shouldLog) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800559 Collection<Call> mCalls = mCallsManager.getCalls();
Santos Cordon33fff492014-09-25 14:57:01 -0700560 for (Call call : mCalls) {
561 // We don't send the parent conference call to the bluetooth device.
Tyler Gunn9365c272015-06-29 09:18:31 -0700562 // We do, however want to send conferences that have no children to the bluetooth
563 // device (e.g. IMS Conference).
564 if (!call.isConference() ||
565 (call.isConference() && call
566 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN))) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700567 sendClccForCall(call, shouldLog);
Santos Cordon33fff492014-09-25 14:57:01 -0700568 }
569 }
570 sendClccEndMarker();
571 }
572
573 /**
574 * Sends a single clcc (C* List Current Calls) event for the specified call.
575 */
Santos Cordon88a4a602014-09-29 19:32:21 -0700576 private void sendClccForCall(Call call, boolean shouldLog) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800577 boolean isForeground = mCallsManager.getForegroundCall() == call;
Santos Cordon33fff492014-09-25 14:57:01 -0700578 int state = convertCallState(call.getState(), isForeground);
Santos Cordon88a4a602014-09-29 19:32:21 -0700579 boolean isPartOfConference = false;
Tyler Gunn9365c272015-06-29 09:18:31 -0700580 boolean isConferenceWithNoChildren = call.isConference() && call
581 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
Nancy Chen05a9e402014-09-26 14:14:32 -0700582
583 if (state == CALL_STATE_IDLE) {
584 return;
585 }
586
Santos Cordon88a4a602014-09-29 19:32:21 -0700587 Call conferenceCall = call.getParentCall();
588 if (conferenceCall != null) {
589 isPartOfConference = true;
590
591 // Run some alternative states for Conference-level merge/swap support.
592 // Basically, if call supports swapping or merging at the conference-level, then we need
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800593 // to expose the calls as having distinct states (ACTIVE vs CAPABILITY_HOLD) or the
594 // functionality won't show up on the bluetooth device.
Santos Cordon88a4a602014-09-29 19:32:21 -0700595
596 // Before doing any special logic, ensure that we are dealing with an ACTIVE call and
597 // that the conference itself has a notion of the current "active" child call.
598 Call activeChild = conferenceCall.getConferenceLevelActiveCall();
599 if (state == CALL_STATE_ACTIVE && activeChild != null) {
600 // Reevaluate state if we can MERGE or if we can SWAP without previously having
601 // MERGED.
602 boolean shouldReevaluateState =
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800603 conferenceCall.can(Connection.CAPABILITY_MERGE_CONFERENCE) ||
604 (conferenceCall.can(Connection.CAPABILITY_SWAP_CONFERENCE) &&
Brad Ebinger53855132015-10-30 10:58:19 -0700605 !conferenceCall.wasConferencePreviouslyMerged());
Santos Cordon88a4a602014-09-29 19:32:21 -0700606
607 if (shouldReevaluateState) {
608 isPartOfConference = false;
609 if (call == activeChild) {
610 state = CALL_STATE_ACTIVE;
611 } else {
612 // At this point we know there is an "active" child and we know that it is
613 // not this call, so set it to HELD instead.
614 state = CALL_STATE_HELD;
615 }
616 }
617 }
Tyler Gunn9365c272015-06-29 09:18:31 -0700618 } else if (isConferenceWithNoChildren) {
619 // Handle the special case of an IMS conference call without conference event package
620 // support. The call will be marked as a conference, but the conference will not have
621 // child calls where conference event packages are not used by the carrier.
622 isPartOfConference = true;
Santos Cordon88a4a602014-09-29 19:32:21 -0700623 }
624
Nancy Chen05a9e402014-09-26 14:14:32 -0700625 int index = getIndexForCall(call);
626 int direction = call.isIncoming() ? 1 : 0;
Yorke Leefb70e1e2014-09-29 14:22:53 -0700627 final Uri addressUri;
628 if (call.getGatewayInfo() != null) {
629 addressUri = call.getGatewayInfo().getOriginalAddress();
630 } else {
631 addressUri = call.getHandle();
632 }
Satish Kodishalaf4bd7c22016-11-18 12:01:51 +0530633
Santos Cordon33fff492014-09-25 14:57:01 -0700634 String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
Satish Kodishalaf4bd7c22016-11-18 12:01:51 +0530635 if (address != null) {
636 address = PhoneNumberUtils.stripSeparators(address);
637 }
638
Santos Cordon33fff492014-09-25 14:57:01 -0700639 int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
640
Santos Cordon88a4a602014-09-29 19:32:21 -0700641 if (shouldLog) {
642 Log.i(this, "sending clcc for call %d, %d, %d, %b, %s, %d",
643 index, direction, state, isPartOfConference, Log.piiHandle(address),
644 addressType);
645 }
Yorke Leee241cd62014-10-09 13:41:19 -0700646
647 if (mBluetoothHeadset != null) {
648 mBluetoothHeadset.clccResponse(
649 index, direction, state, 0, isPartOfConference, address, addressType);
650 }
Santos Cordon33fff492014-09-25 14:57:01 -0700651 }
652
653 private void sendClccEndMarker() {
654 // End marker is recognized with an index value of 0. All other parameters are ignored.
Yorke Leee241cd62014-10-09 13:41:19 -0700655 if (mBluetoothHeadset != null) {
656 mBluetoothHeadset.clccResponse(0 /* index */, 0, 0, 0, false, null, 0);
657 }
Santos Cordon33fff492014-09-25 14:57:01 -0700658 }
659
660 /**
661 * Returns the caches index for the specified call. If no such index exists, then an index is
662 * given (smallest number starting from 1 that isn't already taken).
663 */
664 private int getIndexForCall(Call call) {
665 if (mClccIndexMap.containsKey(call)) {
666 return mClccIndexMap.get(call);
667 }
668
669 int i = 1; // Indexes for bluetooth clcc are 1-based.
670 while (mClccIndexMap.containsValue(i)) {
671 i++;
672 }
673
674 // NOTE: Indexes are removed in {@link #onCallRemoved}.
675 mClccIndexMap.put(call, i);
676 return i;
677 }
678
Tyler Gunncd685e52014-10-10 11:48:25 -0700679 /**
680 * Sends an update of the current call state to the current Headset.
681 *
682 * @param force {@code true} if the headset state should be sent regardless if no changes to the
683 * state have occurred, {@code false} if the state should only be sent if the state has
684 * changed.
685 */
686 private void updateHeadsetWithCallState(boolean force) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700687 Call activeCall = mCallsManager.getActiveCall();
688 Call ringingCall = mCallsManager.getRingingCall();
689 Call heldCall = mCallsManager.getHeldCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700690
691 int bluetoothCallState = getBluetoothCallStateForUpdate();
692
693 String ringingAddress = null;
694 int ringingAddressType = 128;
Santos Cordonecaaeac2014-11-05 20:59:04 -0800695 if (ringingCall != null && ringingCall.getHandle() != null) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700696 ringingAddress = ringingCall.getHandle().getSchemeSpecificPart();
697 if (ringingAddress != null) {
698 ringingAddressType = PhoneNumberUtils.toaFromString(ringingAddress);
699 }
700 }
701 if (ringingAddress == null) {
702 ringingAddress = "";
703 }
704
Santos Cordona0b46122014-09-29 14:20:21 -0700705 int numActiveCalls = activeCall == null ? 0 : 1;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700706 int numHeldCalls = mCallsManager.getNumHeldCalls();
Roshan Pius7d7cf272015-08-28 11:10:56 -0700707 // Intermediate state for GSM calls which are in the process of being swapped.
708 // TODO: Should we be hardcoding this value to 2 or should we check if all top level calls
709 // are held?
710 boolean callsPendingSwitch = (numHeldCalls == 2);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700711
Santos Cordon88a4a602014-09-29 19:32:21 -0700712 // For conference calls which support swapping the active call within the conference
713 // (namely CDMA calls) we need to expose that as a held call in order for the BT device
714 // to show "swap" and "merge" functionality.
Yorke Lee720bcbe2014-10-22 18:09:15 -0700715 boolean ignoreHeldCallChange = false;
Tyler Gunn9365c272015-06-29 09:18:31 -0700716 if (activeCall != null && activeCall.isConference() &&
717 !activeCall.can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800718 if (activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700719 // Indicate that BT device should show SWAP command by indicating that there is a
720 // call on hold, but only if the conference wasn't previously merged.
721 numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1;
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800722 } else if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700723 numHeldCalls = 1; // Merge is available, so expose via numHeldCalls.
724 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700725
726 for (Call childCall : activeCall.getChildCalls()) {
727 // Held call has changed due to it being combined into a CDMA conference. Keep
728 // track of this and ignore any future update since it doesn't really count as
729 // a call change.
730 if (mOldHeldCall == childCall) {
731 ignoreHeldCallChange = true;
732 break;
733 }
734 }
Santos Cordon88a4a602014-09-29 19:32:21 -0700735 }
736
Santos Cordona0b46122014-09-29 14:20:21 -0700737 if (mBluetoothHeadset != null &&
Roshan Pius7d7cf272015-08-28 11:10:56 -0700738 (force ||
739 (!callsPendingSwitch &&
740 (numActiveCalls != mNumActiveCalls ||
Brad Ebinger53855132015-10-30 10:58:19 -0700741 numHeldCalls != mNumHeldCalls ||
742 bluetoothCallState != mBluetoothCallState ||
743 !TextUtils.equals(ringingAddress, mRingingAddress) ||
744 ringingAddressType != mRingingAddressType ||
745 (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700746
Santos Cordona0b46122014-09-29 14:20:21 -0700747 // If the call is transitioning into the alerting state, send DIALING first.
748 // Some devices expect to see a DIALING state prior to seeing an ALERTING state
749 // so we need to send it first.
750 boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState &&
751 bluetoothCallState == CALL_STATE_ALERTING;
752
Santos Cordonc0ca76e2014-10-21 15:54:19 -0700753 mOldHeldCall = heldCall;
Santos Cordona0b46122014-09-29 14:20:21 -0700754 mNumActiveCalls = numActiveCalls;
755 mNumHeldCalls = numHeldCalls;
756 mBluetoothCallState = bluetoothCallState;
757 mRingingAddress = ringingAddress;
758 mRingingAddressType = ringingAddressType;
759
760 if (sendDialingFirst) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700761 // Log in full to make logs easier to debug.
762 Log.i(TAG, "updateHeadsetWithCallState " +
763 "numActive %s, " +
764 "numHeld %s, " +
765 "callState %s, " +
766 "ringing number %s, " +
767 "ringing type %s",
768 mNumActiveCalls,
769 mNumHeldCalls,
770 CALL_STATE_DIALING,
771 Log.pii(mRingingAddress),
772 mRingingAddressType);
Santos Cordona0b46122014-09-29 14:20:21 -0700773 mBluetoothHeadset.phoneStateChanged(
774 mNumActiveCalls,
775 mNumHeldCalls,
776 CALL_STATE_DIALING,
777 mRingingAddress,
778 mRingingAddressType);
779 }
780
781 Log.i(TAG, "updateHeadsetWithCallState " +
782 "numActive %s, " +
783 "numHeld %s, " +
784 "callState %s, " +
785 "ringing number %s, " +
786 "ringing type %s",
787 mNumActiveCalls,
788 mNumHeldCalls,
789 mBluetoothCallState,
790 Log.pii(mRingingAddress),
791 mRingingAddressType);
792
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700793 mBluetoothHeadset.phoneStateChanged(
Santos Cordona0b46122014-09-29 14:20:21 -0700794 mNumActiveCalls,
795 mNumHeldCalls,
796 mBluetoothCallState,
797 mRingingAddress,
798 mRingingAddressType);
Santos Cordon88a4a602014-09-29 19:32:21 -0700799
800 mHeadsetUpdatedRecently = true;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700801 }
802 }
803
804 private int getBluetoothCallStateForUpdate() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800805 CallsManager callsManager = mCallsManager;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700806 Call ringingCall = mCallsManager.getRingingCall();
Roshan Pius7d7cf272015-08-28 11:10:56 -0700807 Call dialingCall = mCallsManager.getOutgoingCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700808
809 //
810 // !! WARNING !!
811 // You will note that CALL_STATE_WAITING, CALL_STATE_HELD, and CALL_STATE_ACTIVE are not
812 // used in this version of the call state mappings. This is on purpose.
813 // phone_state_change() in btif_hf.c is not written to handle these states. Only with the
814 // listCalls*() method are WAITING and ACTIVE used.
815 // Using the unsupported states here caused problems with inconsistent state in some
816 // bluetooth devices (like not getting out of ringing state after answering a call).
817 //
818 int bluetoothCallState = CALL_STATE_IDLE;
819 if (ringingCall != null) {
820 bluetoothCallState = CALL_STATE_INCOMING;
Nancy Chen05a9e402014-09-26 14:14:32 -0700821 } else if (dialingCall != null) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700822 bluetoothCallState = CALL_STATE_ALERTING;
823 }
824 return bluetoothCallState;
825 }
826
Santos Cordon33fff492014-09-25 14:57:01 -0700827 private int convertCallState(int callState, boolean isForegroundCall) {
828 switch (callState) {
829 case CallState.NEW:
830 case CallState.ABORTED:
831 case CallState.DISCONNECTED:
832 return CALL_STATE_IDLE;
833
834 case CallState.ACTIVE:
835 return CALL_STATE_ACTIVE;
836
Santos Cordond9f90062015-10-28 15:55:34 -0700837 case CallState.CONNECTING:
838 case CallState.SELECT_PHONE_ACCOUNT:
Santos Cordon33fff492014-09-25 14:57:01 -0700839 case CallState.DIALING:
Tyler Gunn1e37be52016-07-11 08:54:23 -0700840 case CallState.PULLING:
Santos Cordon33fff492014-09-25 14:57:01 -0700841 // Yes, this is correctly returning ALERTING.
842 // "Dialing" for BT means that we have sent information to the service provider
843 // to place the call but there is no confirmation that the call is going through.
844 // When there finally is confirmation, the ringback is played which is referred to
845 // as an "alert" tone, thus, ALERTING.
846 // TODO: We should consider using the ALERTING terms in Telecom because that
847 // seems to be more industry-standard.
848 return CALL_STATE_ALERTING;
849
850 case CallState.ON_HOLD:
851 return CALL_STATE_HELD;
852
853 case CallState.RINGING:
854 if (isForegroundCall) {
855 return CALL_STATE_INCOMING;
856 } else {
857 return CALL_STATE_WAITING;
858 }
859 }
860 return CALL_STATE_IDLE;
861 }
862
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700863 /**
864 * Returns the best phone account to use for the given state of all calls.
865 * First, tries to return the phone account for the foreground call, second the default
866 * phone account for PhoneAccount.SCHEME_TEL.
867 */
868 private PhoneAccount getBestPhoneAccount() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800869 if (mPhoneAccountRegistrar == null) {
Santos Cordon0b5cb4d2014-12-02 02:40:10 -0800870 return null;
871 }
872
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800873 Call call = mCallsManager.getForegroundCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700874
875 PhoneAccount account = null;
876 if (call != null) {
877 // First try to get the network name of the foreground call.
Tony Mak240656f2015-12-04 11:36:22 +0000878 account = mPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
Santos Cordon6a212642015-05-08 16:35:23 -0700879 call.getTargetPhoneAccount());
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700880 }
881
882 if (account == null) {
883 // Second, Try to get the label for the default Phone Account.
Tony Mak240656f2015-12-04 11:36:22 +0000884 account = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
885 mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
Brad Ebinger53855132015-10-30 10:58:19 -0700886 PhoneAccount.SCHEME_TEL));
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700887 }
888 return account;
889 }
890}