blob: 1fdd77f308e43895c6452c4c16035047502bb879 [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;
33import android.telephony.PhoneNumberUtils;
34import android.telephony.TelephonyManager;
35import android.text.TextUtils;
36
Brad Ebinger53855132015-10-30 10:58:19 -070037import com.android.internal.annotations.VisibleForTesting;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070038import com.android.server.telecom.CallsManager.CallsManagerListener;
39
Santos Cordon33fff492014-09-25 14:57:01 -070040import java.util.Collection;
41import java.util.HashMap;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070042import java.util.List;
Santos Cordon33fff492014-09-25 14:57:01 -070043import java.util.Map;
Santos Cordon68d1a6b2014-09-19 12:25:58 -070044
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 */
Hall Liub3979ee2015-11-11 16:21:25 -080049public class BluetoothPhoneServiceImpl {
50
51 public interface BluetoothPhoneServiceImplFactory {
52 BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
53 TelecomSystem.SyncRoot lock, CallsManager callsManager,
54 PhoneAccountRegistrar phoneAccountRegistrar);
55 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -070056
57 private static final String TAG = "BluetoothPhoneService";
58
Santos Cordon68d1a6b2014-09-19 12:25:58 -070059 // 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
Santos Cordona0b46122014-09-29 14:20:21 -070078 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;
Santos Cordonc0ca76e2014-10-21 15:54:19 -070083 private Call mOldHeldCall = null;
Santos Cordona0b46122014-09-29 14:20:21 -070084
Santos Cordon68d1a6b2014-09-19 12:25:58 -070085 /**
86 * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
87 * bluetooth headset code uses to control call.
88 */
Brad Ebinger53855132015-10-30 10:58:19 -070089 @VisibleForTesting
90 public final IBluetoothHeadsetPhone.Stub mBinder = new IBluetoothHeadsetPhone.Stub() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -070091 @Override
92 public boolean answerCall() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -070093 synchronized (mLock) {
94 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -080095 Log.startSession("BPSI.aC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -070096 long token = Binder.clearCallingIdentity();
97 try {
98 Log.i(TAG, "BT - answering call");
99 Call call = mCallsManager.getRingingCall();
100 if (call != null) {
Mallikarjuna GB8729c182015-06-04 18:50:53 +0530101 mCallsManager.answerCall(call, call.getVideoState());
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700102 return true;
103 }
104 return false;
105 } finally {
106 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800107 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700108 }
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700109
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700110 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700111 }
112
113 @Override
114 public boolean hangupCall() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700115 synchronized (mLock) {
116 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800117 Log.startSession("BPSI.hC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700118 long token = Binder.clearCallingIdentity();
119 try {
120 Log.i(TAG, "BT - hanging up call");
121 Call call = mCallsManager.getForegroundCall();
122 if (call != null) {
123 mCallsManager.disconnectCall(call);
124 return true;
125 }
126 return false;
127 } finally {
128 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800129 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700130 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700131 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700132 }
133
134 @Override
135 public boolean sendDtmf(int dtmf) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700136 synchronized (mLock) {
137 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800138 Log.startSession("BPSI.sD");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700139 long token = Binder.clearCallingIdentity();
140 try {
141 Log.i(TAG, "BT - sendDtmf %c", Log.DEBUG ? dtmf : '.');
142 Call call = mCallsManager.getForegroundCall();
143 if (call != null) {
144 // TODO: Consider making this a queue instead of starting/stopping
145 // in quick succession.
146 mCallsManager.playDtmfTone(call, (char) dtmf);
147 mCallsManager.stopDtmfTone(call);
148 return true;
149 }
150 return false;
151 } finally {
152 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800153 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700154 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700155 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700156 }
157
158 @Override
159 public String getNetworkOperator() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700160 synchronized (mLock) {
161 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800162 Log.startSession("BPSI.gNO");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700163 long token = Binder.clearCallingIdentity();
164 try {
165 Log.i(TAG, "getNetworkOperator");
166 PhoneAccount account = getBestPhoneAccount();
167 if (account != null) {
168 return account.getLabel().toString();
169 } else {
170 // Finally, just get the network name from telephony.
171 return TelephonyManager.from(mContext)
172 .getNetworkOperatorName();
173 }
174 } finally {
175 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800176 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700177 }
178 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700179 }
180
181 @Override
182 public String getSubscriberNumber() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700183 synchronized (mLock) {
184 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800185 Log.startSession("BPSI.gSN");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700186 long token = Binder.clearCallingIdentity();
187 try {
188 Log.i(TAG, "getSubscriberNumber");
189 String address = null;
190 PhoneAccount account = getBestPhoneAccount();
191 if (account != null) {
192 Uri addressUri = account.getAddress();
193 if (addressUri != null) {
194 address = addressUri.getSchemeSpecificPart();
195 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700196 }
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700197 if (TextUtils.isEmpty(address)) {
198 address = TelephonyManager.from(mContext).getLine1Number();
Andre Eisenbach31092622015-06-04 18:56:42 -0700199 if (address == null) address = "";
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700200 }
201 return address;
202 } finally {
203 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800204 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700205 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700206 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700207 }
208
209 @Override
210 public boolean listCurrentCalls() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700211 synchronized (mLock) {
212 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800213 Log.startSession("BPSI.lCC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700214 long token = Binder.clearCallingIdentity();
215 try {
216 // only log if it is after we recently updated the headset state or else it can
217 // clog the android log since this can be queried every second.
218 boolean logQuery = mHeadsetUpdatedRecently;
219 mHeadsetUpdatedRecently = false;
220
221 if (logQuery) {
222 Log.i(TAG, "listcurrentCalls");
223 }
224
225 sendListOfCalls(logQuery);
226 return true;
227 } finally {
228 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800229 Log.endSession();
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700230 }
Santos Cordon88a4a602014-09-29 19:32:21 -0700231 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700232 }
233
234 @Override
235 public boolean queryPhoneState() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700236 synchronized (mLock) {
237 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800238 Log.startSession("BPSI.qPS");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700239 long token = Binder.clearCallingIdentity();
240 try {
241 Log.i(TAG, "queryPhoneState");
242 updateHeadsetWithCallState(true /* force */);
243 return true;
244 } finally {
245 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800246 Log.endSession();
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700247 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700248 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700249 }
250
251 @Override
252 public boolean processChld(int chld) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700253 synchronized (mLock) {
254 enforceModifyPermission();
Brad Ebinger3165d502015-12-15 17:22:29 -0800255 Log.startSession("BPSI.pC");
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700256 long token = Binder.clearCallingIdentity();
257 try {
258 Log.i(TAG, "processChld %d", chld);
259 return BluetoothPhoneServiceImpl.this.processChld(chld);
260 } finally {
261 Binder.restoreCallingIdentity(token);
Brad Ebinger3165d502015-12-15 17:22:29 -0800262 Log.endSession();
Santos Cordonebf2d0f2015-05-15 10:28:29 -0700263 }
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700264 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700265 }
266
267 @Override
268 public void updateBtHandsfreeAfterRadioTechnologyChange() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700269 Log.d(TAG, "RAT change - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700270 // deprecated
271 }
272
273 @Override
274 public void cdmaSetSecondCallState(boolean state) throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700275 Log.d(TAG, "cdma 1 - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700276 // deprecated
277 }
278
279 @Override
280 public void cdmaSwapSecondCallState() throws RemoteException {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700281 Log.d(TAG, "cdma 2 - deprecated");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700282 // deprecated
283 }
284 };
285
286 /**
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700287 * Listens to call changes from the CallsManager and calls into methods to update the bluetooth
288 * headset with the new states.
289 */
Brad Ebinger53855132015-10-30 10:58:19 -0700290 @VisibleForTesting
291 public CallsManagerListener mCallsManagerListener = new CallsManagerListenerBase() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700292 @Override
293 public void onCallAdded(Call call) {
Tyler Gunncd685e52014-10-10 11:48:25 -0700294 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700295 }
296
297 @Override
298 public void onCallRemoved(Call call) {
Santos Cordon33fff492014-09-25 14:57:01 -0700299 mClccIndexMap.remove(call);
Tyler Gunncd685e52014-10-10 11:48:25 -0700300 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700301 }
302
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700303 /**
304 * Where a call which was external becomes a regular call, or a regular call becomes
305 * external, treat as an add or remove, respectively.
306 *
307 * @param call The call.
308 * @param isExternalCall {@code True} if the call became external, {@code false} otherwise.
309 */
310 @Override
311 public void onExternalCallChanged(Call call, boolean isExternalCall) {
312 if (isExternalCall) {
313 onCallRemoved(call);
314 } else {
315 onCallAdded(call);
316 }
317 }
318
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700319 @Override
320 public void onCallStateChanged(Call call, int oldState, int newState) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700321 // If a call is being put on hold because of a new connecting call, ignore the
322 // CONNECTING since the BT state update needs to send out the numHeld = 1 + dialing
323 // state atomically.
324 // When the call later transitions to DIALING/DISCONNECTED we will then send out the
325 // aggregated update.
326 if (oldState == CallState.ACTIVE && newState == CallState.ON_HOLD) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800327 for (Call otherCall : mCallsManager.getCalls()) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700328 if (otherCall.getState() == CallState.CONNECTING) {
329 return;
330 }
331 }
332 }
333
334 // To have an active call and another dialing at the same time is an invalid BT
335 // state. We can assume that the active call will be automatically held which will
336 // send another update at which point we will be in the right state.
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800337 if (mCallsManager.getActiveCall() != null
Yorke Lee720bcbe2014-10-22 18:09:15 -0700338 && oldState == CallState.CONNECTING && newState == CallState.DIALING) {
339 return;
340 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700341 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700342 }
343
344 @Override
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700345 public void onIsConferencedChanged(Call call) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700346 /*
347 * Filter certain onIsConferencedChanged callbacks. Unfortunately this needs to be done
348 * because conference change events are not atomic and multiple callbacks get fired
349 * when two calls are conferenced together. This confuses updateHeadsetWithCallState
350 * if it runs in the middle of two calls being conferenced and can cause spurious and
351 * incorrect headset state updates. One of the scenarios is described below for CDMA
352 * conference calls.
353 *
354 * 1) Call 1 and Call 2 are being merged into conference Call 3.
355 * 2) Call 1 has its parent set to Call 3, but Call 2 does not have a parent yet.
356 * 3) updateHeadsetWithCallState now thinks that there are two active calls (Call 2 and
357 * Call 3) when there is actually only one active call (Call 3).
358 */
359 if (call.getParentCall() != null) {
360 // If this call is newly conferenced, ignore the callback. We only care about the
361 // one sent for the parent conference call.
362 Log.d(this, "Ignoring onIsConferenceChanged from child call with new parent");
363 return;
364 }
365 if (call.getChildCalls().size() == 1) {
366 // If this is a parent call with only one child, ignore the callback as well since
367 // the minimum number of child calls to start a conference call is 2. We expect
368 // this to be called again when the parent call has another child call added.
369 Log.d(this, "Ignoring onIsConferenceChanged from parent with only one child call");
370 return;
371 }
Tyler Gunncd685e52014-10-10 11:48:25 -0700372 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700373 }
374 };
375
376 /**
377 * Listens to connections and disconnections of bluetooth headsets. We need to save the current
378 * bluetooth headset so that we know where to send call updates.
379 */
Brad Ebinger53855132015-10-30 10:58:19 -0700380 @VisibleForTesting
381 public BluetoothProfile.ServiceListener mProfileListener =
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700382 new BluetoothProfile.ServiceListener() {
Brad Ebinger53855132015-10-30 10:58:19 -0700383 @Override
384 public void onServiceConnected(int profile, BluetoothProfile proxy) {
385 synchronized (mLock) {
386 setBluetoothHeadset(new BluetoothHeadsetProxy((BluetoothHeadset) proxy));
387 }
388 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700389
Brad Ebinger53855132015-10-30 10:58:19 -0700390 @Override
391 public void onServiceDisconnected(int profile) {
392 synchronized (mLock) {
393 mBluetoothHeadset = null;
394 }
395 }
396 };
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700397
398 /**
399 * Receives events for global state changes of the bluetooth adapter.
400 */
Brad Ebinger71286022015-12-09 17:13:27 -0800401 @VisibleForTesting
402 public final BroadcastReceiver mBluetoothAdapterReceiver = new BroadcastReceiver() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700403 @Override
404 public void onReceive(Context context, Intent intent) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700405 synchronized (mLock) {
406 int state = intent
407 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
408 Log.d(TAG, "Bluetooth Adapter state: %d", state);
409 if (state == BluetoothAdapter.STATE_ON) {
410 try {
411 mBinder.queryPhoneState();
412 } catch (RemoteException e) {
413 // Remote exception not expected
414 }
415 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700416 }
417 }
418 };
419
Hall Liu7948f5b2016-03-15 17:39:28 -0700420 private BluetoothAdapterProxy mBluetoothAdapter;
Brad Ebinger53855132015-10-30 10:58:19 -0700421 private BluetoothHeadsetProxy mBluetoothHeadset;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700422
Santos Cordon33fff492014-09-25 14:57:01 -0700423 // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
424 private Map<Call, Integer> mClccIndexMap = new HashMap<>();
425
Santos Cordon88a4a602014-09-29 19:32:21 -0700426 private boolean mHeadsetUpdatedRecently = false;
427
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700428 private final Context mContext;
429 private final TelecomSystem.SyncRoot mLock;
430 private final CallsManager mCallsManager;
431 private final PhoneAccountRegistrar mPhoneAccountRegistrar;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700432
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800433 public IBinder getBinder() {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700434 return mBinder;
435 }
436
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800437 public BluetoothPhoneServiceImpl(
438 Context context,
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700439 TelecomSystem.SyncRoot lock,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800440 CallsManager callsManager,
Hall Liu7948f5b2016-03-15 17:39:28 -0700441 BluetoothAdapterProxy bluetoothAdapter,
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800442 PhoneAccountRegistrar phoneAccountRegistrar) {
443 Log.d(this, "onCreate");
444
445 mContext = context;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700446 mLock = lock;
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800447 mCallsManager = callsManager;
448 mPhoneAccountRegistrar = phoneAccountRegistrar;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700449
Hall Liu7948f5b2016-03-15 17:39:28 -0700450 mBluetoothAdapter = bluetoothAdapter;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700451 if (mBluetoothAdapter == null) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800452 Log.d(this, "BluetoothPhoneService shutting down, no BT Adapter found.");
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700453 return;
454 }
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800455 mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700456
457 IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800458 context.registerReceiver(mBluetoothAdapterReceiver, intentFilter);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700459
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800460 mCallsManager.addListener(mCallsManagerListener);
Tyler Gunncd685e52014-10-10 11:48:25 -0700461 updateHeadsetWithCallState(false /* force */);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700462 }
463
Brad Ebinger53855132015-10-30 10:58:19 -0700464 @VisibleForTesting
465 public void setBluetoothHeadset(BluetoothHeadsetProxy bluetoothHeadset) {
466 mBluetoothHeadset = bluetoothHeadset;
467 }
468
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700469 private boolean processChld(int chld) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700470 Call activeCall = mCallsManager.getActiveCall();
471 Call ringingCall = mCallsManager.getRingingCall();
472 Call heldCall = mCallsManager.getHeldCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700473
Santos Cordon66fe8822014-10-10 16:10:58 -0700474 // TODO: Keeping as Log.i for now. Move to Log.d after L release if BT proves stable.
475 Log.i(TAG, "Active: %s\nRinging: %s\nHeld: %s", activeCall, ringingCall, heldCall);
Santos Cordon88a4a602014-09-29 19:32:21 -0700476
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700477 if (chld == CHLD_TYPE_RELEASEHELD) {
478 if (ringingCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700479 mCallsManager.rejectCall(ringingCall, false, null);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700480 return true;
481 } else if (heldCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700482 mCallsManager.disconnectCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700483 return true;
484 }
485 } else if (chld == CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD) {
486 if (activeCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700487 mCallsManager.disconnectCall(activeCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700488 if (ringingCall != null) {
Mallikarjuna GB8729c182015-06-04 18:50:53 +0530489 mCallsManager.answerCall(ringingCall, ringingCall.getVideoState());
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700490 } else if (heldCall != null) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700491 mCallsManager.unholdCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700492 }
493 return true;
494 }
495 } else if (chld == CHLD_TYPE_HOLDACTIVE_ACCEPTHELD) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800496 if (activeCall != null && activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700497 activeCall.swapConference();
Mallikarjuna GB0ae2df82015-06-04 18:20:21 +0530498 Log.i(TAG, "CDMA calls in conference swapped, updating headset");
499 updateHeadsetWithCallState(true /* force */);
Santos Cordon88a4a602014-09-29 19:32:21 -0700500 return true;
501 } else if (ringingCall != null) {
Mallikarjuna GB8729c182015-06-04 18:50:53 +0530502 mCallsManager.answerCall(ringingCall, ringingCall.getVideoState());
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700503 return true;
504 } else if (heldCall != null) {
505 // CallsManager will hold any active calls when unhold() is called on a
506 // currently-held call.
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700507 mCallsManager.unholdCall(heldCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700508 return true;
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800509 } else if (activeCall != null && activeCall.can(Connection.CAPABILITY_HOLD)) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700510 mCallsManager.holdCall(activeCall);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700511 return true;
512 }
513 } else if (chld == CHLD_TYPE_ADDHELDTOCONF) {
514 if (activeCall != null) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800515 if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700516 activeCall.mergeConference();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700517 return true;
Santos Cordon88a4a602014-09-29 19:32:21 -0700518 } else {
519 List<Call> conferenceable = activeCall.getConferenceableCalls();
520 if (!conferenceable.isEmpty()) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700521 mCallsManager.conference(activeCall, conferenceable.get(0));
Santos Cordon88a4a602014-09-29 19:32:21 -0700522 return true;
Brad Ebinger53855132015-10-30 10:58:19 -0700523 }
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700524 }
525 }
526 }
527 return false;
528 }
529
530 private void enforceModifyPermission() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800531 mContext.enforceCallingOrSelfPermission(
532 android.Manifest.permission.MODIFY_PHONE_STATE, null);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700533 }
534
Santos Cordon88a4a602014-09-29 19:32:21 -0700535 private void sendListOfCalls(boolean shouldLog) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800536 Collection<Call> mCalls = mCallsManager.getCalls();
Santos Cordon33fff492014-09-25 14:57:01 -0700537 for (Call call : mCalls) {
538 // We don't send the parent conference call to the bluetooth device.
Tyler Gunn9365c272015-06-29 09:18:31 -0700539 // We do, however want to send conferences that have no children to the bluetooth
540 // device (e.g. IMS Conference).
541 if (!call.isConference() ||
542 (call.isConference() && call
543 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN))) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700544 sendClccForCall(call, shouldLog);
Santos Cordon33fff492014-09-25 14:57:01 -0700545 }
546 }
547 sendClccEndMarker();
548 }
549
550 /**
551 * Sends a single clcc (C* List Current Calls) event for the specified call.
552 */
Santos Cordon88a4a602014-09-29 19:32:21 -0700553 private void sendClccForCall(Call call, boolean shouldLog) {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800554 boolean isForeground = mCallsManager.getForegroundCall() == call;
Santos Cordon33fff492014-09-25 14:57:01 -0700555 int state = convertCallState(call.getState(), isForeground);
Santos Cordon88a4a602014-09-29 19:32:21 -0700556 boolean isPartOfConference = false;
Tyler Gunn9365c272015-06-29 09:18:31 -0700557 boolean isConferenceWithNoChildren = call.isConference() && call
558 .can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
Nancy Chen05a9e402014-09-26 14:14:32 -0700559
560 if (state == CALL_STATE_IDLE) {
561 return;
562 }
563
Santos Cordon88a4a602014-09-29 19:32:21 -0700564 Call conferenceCall = call.getParentCall();
565 if (conferenceCall != null) {
566 isPartOfConference = true;
567
568 // Run some alternative states for Conference-level merge/swap support.
569 // Basically, if call supports swapping or merging at the conference-level, then we need
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800570 // to expose the calls as having distinct states (ACTIVE vs CAPABILITY_HOLD) or the
571 // functionality won't show up on the bluetooth device.
Santos Cordon88a4a602014-09-29 19:32:21 -0700572
573 // Before doing any special logic, ensure that we are dealing with an ACTIVE call and
574 // that the conference itself has a notion of the current "active" child call.
575 Call activeChild = conferenceCall.getConferenceLevelActiveCall();
576 if (state == CALL_STATE_ACTIVE && activeChild != null) {
577 // Reevaluate state if we can MERGE or if we can SWAP without previously having
578 // MERGED.
579 boolean shouldReevaluateState =
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800580 conferenceCall.can(Connection.CAPABILITY_MERGE_CONFERENCE) ||
581 (conferenceCall.can(Connection.CAPABILITY_SWAP_CONFERENCE) &&
Brad Ebinger53855132015-10-30 10:58:19 -0700582 !conferenceCall.wasConferencePreviouslyMerged());
Santos Cordon88a4a602014-09-29 19:32:21 -0700583
584 if (shouldReevaluateState) {
585 isPartOfConference = false;
586 if (call == activeChild) {
587 state = CALL_STATE_ACTIVE;
588 } else {
589 // At this point we know there is an "active" child and we know that it is
590 // not this call, so set it to HELD instead.
591 state = CALL_STATE_HELD;
592 }
593 }
594 }
Tyler Gunn9365c272015-06-29 09:18:31 -0700595 } else if (isConferenceWithNoChildren) {
596 // Handle the special case of an IMS conference call without conference event package
597 // support. The call will be marked as a conference, but the conference will not have
598 // child calls where conference event packages are not used by the carrier.
599 isPartOfConference = true;
Santos Cordon88a4a602014-09-29 19:32:21 -0700600 }
601
Nancy Chen05a9e402014-09-26 14:14:32 -0700602 int index = getIndexForCall(call);
603 int direction = call.isIncoming() ? 1 : 0;
Yorke Leefb70e1e2014-09-29 14:22:53 -0700604 final Uri addressUri;
605 if (call.getGatewayInfo() != null) {
606 addressUri = call.getGatewayInfo().getOriginalAddress();
607 } else {
608 addressUri = call.getHandle();
609 }
Santos Cordon33fff492014-09-25 14:57:01 -0700610 String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
611 int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
612
Santos Cordon88a4a602014-09-29 19:32:21 -0700613 if (shouldLog) {
614 Log.i(this, "sending clcc for call %d, %d, %d, %b, %s, %d",
615 index, direction, state, isPartOfConference, Log.piiHandle(address),
616 addressType);
617 }
Yorke Leee241cd62014-10-09 13:41:19 -0700618
619 if (mBluetoothHeadset != null) {
620 mBluetoothHeadset.clccResponse(
621 index, direction, state, 0, isPartOfConference, address, addressType);
622 }
Santos Cordon33fff492014-09-25 14:57:01 -0700623 }
624
625 private void sendClccEndMarker() {
626 // End marker is recognized with an index value of 0. All other parameters are ignored.
Yorke Leee241cd62014-10-09 13:41:19 -0700627 if (mBluetoothHeadset != null) {
628 mBluetoothHeadset.clccResponse(0 /* index */, 0, 0, 0, false, null, 0);
629 }
Santos Cordon33fff492014-09-25 14:57:01 -0700630 }
631
632 /**
633 * Returns the caches index for the specified call. If no such index exists, then an index is
634 * given (smallest number starting from 1 that isn't already taken).
635 */
636 private int getIndexForCall(Call call) {
637 if (mClccIndexMap.containsKey(call)) {
638 return mClccIndexMap.get(call);
639 }
640
641 int i = 1; // Indexes for bluetooth clcc are 1-based.
642 while (mClccIndexMap.containsValue(i)) {
643 i++;
644 }
645
646 // NOTE: Indexes are removed in {@link #onCallRemoved}.
647 mClccIndexMap.put(call, i);
648 return i;
649 }
650
Tyler Gunncd685e52014-10-10 11:48:25 -0700651 /**
652 * Sends an update of the current call state to the current Headset.
653 *
654 * @param force {@code true} if the headset state should be sent regardless if no changes to the
655 * state have occurred, {@code false} if the state should only be sent if the state has
656 * changed.
657 */
658 private void updateHeadsetWithCallState(boolean force) {
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700659 Call activeCall = mCallsManager.getActiveCall();
660 Call ringingCall = mCallsManager.getRingingCall();
661 Call heldCall = mCallsManager.getHeldCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700662
663 int bluetoothCallState = getBluetoothCallStateForUpdate();
664
665 String ringingAddress = null;
666 int ringingAddressType = 128;
Santos Cordonecaaeac2014-11-05 20:59:04 -0800667 if (ringingCall != null && ringingCall.getHandle() != null) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700668 ringingAddress = ringingCall.getHandle().getSchemeSpecificPart();
669 if (ringingAddress != null) {
670 ringingAddressType = PhoneNumberUtils.toaFromString(ringingAddress);
671 }
672 }
673 if (ringingAddress == null) {
674 ringingAddress = "";
675 }
676
Santos Cordona0b46122014-09-29 14:20:21 -0700677 int numActiveCalls = activeCall == null ? 0 : 1;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700678 int numHeldCalls = mCallsManager.getNumHeldCalls();
Roshan Pius7d7cf272015-08-28 11:10:56 -0700679 // Intermediate state for GSM calls which are in the process of being swapped.
680 // TODO: Should we be hardcoding this value to 2 or should we check if all top level calls
681 // are held?
682 boolean callsPendingSwitch = (numHeldCalls == 2);
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700683
Santos Cordon88a4a602014-09-29 19:32:21 -0700684 // For conference calls which support swapping the active call within the conference
685 // (namely CDMA calls) we need to expose that as a held call in order for the BT device
686 // to show "swap" and "merge" functionality.
Yorke Lee720bcbe2014-10-22 18:09:15 -0700687 boolean ignoreHeldCallChange = false;
Tyler Gunn9365c272015-06-29 09:18:31 -0700688 if (activeCall != null && activeCall.isConference() &&
689 !activeCall.can(Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800690 if (activeCall.can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700691 // Indicate that BT device should show SWAP command by indicating that there is a
692 // call on hold, but only if the conference wasn't previously merged.
693 numHeldCalls = activeCall.wasConferencePreviouslyMerged() ? 0 : 1;
Ihab Awad07bc5ee2014-11-12 13:42:52 -0800694 } else if (activeCall.can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
Santos Cordon88a4a602014-09-29 19:32:21 -0700695 numHeldCalls = 1; // Merge is available, so expose via numHeldCalls.
696 }
Yorke Lee720bcbe2014-10-22 18:09:15 -0700697
698 for (Call childCall : activeCall.getChildCalls()) {
699 // Held call has changed due to it being combined into a CDMA conference. Keep
700 // track of this and ignore any future update since it doesn't really count as
701 // a call change.
702 if (mOldHeldCall == childCall) {
703 ignoreHeldCallChange = true;
704 break;
705 }
706 }
Santos Cordon88a4a602014-09-29 19:32:21 -0700707 }
708
Santos Cordona0b46122014-09-29 14:20:21 -0700709 if (mBluetoothHeadset != null &&
Roshan Pius7d7cf272015-08-28 11:10:56 -0700710 (force ||
711 (!callsPendingSwitch &&
712 (numActiveCalls != mNumActiveCalls ||
Brad Ebinger53855132015-10-30 10:58:19 -0700713 numHeldCalls != mNumHeldCalls ||
714 bluetoothCallState != mBluetoothCallState ||
715 !TextUtils.equals(ringingAddress, mRingingAddress) ||
716 ringingAddressType != mRingingAddressType ||
717 (heldCall != mOldHeldCall && !ignoreHeldCallChange))))) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700718
Santos Cordona0b46122014-09-29 14:20:21 -0700719 // If the call is transitioning into the alerting state, send DIALING first.
720 // Some devices expect to see a DIALING state prior to seeing an ALERTING state
721 // so we need to send it first.
722 boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState &&
723 bluetoothCallState == CALL_STATE_ALERTING;
724
Santos Cordonc0ca76e2014-10-21 15:54:19 -0700725 mOldHeldCall = heldCall;
Santos Cordona0b46122014-09-29 14:20:21 -0700726 mNumActiveCalls = numActiveCalls;
727 mNumHeldCalls = numHeldCalls;
728 mBluetoothCallState = bluetoothCallState;
729 mRingingAddress = ringingAddress;
730 mRingingAddressType = ringingAddressType;
731
732 if (sendDialingFirst) {
Yorke Lee720bcbe2014-10-22 18:09:15 -0700733 // Log in full to make logs easier to debug.
734 Log.i(TAG, "updateHeadsetWithCallState " +
735 "numActive %s, " +
736 "numHeld %s, " +
737 "callState %s, " +
738 "ringing number %s, " +
739 "ringing type %s",
740 mNumActiveCalls,
741 mNumHeldCalls,
742 CALL_STATE_DIALING,
743 Log.pii(mRingingAddress),
744 mRingingAddressType);
Santos Cordona0b46122014-09-29 14:20:21 -0700745 mBluetoothHeadset.phoneStateChanged(
746 mNumActiveCalls,
747 mNumHeldCalls,
748 CALL_STATE_DIALING,
749 mRingingAddress,
750 mRingingAddressType);
751 }
752
753 Log.i(TAG, "updateHeadsetWithCallState " +
754 "numActive %s, " +
755 "numHeld %s, " +
756 "callState %s, " +
757 "ringing number %s, " +
758 "ringing type %s",
759 mNumActiveCalls,
760 mNumHeldCalls,
761 mBluetoothCallState,
762 Log.pii(mRingingAddress),
763 mRingingAddressType);
764
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700765 mBluetoothHeadset.phoneStateChanged(
Santos Cordona0b46122014-09-29 14:20:21 -0700766 mNumActiveCalls,
767 mNumHeldCalls,
768 mBluetoothCallState,
769 mRingingAddress,
770 mRingingAddressType);
Santos Cordon88a4a602014-09-29 19:32:21 -0700771
772 mHeadsetUpdatedRecently = true;
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700773 }
774 }
775
776 private int getBluetoothCallStateForUpdate() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800777 CallsManager callsManager = mCallsManager;
Ihab Awad8d5d9dd2015-03-12 11:11:06 -0700778 Call ringingCall = mCallsManager.getRingingCall();
Roshan Pius7d7cf272015-08-28 11:10:56 -0700779 Call dialingCall = mCallsManager.getOutgoingCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700780
781 //
782 // !! WARNING !!
783 // You will note that CALL_STATE_WAITING, CALL_STATE_HELD, and CALL_STATE_ACTIVE are not
784 // used in this version of the call state mappings. This is on purpose.
785 // phone_state_change() in btif_hf.c is not written to handle these states. Only with the
786 // listCalls*() method are WAITING and ACTIVE used.
787 // Using the unsupported states here caused problems with inconsistent state in some
788 // bluetooth devices (like not getting out of ringing state after answering a call).
789 //
790 int bluetoothCallState = CALL_STATE_IDLE;
791 if (ringingCall != null) {
792 bluetoothCallState = CALL_STATE_INCOMING;
Nancy Chen05a9e402014-09-26 14:14:32 -0700793 } else if (dialingCall != null) {
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700794 bluetoothCallState = CALL_STATE_ALERTING;
795 }
796 return bluetoothCallState;
797 }
798
Santos Cordon33fff492014-09-25 14:57:01 -0700799 private int convertCallState(int callState, boolean isForegroundCall) {
800 switch (callState) {
801 case CallState.NEW:
802 case CallState.ABORTED:
803 case CallState.DISCONNECTED:
804 return CALL_STATE_IDLE;
805
806 case CallState.ACTIVE:
807 return CALL_STATE_ACTIVE;
808
Santos Cordond9f90062015-10-28 15:55:34 -0700809 case CallState.CONNECTING:
810 case CallState.SELECT_PHONE_ACCOUNT:
Santos Cordon33fff492014-09-25 14:57:01 -0700811 case CallState.DIALING:
812 // Yes, this is correctly returning ALERTING.
813 // "Dialing" for BT means that we have sent information to the service provider
814 // to place the call but there is no confirmation that the call is going through.
815 // When there finally is confirmation, the ringback is played which is referred to
816 // as an "alert" tone, thus, ALERTING.
817 // TODO: We should consider using the ALERTING terms in Telecom because that
818 // seems to be more industry-standard.
819 return CALL_STATE_ALERTING;
820
821 case CallState.ON_HOLD:
822 return CALL_STATE_HELD;
823
824 case CallState.RINGING:
825 if (isForegroundCall) {
826 return CALL_STATE_INCOMING;
827 } else {
828 return CALL_STATE_WAITING;
829 }
830 }
831 return CALL_STATE_IDLE;
832 }
833
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700834 /**
835 * Returns the best phone account to use for the given state of all calls.
836 * First, tries to return the phone account for the foreground call, second the default
837 * phone account for PhoneAccount.SCHEME_TEL.
838 */
839 private PhoneAccount getBestPhoneAccount() {
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800840 if (mPhoneAccountRegistrar == null) {
Santos Cordon0b5cb4d2014-12-02 02:40:10 -0800841 return null;
842 }
843
Ihab Awad78a5e6b2015-02-06 10:13:05 -0800844 Call call = mCallsManager.getForegroundCall();
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700845
846 PhoneAccount account = null;
847 if (call != null) {
848 // First try to get the network name of the foreground call.
Tony Mak240656f2015-12-04 11:36:22 +0000849 account = mPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
Santos Cordon6a212642015-05-08 16:35:23 -0700850 call.getTargetPhoneAccount());
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700851 }
852
853 if (account == null) {
854 // Second, Try to get the label for the default Phone Account.
Tony Mak240656f2015-12-04 11:36:22 +0000855 account = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
856 mPhoneAccountRegistrar.getOutgoingPhoneAccountForSchemeOfCurrentUser(
Brad Ebinger53855132015-10-30 10:58:19 -0700857 PhoneAccount.SCHEME_TEL));
Santos Cordon68d1a6b2014-09-19 12:25:58 -0700858 }
859 return account;
860 }
861}