• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.telecom;
18 
19 import android.app.ActivityManager;
20 import android.app.NotificationManager;
21 import android.content.Context;
22 import android.content.pm.UserInfo;
23 import android.content.Intent;
24 import android.media.AudioManager;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.Process;
30 import android.os.SystemClock;
31 import android.os.SystemProperties;
32 import android.os.SystemVibrator;
33 import android.os.Trace;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.provider.CallLog.Calls;
37 import android.provider.Settings;
38 import android.telecom.CallAudioState;
39 import android.telecom.Conference;
40 import android.telecom.Connection;
41 import android.telecom.DisconnectCause;
42 import android.telecom.GatewayInfo;
43 import android.telecom.ParcelableConference;
44 import android.telecom.ParcelableConnection;
45 import android.telecom.PhoneAccount;
46 import android.telecom.PhoneAccountHandle;
47 import android.telecom.TelecomManager;
48 import android.telecom.VideoProfile;
49 import android.telephony.PhoneNumberUtils;
50 import android.telephony.TelephonyManager;
51 import android.text.TextUtils;
52 
53 import com.android.internal.annotations.VisibleForTesting;
54 import com.android.internal.telephony.AsyncEmergencyContactNotifier;
55 import com.android.internal.telephony.PhoneConstants;
56 import com.android.internal.telephony.TelephonyProperties;
57 import com.android.internal.util.IndentingPrintWriter;
58 import com.android.server.telecom.TelecomServiceImpl.DefaultDialerManagerAdapter;
59 import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
60 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
61 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
62 import com.android.server.telecom.callfiltering.CallFilteringResult;
63 import com.android.server.telecom.callfiltering.CallScreeningServiceFilter;
64 import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
65 import com.android.server.telecom.callfiltering.IncomingCallFilter;
66 import com.android.server.telecom.components.ErrorDialogActivity;
67 
68 import java.util.ArrayList;
69 import java.util.Collection;
70 import java.util.Collections;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.Iterator;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.Objects;
77 import java.util.Optional;
78 import java.util.Set;
79 import java.util.concurrent.ConcurrentHashMap;
80 
81 /**
82  * Singleton.
83  *
84  * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
85  * access from other packages specifically refraining from passing the CallsManager instance
86  * beyond the com.android.server.telecom package boundary.
87  */
88 @VisibleForTesting
89 public class CallsManager extends Call.ListenerBase
90         implements VideoProviderProxy.Listener, CallFilterResultCallback {
91 
92     // TODO: Consider renaming this CallsManagerPlugin.
93     @VisibleForTesting
94     public interface CallsManagerListener {
onCallAdded(Call call)95         void onCallAdded(Call call);
onCallRemoved(Call call)96         void onCallRemoved(Call call);
onCallStateChanged(Call call, int oldState, int newState)97         void onCallStateChanged(Call call, int oldState, int newState);
onConnectionServiceChanged( Call call, ConnectionServiceWrapper oldService, ConnectionServiceWrapper newService)98         void onConnectionServiceChanged(
99                 Call call,
100                 ConnectionServiceWrapper oldService,
101                 ConnectionServiceWrapper newService);
onIncomingCallAnswered(Call call)102         void onIncomingCallAnswered(Call call);
onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)103         void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState)104         void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState);
onRingbackRequested(Call call, boolean ringback)105         void onRingbackRequested(Call call, boolean ringback);
onIsConferencedChanged(Call call)106         void onIsConferencedChanged(Call call);
onIsVoipAudioModeChanged(Call call)107         void onIsVoipAudioModeChanged(Call call);
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)108         void onVideoStateChanged(Call call, int previousVideoState, int newVideoState);
onCanAddCallChanged(boolean canAddCall)109         void onCanAddCallChanged(boolean canAddCall);
onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)110         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
onHoldToneRequested(Call call)111         void onHoldToneRequested(Call call);
onExternalCallChanged(Call call, boolean isExternalCall)112         void onExternalCallChanged(Call call, boolean isExternalCall);
113     }
114 
115     private static final String TAG = "CallsManager";
116 
117     private static final int MAXIMUM_LIVE_CALLS = 1;
118     private static final int MAXIMUM_HOLD_CALLS = 1;
119     private static final int MAXIMUM_RINGING_CALLS = 1;
120     private static final int MAXIMUM_DIALING_CALLS = 1;
121     private static final int MAXIMUM_OUTGOING_CALLS = 1;
122     private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
123 
124     private static final int[] OUTGOING_CALL_STATES =
125             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
126                     CallState.PULLING};
127 
128     private static final int[] LIVE_CALL_STATES =
129             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
130                     CallState.PULLING, CallState.ACTIVE};
131 
132     public static final String TELECOM_CALL_ID_PREFIX = "TC@";
133 
134     // Maps call technologies in PhoneConstants to those in Analytics.
135     private static final Map<Integer, Integer> sAnalyticsTechnologyMap;
136     static {
137         sAnalyticsTechnologyMap = new HashMap<>(5);
sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE)138         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE);
sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE)139         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE);
sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE)140         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE);
sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE)141         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE);
sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY, Analytics.THIRD_PARTY_PHONE)142         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY,
143                 Analytics.THIRD_PARTY_PHONE);
144     }
145 
146     /**
147      * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
148      * calls are added to the map and removed when the calls move to the disconnected state.
149      *
150      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
151      * load factor before resizing, 1 means we only expect a single thread to
152      * access the map so make only a single shard
153      */
154     private final Set<Call> mCalls = Collections.newSetFromMap(
155             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
156 
157     /**
158      * The current telecom call ID.  Used when creating new instances of {@link Call}.  Should
159      * only be accessed using the {@link #getNextCallId()} method which synchronizes on the
160      * {@link #mLock} sync root.
161      */
162     private int mCallId = 0;
163 
164     /**
165      * Stores the current foreground user.
166      */
167     private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
168 
169     private final ConnectionServiceRepository mConnectionServiceRepository;
170     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
171     private final InCallController mInCallController;
172     private final CallAudioManager mCallAudioManager;
173     private RespondViaSmsManager mRespondViaSmsManager;
174     private final Ringer mRinger;
175     private final InCallWakeLockController mInCallWakeLockController;
176     // For this set initial table size to 16 because we add 13 listeners in
177     // the CallsManager constructor.
178     private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
179             new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
180     private final HeadsetMediaButton mHeadsetMediaButton;
181     private final WiredHeadsetManager mWiredHeadsetManager;
182     private final BluetoothManager mBluetoothManager;
183     private final DockManager mDockManager;
184     private final TtyManager mTtyManager;
185     private final ProximitySensorManager mProximitySensorManager;
186     private final PhoneStateBroadcaster mPhoneStateBroadcaster;
187     private final CallLogManager mCallLogManager;
188     private final Context mContext;
189     private final TelecomSystem.SyncRoot mLock;
190     private final ContactsAsyncHelper mContactsAsyncHelper;
191     private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
192     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
193     private final MissedCallNotifier mMissedCallNotifier;
194     private final CallerInfoLookupHelper mCallerInfoLookupHelper;
195     private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
196     private final Timeouts.Adapter mTimeoutsAdapter;
197     private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
198     private final NotificationManager mNotificationManager;
199     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
200     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
201     /* Handler tied to thread in which CallManager was initialized. */
202     private final Handler mHandler = new Handler(Looper.getMainLooper());
203 
204     private boolean mCanAddCall = true;
205 
206     private TelephonyManager.MultiSimVariants mRadioSimVariants = null;
207 
208     private Runnable mStopTone;
209 
210     /**
211      * Initializes the required Telecom components.
212      */
CallsManager( Context context, TelecomSystem.SyncRoot lock, ContactsAsyncHelper contactsAsyncHelper, CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar, HeadsetMediaButtonFactory headsetMediaButtonFactory, ProximitySensorManagerFactory proximitySensorManagerFactory, InCallWakeLockControllerFactory inCallWakeLockControllerFactory, CallAudioManager.AudioServiceFactory audioServiceFactory, BluetoothManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, SystemStateProvider systemStateProvider, DefaultDialerManagerAdapter defaultDialerAdapter, Timeouts.Adapter timeoutsAdapter, AsyncRingtonePlayer asyncRingtonePlayer, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, InterruptionFilterProxy interruptionFilterProxy)213     CallsManager(
214             Context context,
215             TelecomSystem.SyncRoot lock,
216             ContactsAsyncHelper contactsAsyncHelper,
217             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
218             MissedCallNotifier missedCallNotifier,
219             PhoneAccountRegistrar phoneAccountRegistrar,
220             HeadsetMediaButtonFactory headsetMediaButtonFactory,
221             ProximitySensorManagerFactory proximitySensorManagerFactory,
222             InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
223             CallAudioManager.AudioServiceFactory audioServiceFactory,
224             BluetoothManager bluetoothManager,
225             WiredHeadsetManager wiredHeadsetManager,
226             SystemStateProvider systemStateProvider,
227             DefaultDialerManagerAdapter defaultDialerAdapter,
228             Timeouts.Adapter timeoutsAdapter,
229             AsyncRingtonePlayer asyncRingtonePlayer,
230             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
231             InterruptionFilterProxy interruptionFilterProxy) {
232         mContext = context;
233         mLock = lock;
234         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
235         mContactsAsyncHelper = contactsAsyncHelper;
236         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
237         mPhoneAccountRegistrar = phoneAccountRegistrar;
238         mMissedCallNotifier = missedCallNotifier;
239         StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
240         mWiredHeadsetManager = wiredHeadsetManager;
241         mBluetoothManager = bluetoothManager;
242         mDefaultDialerManagerAdapter = defaultDialerAdapter;
243         mDockManager = new DockManager(context);
244         mTimeoutsAdapter = timeoutsAdapter;
245         mCallerInfoLookupHelper = new CallerInfoLookupHelper(context, mCallerInfoAsyncQueryFactory,
246                 mContactsAsyncHelper, mLock);
247 
248         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer();
249         mNotificationManager = (NotificationManager) context.getSystemService(
250                 Context.NOTIFICATION_SERVICE);
251         CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
252                 context,
253                 this,
254                 bluetoothManager,
255                 wiredHeadsetManager,
256                 statusBarNotifier,
257                 audioServiceFactory,
258                 interruptionFilterProxy,
259                 CallAudioRouteStateMachine.doesDeviceSupportEarpieceRoute()
260         );
261         callAudioRouteStateMachine.initialize();
262 
263         CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter =
264                 new CallAudioRoutePeripheralAdapter(
265                         callAudioRouteStateMachine,
266                         bluetoothManager,
267                         wiredHeadsetManager,
268                         mDockManager);
269 
270         InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(
271                 callAudioRoutePeripheralAdapter, lock);
272 
273         SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
274         RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
275         SystemVibrator systemVibrator = new SystemVibrator(context);
276         mInCallController = new InCallController(
277                 context, mLock, this, systemStateProvider, defaultDialerAdapter, mTimeoutsAdapter);
278         mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
279                 ringtoneFactory, systemVibrator, mInCallController);
280 
281         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
282                 this,new CallAudioModeStateMachine((AudioManager)
283                         mContext.getSystemService(Context.AUDIO_SERVICE)),
284                 playerFactory, mRinger, new RingbackPlayer(playerFactory), mDtmfLocalTonePlayer);
285 
286         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
287         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
288         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
289         mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
290         mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
291         mConnectionServiceRepository =
292                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
293         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
294 
295         mListeners.add(mInCallWakeLockController);
296         mListeners.add(statusBarNotifier);
297         mListeners.add(mCallLogManager);
298         mListeners.add(mPhoneStateBroadcaster);
299         mListeners.add(mInCallController);
300         mListeners.add(mCallAudioManager);
301         mListeners.add(missedCallNotifier);
302         mListeners.add(mHeadsetMediaButton);
303         mListeners.add(mProximitySensorManager);
304 
305         // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
306         final UserManager userManager = UserManager.get(mContext);
307         // Don't load missed call if it is run in split user model.
308         if (userManager.isPrimaryUser()) {
309             onUserSwitch(Process.myUserHandle());
310         }
311     }
312 
setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager)313     public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
314         if (mRespondViaSmsManager != null) {
315             mListeners.remove(mRespondViaSmsManager);
316         }
317         mRespondViaSmsManager = respondViaSmsManager;
318         mListeners.add(respondViaSmsManager);
319     }
320 
getRespondViaSmsManager()321     public RespondViaSmsManager getRespondViaSmsManager() {
322         return mRespondViaSmsManager;
323     }
324 
getCallerInfoLookupHelper()325     public CallerInfoLookupHelper getCallerInfoLookupHelper() {
326         return mCallerInfoLookupHelper;
327     }
328 
329     @Override
onSuccessfulOutgoingCall(Call call, int callState)330     public void onSuccessfulOutgoingCall(Call call, int callState) {
331         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
332 
333         setCallState(call, callState, "successful outgoing call");
334         if (!mCalls.contains(call)) {
335             // Call was not added previously in startOutgoingCall due to it being a potential MMI
336             // code, so add it now.
337             addCall(call);
338         }
339 
340         // The call's ConnectionService has been updated.
341         for (CallsManagerListener listener : mListeners) {
342             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
343         }
344 
345         markCallAsDialing(call);
346     }
347 
348     @Override
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)349     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
350         Log.v(this, "onFailedOutgoingCall, call: %s", call);
351 
352         markCallAsRemoved(call);
353     }
354 
355     @Override
onSuccessfulIncomingCall(Call incomingCall)356     public void onSuccessfulIncomingCall(Call incomingCall) {
357         Log.d(this, "onSuccessfulIncomingCall");
358         if (incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE)) {
359             Log.i(this, "Skipping call filtering due to ECBM");
360             onCallFilteringComplete(incomingCall, new CallFilteringResult(true, false, true, true));
361             return;
362         }
363 
364         List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();
365         filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));
366         filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter()));
367         filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar,
368                 mDefaultDialerManagerAdapter,
369                 new ParcelableCallUtils.Converter(), mLock));
370         new IncomingCallFilter(mContext, this, incomingCall, mLock,
371                 mTimeoutsAdapter, filters).performFiltering();
372     }
373 
374     @Override
onCallFilteringComplete(Call incomingCall, CallFilteringResult result)375     public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) {
376         // Only set the incoming call as ringing if it isn't already disconnected. It is possible
377         // that the connection service disconnected the call before it was even added to Telecom, in
378         // which case it makes no sense to set it back to a ringing state.
379         if (incomingCall.getState() != CallState.DISCONNECTED &&
380                 incomingCall.getState() != CallState.DISCONNECTING) {
381             setCallState(incomingCall, CallState.RINGING,
382                     result.shouldAllowCall ? "successful incoming call" : "blocking call");
383         } else {
384             Log.i(this, "onCallFilteringCompleted: call already disconnected.");
385             return;
386         }
387 
388         if (result.shouldAllowCall) {
389             if (hasMaximumRingingCalls()) {
390                 if (shouldSilenceInsteadOfReject(incomingCall)) {
391                     incomingCall.silence();
392                 } else {
393                     Log.i(this, "onCallFilteringCompleted: Call rejected! " +
394                             "Exceeds maximum number of ringing calls.");
395                     rejectCallAndLog(incomingCall);
396                 }
397             } else if (hasMaximumDialingCalls()) {
398                 Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " +
399                         "dialing calls.");
400                 rejectCallAndLog(incomingCall);
401             } else {
402                 addCall(incomingCall);
403             }
404         } else {
405             if (result.shouldReject) {
406                 Log.i(this, "onCallFilteringCompleted: blocked call, rejecting.");
407                 incomingCall.reject(false, null);
408             }
409             if (result.shouldAddToCallLog) {
410                 Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log.");
411                 if (result.shouldShowNotification) {
412                     Log.w(this, "onCallScreeningCompleted: blocked call, showing notification.");
413                 }
414                 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
415                         result.shouldShowNotification);
416             } else if (result.shouldShowNotification) {
417                 Log.i(this, "onCallScreeningCompleted: blocked call, showing notification.");
418                 mMissedCallNotifier.showMissedCallNotification(incomingCall);
419             }
420         }
421     }
422 
423     /**
424      * Whether allow (silence rather than reject) the incoming call if it has a different source
425      * (connection service) from the existing ringing call when reaching maximum ringing calls.
426      */
shouldSilenceInsteadOfReject(Call incomingCall)427     private boolean shouldSilenceInsteadOfReject(Call incomingCall) {
428         if (!mContext.getResources().getBoolean(
429                 R.bool.silence_incoming_when_different_service_and_maximum_ringing)) {
430             return false;
431         }
432 
433         Call ringingCall = null;
434 
435         for (Call call : mCalls) {
436             // Only operate on top-level calls
437             if (call.getParentCall() != null) {
438                 continue;
439             }
440 
441             if (call.isExternalCall()) {
442                 continue;
443             }
444 
445             if (CallState.RINGING == call.getState() &&
446                     call.getConnectionService() == incomingCall.getConnectionService()) {
447                 return false;
448             }
449         }
450 
451         return true;
452     }
453 
454     @Override
onFailedIncomingCall(Call call)455     public void onFailedIncomingCall(Call call) {
456         setCallState(call, CallState.DISCONNECTED, "failed incoming call");
457         call.removeListener(this);
458     }
459 
460     @Override
onSuccessfulUnknownCall(Call call, int callState)461     public void onSuccessfulUnknownCall(Call call, int callState) {
462         setCallState(call, callState, "successful unknown call");
463         Log.i(this, "onSuccessfulUnknownCall for call %s", call);
464         addCall(call);
465     }
466 
467     @Override
onFailedUnknownCall(Call call)468     public void onFailedUnknownCall(Call call) {
469         Log.i(this, "onFailedUnknownCall for call %s", call);
470         setCallState(call, CallState.DISCONNECTED, "failed unknown call");
471         call.removeListener(this);
472     }
473 
474     @Override
onRingbackRequested(Call call, boolean ringback)475     public void onRingbackRequested(Call call, boolean ringback) {
476         for (CallsManagerListener listener : mListeners) {
477             listener.onRingbackRequested(call, ringback);
478         }
479     }
480 
481     @Override
onPostDialWait(Call call, String remaining)482     public void onPostDialWait(Call call, String remaining) {
483         mInCallController.onPostDialWait(call, remaining);
484     }
485 
486     @Override
onPostDialChar(final Call call, char nextChar)487     public void onPostDialChar(final Call call, char nextChar) {
488         if (PhoneNumberUtils.is12Key(nextChar)) {
489             // Play tone if it is one of the dialpad digits, canceling out the previously queued
490             // up stopTone runnable since playing a new tone automatically stops the previous tone.
491             if (mStopTone != null) {
492                 mHandler.removeCallbacks(mStopTone.getRunnableToCancel());
493                 mStopTone.cancel();
494             }
495 
496             mDtmfLocalTonePlayer.playTone(call, nextChar);
497 
498             mStopTone = new Runnable("CM.oPDC", mLock) {
499                 @Override
500                 public void loggedRun() {
501                     // Set a timeout to stop the tone in case there isn't another tone to
502                     // follow.
503                     mDtmfLocalTonePlayer.stopTone(call);
504                 }
505             };
506             mHandler.postDelayed(mStopTone.prepare(),
507                     Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
508         } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
509                 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
510             // Stop the tone if a tone is playing, removing any other stopTone callbacks since
511             // the previous tone is being stopped anyway.
512             if (mStopTone != null) {
513                 mHandler.removeCallbacks(mStopTone.getRunnableToCancel());
514                 mStopTone.cancel();
515             }
516             mDtmfLocalTonePlayer.stopTone(call);
517         } else {
518             Log.w(this, "onPostDialChar: invalid value %d", nextChar);
519         }
520     }
521 
522     @Override
onParentChanged(Call call)523     public void onParentChanged(Call call) {
524         // parent-child relationship affects which call should be foreground, so do an update.
525         updateCanAddCall();
526         for (CallsManagerListener listener : mListeners) {
527             listener.onIsConferencedChanged(call);
528         }
529     }
530 
531     @Override
onChildrenChanged(Call call)532     public void onChildrenChanged(Call call) {
533         // parent-child relationship affects which call should be foreground, so do an update.
534         updateCanAddCall();
535         for (CallsManagerListener listener : mListeners) {
536             listener.onIsConferencedChanged(call);
537         }
538     }
539 
540     @Override
onIsVoipAudioModeChanged(Call call)541     public void onIsVoipAudioModeChanged(Call call) {
542         for (CallsManagerListener listener : mListeners) {
543             listener.onIsVoipAudioModeChanged(call);
544         }
545     }
546 
547     @Override
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)548     public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
549         for (CallsManagerListener listener : mListeners) {
550             listener.onVideoStateChanged(call, previousVideoState, newVideoState);
551         }
552     }
553 
554     @Override
onCanceledViaNewOutgoingCallBroadcast(final Call call, long disconnectionTimeout)555     public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call,
556             long disconnectionTimeout) {
557         mPendingCallsToDisconnect.add(call);
558         mHandler.postDelayed(new Runnable("CM.oCVNOCB", mLock) {
559             @Override
560             public void loggedRun() {
561                 if (mPendingCallsToDisconnect.remove(call)) {
562                     Log.i(this, "Delayed disconnection of call: %s", call);
563                     call.disconnect();
564                 }
565             }
566         }.prepare(), disconnectionTimeout);
567 
568         return true;
569     }
570 
571     /**
572      * Handles changes to the {@link Connection.VideoProvider} for a call.  Adds the
573      * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created
574      * in {@link Call#setVideoProvider(IVideoProvider)}.  This allows the {@link CallsManager} to
575      * respond to callbacks from the {@link VideoProviderProxy}.
576      *
577      * @param call The call.
578      */
579     @Override
onVideoCallProviderChanged(Call call)580     public void onVideoCallProviderChanged(Call call) {
581         VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy();
582 
583         if (videoProviderProxy == null) {
584             return;
585         }
586 
587         videoProviderProxy.addListener(this);
588     }
589 
590     /**
591      * Handles session modification requests received via the {@link TelecomVideoCallCallback} for
592      * a call.  Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session
593      * modification request.
594      *
595      * @param call The call.
596      * @param videoProfile The {@link VideoProfile}.
597      */
598     @Override
onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)599     public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
600         int videoState = videoProfile != null ? videoProfile.getVideoState() :
601                 VideoProfile.STATE_AUDIO_ONLY;
602         Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
603                 .videoStateToString(videoState));
604 
605         for (CallsManagerListener listener : mListeners) {
606             listener.onSessionModifyRequestReceived(call, videoProfile);
607         }
608     }
609 
getCalls()610     public Collection<Call> getCalls() {
611         return Collections.unmodifiableCollection(mCalls);
612     }
613 
614     /**
615      * Play or stop a call hold tone for a call.  Triggered via
616      * {@link Connection#sendConnectionEvent(String)} when the
617      * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
618      * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
619      *
620      * @param call The call which requested the hold tone.
621      */
622     @Override
onHoldToneRequested(Call call)623     public void onHoldToneRequested(Call call) {
624         for (CallsManagerListener listener : mListeners) {
625             listener.onHoldToneRequested(call);
626         }
627     }
628 
629     @VisibleForTesting
getForegroundCall()630     public Call getForegroundCall() {
631         if (mCallAudioManager == null) {
632             // Happens when getForegroundCall is called before full initialization.
633             return null;
634         }
635         return mCallAudioManager.getForegroundCall();
636     }
637 
getCurrentUserHandle()638     public UserHandle getCurrentUserHandle() {
639         return mCurrentUserHandle;
640     }
641 
getCallAudioManager()642     public CallAudioManager getCallAudioManager() {
643         return mCallAudioManager;
644     }
645 
getInCallController()646     InCallController getInCallController() {
647         return mInCallController;
648     }
649 
650     @VisibleForTesting
hasEmergencyCall()651     public boolean hasEmergencyCall() {
652         for (Call call : mCalls) {
653             if (call.isEmergencyCall()) {
654                 return true;
655             }
656         }
657         return false;
658     }
659 
hasOnlyDisconnectedCalls()660     boolean hasOnlyDisconnectedCalls() {
661         for (Call call : mCalls) {
662             if (!call.isDisconnected()) {
663                 return false;
664             }
665         }
666         return true;
667     }
668 
hasVideoCall()669     boolean hasVideoCall() {
670         for (Call call : mCalls) {
671             if (VideoProfile.isVideo(call.getVideoState())) {
672                 return true;
673             }
674         }
675         return false;
676     }
677 
678     @VisibleForTesting
getAudioState()679     public CallAudioState getAudioState() {
680         return mCallAudioManager.getCallAudioState();
681     }
682 
isTtySupported()683     boolean isTtySupported() {
684         return mTtyManager.isTtySupported();
685     }
686 
getCurrentTtyMode()687     int getCurrentTtyMode() {
688         return mTtyManager.getCurrentTtyMode();
689     }
690 
691     @VisibleForTesting
addListener(CallsManagerListener listener)692     public void addListener(CallsManagerListener listener) {
693         mListeners.add(listener);
694     }
695 
removeListener(CallsManagerListener listener)696     void removeListener(CallsManagerListener listener) {
697         mListeners.remove(listener);
698     }
699 
700     /**
701      * Starts the process to attach the call to a connection service.
702      *
703      * @param phoneAccountHandle The phone account which contains the component name of the
704      *        connection service to use for this call.
705      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
706      */
processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras)707     void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
708         Log.d(this, "processIncomingCallIntent");
709         Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
710         if (handle == null) {
711             // Required for backwards compatibility
712             handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
713         }
714         Call call = new Call(
715                 getNextCallId(),
716                 mContext,
717                 this,
718                 mLock,
719                 mConnectionServiceRepository,
720                 mContactsAsyncHelper,
721                 mCallerInfoAsyncQueryFactory,
722                 mPhoneNumberUtilsAdapter,
723                 handle,
724                 null /* gatewayInfo */,
725                 null /* connectionManagerPhoneAccount */,
726                 phoneAccountHandle,
727                 Call.CALL_DIRECTION_INCOMING /* callDirection */,
728                 false /* forceAttachToExistingConnection */,
729                 false /* isConference */
730         );
731 
732         call.initAnalytics();
733         if (getForegroundCall() != null) {
734             getForegroundCall().getAnalytics().setCallIsInterrupted(true);
735             call.getAnalytics().setCallIsAdditional(true);
736         }
737 
738         setIntentExtrasAndStartTime(call, extras);
739         // TODO: Move this to be a part of addCall()
740         call.addListener(this);
741         call.startCreateConnection(mPhoneAccountRegistrar);
742     }
743 
addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras)744     void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
745         Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
746         Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
747         Call call = new Call(
748                 getNextCallId(),
749                 mContext,
750                 this,
751                 mLock,
752                 mConnectionServiceRepository,
753                 mContactsAsyncHelper,
754                 mCallerInfoAsyncQueryFactory,
755                 mPhoneNumberUtilsAdapter,
756                 handle,
757                 null /* gatewayInfo */,
758                 null /* connectionManagerPhoneAccount */,
759                 phoneAccountHandle,
760                 Call.CALL_DIRECTION_UNKNOWN /* callDirection */,
761                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
762                 // to the existing connection instead of trying to create a new one.
763                 true /* forceAttachToExistingConnection */,
764                 false /* isConference */
765         );
766         call.initAnalytics();
767 
768         setIntentExtrasAndStartTime(call, extras);
769         call.addListener(this);
770         call.startCreateConnection(mPhoneAccountRegistrar);
771     }
772 
areHandlesEqual(Uri handle1, Uri handle2)773     private boolean areHandlesEqual(Uri handle1, Uri handle2) {
774         if (handle1 == null || handle2 == null) {
775             return handle1 == handle2;
776         }
777 
778         if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) {
779             return false;
780         }
781 
782         final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart());
783         final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart());
784         return TextUtils.equals(number1, number2);
785     }
786 
reuseOutgoingCall(Uri handle)787     private Call reuseOutgoingCall(Uri handle) {
788         // Check to see if we can reuse any of the calls that are waiting to disconnect.
789         // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
790         Call reusedCall = null;
791         for (Iterator<Call> callIter = mPendingCallsToDisconnect.iterator(); callIter.hasNext();) {
792             Call pendingCall = callIter.next();
793             if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) {
794                 callIter.remove();
795                 Log.i(this, "Reusing disconnected call %s", pendingCall);
796                 reusedCall = pendingCall;
797             } else {
798                 Log.i(this, "Not reusing disconnected call %s", pendingCall);
799                 pendingCall.disconnect();
800             }
801         }
802 
803         return reusedCall;
804     }
805 
806     /**
807      * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
808      *
809      * @param handle Handle to connect the call with.
810      * @param phoneAccountHandle The phone account which contains the component name of the
811      *        connection service to use for this call.
812      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
813      * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
814      */
startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras, UserHandle initiatingUser)815     Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
816             UserHandle initiatingUser) {
817         boolean isReusedCall = true;
818         Call call = reuseOutgoingCall(handle);
819 
820         // Create a call with original handle. The handle may be changed when the call is attached
821         // to a connection service, but in most cases will remain the same.
822         if (call == null) {
823             call = new Call(getNextCallId(), mContext,
824                     this,
825                     mLock,
826                     mConnectionServiceRepository,
827                     mContactsAsyncHelper,
828                     mCallerInfoAsyncQueryFactory,
829                     mPhoneNumberUtilsAdapter,
830                     handle,
831                     null /* gatewayInfo */,
832                     null /* connectionManagerPhoneAccount */,
833                     null /* phoneAccountHandle */,
834                     Call.CALL_DIRECTION_OUTGOING /* callDirection */,
835                     false /* forceAttachToExistingConnection */,
836                     false /* isConference */
837             );
838             call.initAnalytics();
839 
840             call.setInitiatingUser(initiatingUser);
841 
842             isReusedCall = false;
843         }
844 
845         // Set the video state on the call early so that when it is added to the InCall UI the UI
846         // knows to configure itself as a video call immediately.
847         if (extras != null) {
848             int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
849                     VideoProfile.STATE_AUDIO_ONLY);
850 
851             // If this is an emergency video call, we need to check if the phone account supports
852             // emergency video calling.
853             // Also, ensure we don't try to place an outgoing call with video if video is not
854             // supported.
855             if (VideoProfile.isVideo(videoState)) {
856                 PhoneAccount account =
857                         mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
858 
859                 if (call.isEmergencyCall() && account != null &&
860                         !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
861                     // Phone account doesn't support emergency video calling, so fallback to
862                     // audio-only now to prevent the InCall UI from setting up video surfaces
863                     // needlessly.
864                     Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
865                             "falling back to audio-only");
866                     videoState = VideoProfile.STATE_AUDIO_ONLY;
867                 } else if (account != null &&
868                         !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) {
869                     // Phone account doesn't support video calling, so fallback to audio-only.
870                     Log.i(this, "startOutgoingCall - video calls not supported; fallback to " +
871                             "audio-only.");
872                     videoState = VideoProfile.STATE_AUDIO_ONLY;
873                 }
874             }
875 
876             call.setVideoState(videoState);
877         }
878 
879         List<PhoneAccountHandle> accounts = constructPossiblePhoneAccounts(handle, initiatingUser);
880         Log.v(this, "startOutgoingCall found accounts = " + accounts);
881 
882         // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
883         // as if a phoneAccount was not specified (does the default behavior instead).
884         // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
885         if (phoneAccountHandle != null) {
886             if (!accounts.contains(phoneAccountHandle)) {
887                 phoneAccountHandle = null;
888             }
889         }
890 
891         if (phoneAccountHandle == null && accounts.size() > 0) {
892             // No preset account, check if default exists that supports the URI scheme for the
893             // handle and verify it can be used.
894             if(accounts.size() > 1) {
895                 PhoneAccountHandle defaultPhoneAccountHandle =
896                         mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(),
897                                 initiatingUser);
898                 if (defaultPhoneAccountHandle != null &&
899                         accounts.contains(defaultPhoneAccountHandle)) {
900                     phoneAccountHandle = defaultPhoneAccountHandle;
901                 }
902             } else {
903                 // Use the only PhoneAccount that is available
904                 phoneAccountHandle = accounts.get(0);
905             }
906 
907         }
908 
909         call.setTargetPhoneAccount(phoneAccountHandle);
910 
911         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
912 
913         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
914         // a call, or cancel this call altogether. If a call is being reused, then it has already
915         // passed the makeRoomForOutgoingCall check once and will fail the second time due to the
916         // call transitioning into the CONNECTING state.
917         if (!isPotentialInCallMMICode && (!isReusedCall &&
918                 !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
919             // just cancel at this point.
920             Log.i(this, "No remaining room for outgoing call: %s", call);
921             if (mCalls.contains(call)) {
922                 // This call can already exist if it is a reused call,
923                 // See {@link #reuseOutgoingCall}.
924                 call.disconnect();
925             }
926             return null;
927         }
928 
929         boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
930                 !call.isEmergencyCall();
931 
932         if (needsAccountSelection) {
933             // This is the state where the user is expected to select an account
934             call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
935             // Create our own instance to modify (since extras may be Bundle.EMPTY)
936             extras = new Bundle(extras);
937             extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
938         } else {
939             call.setState(
940                     CallState.CONNECTING,
941                     phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
942         }
943 
944         setIntentExtrasAndStartTime(call, extras);
945 
946         // Do not add the call if it is a potential MMI code.
947         if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
948             call.addListener(this);
949         } else if (!mCalls.contains(call)) {
950             // We check if mCalls already contains the call because we could potentially be reusing
951             // a call which was previously added (See {@link #reuseOutgoingCall}).
952             addCall(call);
953         }
954 
955         return call;
956     }
957 
958     /**
959      * Attempts to issue/connect the specified call.
960      *
961      * @param handle Handle to connect the call with.
962      * @param gatewayInfo Optional gateway information that can be used to route the call to the
963      *        actual dialed handle via a gateway provider. May be null.
964      * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
965      * @param videoState The desired video state for the outgoing call.
966      */
967     @VisibleForTesting
placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)968     public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
969             boolean speakerphoneOn, int videoState) {
970         if (call == null) {
971             // don't do anything if the call no longer exists
972             Log.i(this, "Canceling unknown call.");
973             return;
974         }
975 
976         final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
977 
978         if (gatewayInfo == null) {
979             Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
980         } else {
981             Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
982                     Log.pii(uriHandle), Log.pii(handle));
983         }
984 
985         call.setHandle(uriHandle);
986         call.setGatewayInfo(gatewayInfo);
987 
988         final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
989                 R.bool.use_speaker_when_docked);
990         final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock();
991         final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState);
992 
993         // Auto-enable speakerphone if the originating intent specified to do so, if the call
994         // is a video call, of if using speaker when docked
995         call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
996                 || (useSpeakerWhenDocked && useSpeakerForDock));
997         call.setVideoState(videoState);
998 
999         if (speakerphoneOn) {
1000             Log.i(this, "%s Starting with speakerphone as requested", call);
1001         } else if (useSpeakerWhenDocked && useSpeakerForDock) {
1002             Log.i(this, "%s Starting with speakerphone because car is docked.", call);
1003         } else if (useSpeakerForVideoCall) {
1004             Log.i(this, "%s Starting with speakerphone because its a video call.", call);
1005         }
1006 
1007         if (call.isEmergencyCall()) {
1008             new AsyncEmergencyContactNotifier(mContext).execute();
1009         }
1010 
1011         final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
1012                 com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
1013 
1014         if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
1015             // If the account has been set, proceed to place the outgoing call.
1016             // Otherwise the connection will be initiated when the account is set by the user.
1017             call.startCreateConnection(mPhoneAccountRegistrar);
1018         } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
1019                 requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false,
1020                 call.getInitiatingUser()).isEmpty()) {
1021             // If there are no call capable accounts, disconnect the call.
1022             markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
1023                     "No registered PhoneAccounts"));
1024             markCallAsRemoved(call);
1025         }
1026     }
1027 
1028     /**
1029      * Attempts to start a conference call for the specified call.
1030      *
1031      * @param call The call to conference.
1032      * @param otherCall The other call to conference with.
1033      */
1034     @VisibleForTesting
conference(Call call, Call otherCall)1035     public void conference(Call call, Call otherCall) {
1036         call.conferenceWith(otherCall);
1037     }
1038 
1039     /**
1040      * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
1041      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
1042      * the user opting to answer said call.
1043      *
1044      * @param call The call to answer.
1045      * @param videoState The video state in which to answer the call.
1046      */
1047     @VisibleForTesting
answerCall(Call call, int videoState)1048     public void answerCall(Call call, int videoState) {
1049         if (!mCalls.contains(call)) {
1050             Log.i(this, "Request to answer a non-existent call %s", call);
1051         } else {
1052             Call foregroundCall = getForegroundCall();
1053             // If the foreground call is not the ringing call and it is currently isActive() or
1054             // STATE_DIALING, put it on hold before answering the call.
1055             if (foregroundCall != null && foregroundCall != call &&
1056                     (foregroundCall.isActive() ||
1057                      foregroundCall.getState() == CallState.DIALING ||
1058                      foregroundCall.getState() == CallState.PULLING)) {
1059                 if (0 == (foregroundCall.getConnectionCapabilities()
1060                         & Connection.CAPABILITY_HOLD)) {
1061                     // This call does not support hold.  If it is from a different connection
1062                     // service, then disconnect it, otherwise allow the connection service to
1063                     // figure out the right states.
1064                     if (foregroundCall.getConnectionService() != call.getConnectionService()) {
1065                         foregroundCall.disconnect();
1066                     }
1067                 } else {
1068                     Call heldCall = getHeldCall();
1069                     if (heldCall != null) {
1070                         Log.v(this, "Disconnecting held call %s before holding active call.",
1071                                 heldCall);
1072                         heldCall.disconnect();
1073                     }
1074 
1075                     Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
1076                             foregroundCall, call);
1077                     foregroundCall.hold();
1078                 }
1079                 // TODO: Wait until we get confirmation of the active call being
1080                 // on-hold before answering the new call.
1081                 // TODO: Import logic from CallManager.acceptCall()
1082             }
1083 
1084             for (CallsManagerListener listener : mListeners) {
1085                 listener.onIncomingCallAnswered(call);
1086             }
1087 
1088             // We do not update the UI until we get confirmation of the answer() through
1089             // {@link #markCallAsActive}.
1090             call.answer(videoState);
1091             if (isSpeakerphoneAutoEnabledForVideoCalls(videoState)) {
1092                 call.setStartWithSpeakerphoneOn(true);
1093             }
1094         }
1095     }
1096 
1097     /**
1098      * Determines if the speakerphone should be automatically enabled for the call.  Speakerphone
1099      * should be enabled if the call is a video call and bluetooth or the wired headset are not in
1100      * use.
1101      *
1102      * @param videoState The video state of the call.
1103      * @return {@code true} if the speakerphone should be enabled.
1104      */
isSpeakerphoneAutoEnabledForVideoCalls(int videoState)1105     public boolean isSpeakerphoneAutoEnabledForVideoCalls(int videoState) {
1106         return VideoProfile.isVideo(videoState) &&
1107             !mWiredHeadsetManager.isPluggedIn() &&
1108             !mBluetoothManager.isBluetoothAvailable() &&
1109             isSpeakerEnabledForVideoCalls();
1110     }
1111 
1112     /**
1113      * Determines if the speakerphone should be enabled for when docked.  Speakerphone
1114      * should be enabled if the device is docked and bluetooth or the wired headset are
1115      * not in use.
1116      *
1117      * @return {@code true} if the speakerphone should be enabled for the dock.
1118      */
isSpeakerphoneEnabledForDock()1119     private boolean isSpeakerphoneEnabledForDock() {
1120         return mDockManager.isDocked() &&
1121             !mWiredHeadsetManager.isPluggedIn() &&
1122             !mBluetoothManager.isBluetoothAvailable();
1123     }
1124 
1125     /**
1126      * Determines if the speakerphone should be automatically enabled for video calls.
1127      *
1128      * @return {@code true} if the speakerphone should automatically be enabled.
1129      */
isSpeakerEnabledForVideoCalls()1130     private static boolean isSpeakerEnabledForVideoCalls() {
1131         return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
1132                 PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
1133                 PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
1134     }
1135 
1136     /**
1137      * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
1138      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
1139      * the user opting to reject said call.
1140      */
1141     @VisibleForTesting
rejectCall(Call call, boolean rejectWithMessage, String textMessage)1142     public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
1143         if (!mCalls.contains(call)) {
1144             Log.i(this, "Request to reject a non-existent call %s", call);
1145         } else {
1146             for (CallsManagerListener listener : mListeners) {
1147                 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
1148             }
1149             call.reject(rejectWithMessage, textMessage);
1150         }
1151     }
1152 
1153     /**
1154      * Instructs Telecom to play the specified DTMF tone within the specified call.
1155      *
1156      * @param digit The DTMF digit to play.
1157      */
1158     @VisibleForTesting
playDtmfTone(Call call, char digit)1159     public void playDtmfTone(Call call, char digit) {
1160         if (!mCalls.contains(call)) {
1161             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
1162         } else {
1163             call.playDtmfTone(digit);
1164             mDtmfLocalTonePlayer.playTone(call, digit);
1165         }
1166     }
1167 
1168     /**
1169      * Instructs Telecom to stop the currently playing DTMF tone, if any.
1170      */
1171     @VisibleForTesting
stopDtmfTone(Call call)1172     public void stopDtmfTone(Call call) {
1173         if (!mCalls.contains(call)) {
1174             Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
1175         } else {
1176             call.stopDtmfTone();
1177             mDtmfLocalTonePlayer.stopTone(call);
1178         }
1179     }
1180 
1181     /**
1182      * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
1183      */
postDialContinue(Call call, boolean proceed)1184     void postDialContinue(Call call, boolean proceed) {
1185         if (!mCalls.contains(call)) {
1186             Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
1187         } else {
1188             call.postDialContinue(proceed);
1189         }
1190     }
1191 
1192     /**
1193      * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
1194      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
1195      * the user hitting the end-call button.
1196      */
1197     @VisibleForTesting
disconnectCall(Call call)1198     public void disconnectCall(Call call) {
1199         Log.v(this, "disconnectCall %s", call);
1200 
1201         if (!mCalls.contains(call)) {
1202             Log.w(this, "Unknown call (%s) asked to disconnect", call);
1203         } else {
1204             mLocallyDisconnectingCalls.add(call);
1205             call.disconnect();
1206         }
1207     }
1208 
1209     /**
1210      * Instructs Telecom to disconnect all calls.
1211      */
disconnectAllCalls()1212     void disconnectAllCalls() {
1213         Log.v(this, "disconnectAllCalls");
1214 
1215         for (Call call : mCalls) {
1216             disconnectCall(call);
1217         }
1218     }
1219 
1220 
1221     /**
1222      * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
1223      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
1224      * the user hitting the hold button during an active call.
1225      */
1226     @VisibleForTesting
holdCall(Call call)1227     public void holdCall(Call call) {
1228         if (!mCalls.contains(call)) {
1229             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
1230         } else {
1231             Log.d(this, "Putting call on hold: (%s)", call);
1232             call.hold();
1233         }
1234     }
1235 
1236     /**
1237      * Instructs Telecom to release the specified call from hold. Intended to be invoked by
1238      * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
1239      * by the user hitting the hold button during a held call.
1240      */
1241     @VisibleForTesting
unholdCall(Call call)1242     public void unholdCall(Call call) {
1243         if (!mCalls.contains(call)) {
1244             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
1245         } else {
1246             boolean otherCallHeld = false;
1247             Log.d(this, "unholding call: (%s)", call);
1248             for (Call c : mCalls) {
1249                 // Only attempt to hold parent calls and not the individual children.
1250                 if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
1251                     otherCallHeld = true;
1252                     Log.event(c, Log.Events.SWAP);
1253                     c.hold();
1254                 }
1255             }
1256             if (otherCallHeld) {
1257                 Log.event(call, Log.Events.SWAP);
1258             }
1259             call.unhold();
1260         }
1261     }
1262 
1263     @Override
onExtrasChanged(Call c, int source, Bundle extras)1264     public void onExtrasChanged(Call c, int source, Bundle extras) {
1265         if (source != Call.SOURCE_CONNECTION_SERVICE) {
1266             return;
1267         }
1268         handleCallTechnologyChange(c);
1269         handleChildAddressChange(c);
1270         updateCanAddCall();
1271     }
1272 
1273     // Construct the list of possible PhoneAccounts that the outgoing call can use based on the
1274     // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,
1275     // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
constructPossiblePhoneAccounts(Uri handle, UserHandle user)1276     private List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user) {
1277         if (handle == null) {
1278             return Collections.emptyList();
1279         }
1280         List<PhoneAccountHandle> allAccounts =
1281                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user);
1282         // First check the Radio SIM Technology
1283         if(mRadioSimVariants == null) {
1284             TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
1285                     Context.TELEPHONY_SERVICE);
1286             // Cache Sim Variants
1287             mRadioSimVariants = tm.getMultiSimConfiguration();
1288         }
1289         // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount
1290         // Should be available if a call is already active on the SIM account.
1291         if(mRadioSimVariants != TelephonyManager.MultiSimVariants.DSDA) {
1292             List<PhoneAccountHandle> simAccounts =
1293                     mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser();
1294             PhoneAccountHandle ongoingCallAccount = null;
1295             for (Call c : mCalls) {
1296                 if (!c.isDisconnected() && !c.isNew() && simAccounts.contains(
1297                         c.getTargetPhoneAccount())) {
1298                     ongoingCallAccount = c.getTargetPhoneAccount();
1299                     break;
1300                 }
1301             }
1302             if (ongoingCallAccount != null) {
1303                 // Remove all SIM accounts that are not the active SIM from the list.
1304                 simAccounts.remove(ongoingCallAccount);
1305                 allAccounts.removeAll(simAccounts);
1306             }
1307         }
1308         return allAccounts;
1309     }
1310 
1311     /**
1312      * Informs listeners (notably {@link CallAudioManager} of a change to the call's external
1313      * property.
1314      * .
1315      * @param call The call whose external property changed.
1316      * @param isExternalCall {@code True} if the call is now external, {@code false} otherwise.
1317      */
1318     @Override
onExternalCallChanged(Call call, boolean isExternalCall)1319     public void onExternalCallChanged(Call call, boolean isExternalCall) {
1320         Log.v(this, "onConnectionPropertiesChanged: %b", isExternalCall);
1321         for (CallsManagerListener listener : mListeners) {
1322             listener.onExternalCallChanged(call, isExternalCall);
1323         }
1324     }
1325 
handleCallTechnologyChange(Call call)1326     private void handleCallTechnologyChange(Call call) {
1327         if (call.getExtras() != null
1328                 && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) {
1329 
1330             Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get(
1331                     call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE));
1332             if (analyticsCallTechnology == null) {
1333                 analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE;
1334             }
1335             call.getAnalytics().addCallTechnology(analyticsCallTechnology);
1336         }
1337     }
1338 
handleChildAddressChange(Call call)1339     public void handleChildAddressChange(Call call) {
1340         if (call.getExtras() != null
1341                 && call.getExtras().containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
1342 
1343             String viaNumber = call.getExtras().getString(Connection.EXTRA_CHILD_ADDRESS);
1344             call.setViaNumber(viaNumber);
1345         }
1346     }
1347 
1348     /** Called by the in-call UI to change the mute state. */
mute(boolean shouldMute)1349     void mute(boolean shouldMute) {
1350         mCallAudioManager.mute(shouldMute);
1351     }
1352 
1353     /**
1354       * Called by the in-call UI to change the audio route, for example to change from earpiece to
1355       * speaker phone.
1356       */
setAudioRoute(int route)1357     void setAudioRoute(int route) {
1358         mCallAudioManager.setAudioRoute(route);
1359     }
1360 
1361     /** Called by the in-call UI to turn the proximity sensor on. */
turnOnProximitySensor()1362     void turnOnProximitySensor() {
1363         mProximitySensorManager.turnOn();
1364     }
1365 
1366     /**
1367      * Called by the in-call UI to turn the proximity sensor off.
1368      * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
1369      *        the screen will be kept off until the proximity sensor goes negative.
1370      */
turnOffProximitySensor(boolean screenOnImmediately)1371     void turnOffProximitySensor(boolean screenOnImmediately) {
1372         mProximitySensorManager.turnOff(screenOnImmediately);
1373     }
1374 
phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault)1375     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
1376         if (!mCalls.contains(call)) {
1377             Log.i(this, "Attempted to add account to unknown call %s", call);
1378         } else {
1379             call.setTargetPhoneAccount(account);
1380 
1381             if (!call.isNewOutgoingCallIntentBroadcastDone()) {
1382                 return;
1383             }
1384 
1385             // Note: emergency calls never go through account selection dialog so they never
1386             // arrive here.
1387             if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
1388                 call.startCreateConnection(mPhoneAccountRegistrar);
1389             } else {
1390                 call.disconnect();
1391             }
1392 
1393             if (setDefault) {
1394                 mPhoneAccountRegistrar
1395                         .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
1396             }
1397         }
1398     }
1399 
1400     /** Called when the audio state changes. */
1401     @VisibleForTesting
onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState)1402     public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState
1403             newAudioState) {
1404         Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
1405         for (CallsManagerListener listener : mListeners) {
1406             listener.onCallAudioStateChanged(oldAudioState, newAudioState);
1407         }
1408     }
1409 
markCallAsRinging(Call call)1410     void markCallAsRinging(Call call) {
1411         setCallState(call, CallState.RINGING, "ringing set explicitly");
1412     }
1413 
markCallAsDialing(Call call)1414     void markCallAsDialing(Call call) {
1415         setCallState(call, CallState.DIALING, "dialing set explicitly");
1416         maybeMoveToSpeakerPhone(call);
1417     }
1418 
markCallAsPulling(Call call)1419     void markCallAsPulling(Call call) {
1420         setCallState(call, CallState.PULLING, "pulling set explicitly");
1421         maybeMoveToSpeakerPhone(call);
1422     }
1423 
markCallAsActive(Call call)1424     void markCallAsActive(Call call) {
1425         setCallState(call, CallState.ACTIVE, "active set explicitly");
1426         maybeMoveToSpeakerPhone(call);
1427     }
1428 
markCallAsOnHold(Call call)1429     void markCallAsOnHold(Call call) {
1430         setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
1431     }
1432 
1433     /**
1434      * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
1435      * last live call, then also disconnect from the in-call controller.
1436      *
1437      * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
1438      */
markCallAsDisconnected(Call call, DisconnectCause disconnectCause)1439     void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
1440         call.setDisconnectCause(disconnectCause);
1441         setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly");
1442     }
1443 
1444     /**
1445      * Removes an existing disconnected call, and notifies the in-call app.
1446      */
markCallAsRemoved(Call call)1447     void markCallAsRemoved(Call call) {
1448         removeCall(call);
1449         Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
1450         if (mLocallyDisconnectingCalls.contains(call)) {
1451             mLocallyDisconnectingCalls.remove(call);
1452             if (foregroundCall != null && foregroundCall.getState() == CallState.ON_HOLD) {
1453                 foregroundCall.unhold();
1454             }
1455         } else if (foregroundCall != null &&
1456                 !foregroundCall.can(Connection.CAPABILITY_SUPPORT_HOLD)  &&
1457                 foregroundCall.getState() == CallState.ON_HOLD) {
1458 
1459             // The new foreground call is on hold, however the carrier does not display the hold
1460             // button in the UI.  Therefore, we need to auto unhold the held call since the user has
1461             // no means of unholding it themselves.
1462             Log.i(this, "Auto-unholding held foreground call (call doesn't support hold)");
1463             foregroundCall.unhold();
1464         }
1465     }
1466 
1467     /**
1468      * Cleans up any calls currently associated with the specified connection service when the
1469      * service binder disconnects unexpectedly.
1470      *
1471      * @param service The connection service that disconnected.
1472      */
handleConnectionServiceDeath(ConnectionServiceWrapper service)1473     void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
1474         if (service != null) {
1475             for (Call call : mCalls) {
1476                 if (call.getConnectionService() == service) {
1477                     if (call.getState() != CallState.DISCONNECTED) {
1478                         markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
1479                     }
1480                     markCallAsRemoved(call);
1481                 }
1482             }
1483         }
1484     }
1485 
1486     /**
1487      * Determines if the {@link CallsManager} has any non-external calls.
1488      *
1489      * @return {@code True} if there are any non-external calls, {@code false} otherwise.
1490      */
hasAnyCalls()1491     boolean hasAnyCalls() {
1492         if (mCalls.isEmpty()) {
1493             return false;
1494         }
1495 
1496         for (Call call : mCalls) {
1497             if (!call.isExternalCall()) {
1498                 return true;
1499             }
1500         }
1501         return false;
1502     }
1503 
hasActiveOrHoldingCall()1504     boolean hasActiveOrHoldingCall() {
1505         return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
1506     }
1507 
hasRingingCall()1508     boolean hasRingingCall() {
1509         return getFirstCallWithState(CallState.RINGING) != null;
1510     }
1511 
onMediaButton(int type)1512     boolean onMediaButton(int type) {
1513         if (hasAnyCalls()) {
1514             if (HeadsetMediaButton.SHORT_PRESS == type) {
1515                 Call ringingCall = getFirstCallWithState(CallState.RINGING);
1516                 if (ringingCall == null) {
1517                     mCallAudioManager.toggleMute();
1518                     return true;
1519                 } else {
1520                     ringingCall.answer(VideoProfile.STATE_AUDIO_ONLY);
1521                     return true;
1522                 }
1523             } else if (HeadsetMediaButton.LONG_PRESS == type) {
1524                 Log.d(this, "handleHeadsetHook: longpress -> hangup");
1525                 Call callToHangup = getFirstCallWithState(
1526                         CallState.RINGING, CallState.DIALING, CallState.PULLING, CallState.ACTIVE,
1527                         CallState.ON_HOLD);
1528                 if (callToHangup != null) {
1529                     callToHangup.disconnect();
1530                     return true;
1531                 }
1532             }
1533         }
1534         return false;
1535     }
1536 
1537     /**
1538      * Returns true if telecom supports adding another top-level call.
1539      */
1540     @VisibleForTesting
canAddCall()1541     public boolean canAddCall() {
1542         boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
1543                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
1544         if (!isDeviceProvisioned) {
1545             Log.d(TAG, "Device not provisioned, canAddCall is false.");
1546             return false;
1547         }
1548 
1549         if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
1550             return false;
1551         }
1552 
1553         int count = 0;
1554         for (Call call : mCalls) {
1555             if (call.isEmergencyCall()) {
1556                 // We never support add call if one of the calls is an emergency call.
1557                 return false;
1558             } else if (call.isExternalCall()) {
1559                 // External calls don't count.
1560                 continue;
1561             } else if (call.getParentCall() == null) {
1562                 count++;
1563             }
1564             Bundle extras = call.getExtras();
1565             if (extras != null) {
1566                 if (extras.getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) {
1567                     return false;
1568                 }
1569             }
1570 
1571             // We do not check states for canAddCall. We treat disconnected calls the same
1572             // and wait until they are removed instead. If we didn't count disconnected calls,
1573             // we could put InCallServices into a state where they are showing two calls but
1574             // also support add-call. Technically it's right, but overall looks better (UI-wise)
1575             // and acts better if we wait until the call is removed.
1576             if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
1577                 return false;
1578             }
1579         }
1580 
1581         return true;
1582     }
1583 
1584     @VisibleForTesting
getRingingCall()1585     public Call getRingingCall() {
1586         return getFirstCallWithState(CallState.RINGING);
1587     }
1588 
1589     @VisibleForTesting
getActiveCall()1590     public Call getActiveCall() {
1591         return getFirstCallWithState(CallState.ACTIVE);
1592     }
1593 
getDialingCall()1594     Call getDialingCall() {
1595         return getFirstCallWithState(CallState.DIALING);
1596     }
1597 
1598     @VisibleForTesting
getHeldCall()1599     public Call getHeldCall() {
1600         return getFirstCallWithState(CallState.ON_HOLD);
1601     }
1602 
1603     @VisibleForTesting
getNumHeldCalls()1604     public int getNumHeldCalls() {
1605         int count = 0;
1606         for (Call call : mCalls) {
1607             if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
1608                 count++;
1609             }
1610         }
1611         return count;
1612     }
1613 
1614     @VisibleForTesting
getOutgoingCall()1615     public Call getOutgoingCall() {
1616         return getFirstCallWithState(OUTGOING_CALL_STATES);
1617     }
1618 
1619     @VisibleForTesting
getFirstCallWithState(int... states)1620     public Call getFirstCallWithState(int... states) {
1621         return getFirstCallWithState(null, states);
1622     }
1623 
1624     @VisibleForTesting
getPhoneNumberUtilsAdapter()1625     public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() {
1626         return mPhoneNumberUtilsAdapter;
1627     }
1628 
1629     /**
1630      * Returns the first call that it finds with the given states. The states are treated as having
1631      * priority order so that any call with the first state will be returned before any call with
1632      * states listed later in the parameter list.
1633      *
1634      * @param callToSkip Call that this method should skip while searching
1635      */
getFirstCallWithState(Call callToSkip, int... states)1636     Call getFirstCallWithState(Call callToSkip, int... states) {
1637         for (int currentState : states) {
1638             // check the foreground first
1639             Call foregroundCall = getForegroundCall();
1640             if (foregroundCall != null && foregroundCall.getState() == currentState) {
1641                 return foregroundCall;
1642             }
1643 
1644             for (Call call : mCalls) {
1645                 if (Objects.equals(callToSkip, call)) {
1646                     continue;
1647                 }
1648 
1649                 // Only operate on top-level calls
1650                 if (call.getParentCall() != null) {
1651                     continue;
1652                 }
1653 
1654                 if (call.isExternalCall()) {
1655                     continue;
1656                 }
1657 
1658                 if (currentState == call.getState()) {
1659                     return call;
1660                 }
1661             }
1662         }
1663         return null;
1664     }
1665 
createConferenceCall( String callId, PhoneAccountHandle phoneAccount, ParcelableConference parcelableConference)1666     Call createConferenceCall(
1667             String callId,
1668             PhoneAccountHandle phoneAccount,
1669             ParcelableConference parcelableConference) {
1670 
1671         // If the parceled conference specifies a connect time, use it; otherwise default to 0,
1672         // which is the default value for new Calls.
1673         long connectTime =
1674                 parcelableConference.getConnectTimeMillis() ==
1675                         Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
1676                         parcelableConference.getConnectTimeMillis();
1677 
1678         Call call = new Call(
1679                 callId,
1680                 mContext,
1681                 this,
1682                 mLock,
1683                 mConnectionServiceRepository,
1684                 mContactsAsyncHelper,
1685                 mCallerInfoAsyncQueryFactory,
1686                 mPhoneNumberUtilsAdapter,
1687                 null /* handle */,
1688                 null /* gatewayInfo */,
1689                 null /* connectionManagerPhoneAccount */,
1690                 phoneAccount,
1691                 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
1692                 false /* forceAttachToExistingConnection */,
1693                 true /* isConference */,
1694                 connectTime);
1695 
1696         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
1697                 "new conference call");
1698         call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
1699         call.setConnectionProperties(parcelableConference.getConnectionProperties());
1700         call.setVideoState(parcelableConference.getVideoState());
1701         call.setVideoProvider(parcelableConference.getVideoProvider());
1702         call.setStatusHints(parcelableConference.getStatusHints());
1703         call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras());
1704         // In case this Conference was added via a ConnectionManager, keep track of the original
1705         // Connection ID as created by the originating ConnectionService.
1706         Bundle extras = parcelableConference.getExtras();
1707         if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
1708             call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
1709         }
1710 
1711         // TODO: Move this to be a part of addCall()
1712         call.addListener(this);
1713         addCall(call);
1714         return call;
1715     }
1716 
1717     /**
1718      * @return the call state currently tracked by {@link PhoneStateBroadcaster}
1719      */
getCallState()1720     int getCallState() {
1721         return mPhoneStateBroadcaster.getCallState();
1722     }
1723 
1724     /**
1725      * Retrieves the {@link PhoneAccountRegistrar}.
1726      *
1727      * @return The {@link PhoneAccountRegistrar}.
1728      */
getPhoneAccountRegistrar()1729     PhoneAccountRegistrar getPhoneAccountRegistrar() {
1730         return mPhoneAccountRegistrar;
1731     }
1732 
1733     /**
1734      * Retrieves the {@link MissedCallNotifier}
1735      * @return The {@link MissedCallNotifier}.
1736      */
getMissedCallNotifier()1737     MissedCallNotifier getMissedCallNotifier() {
1738         return mMissedCallNotifier;
1739     }
1740 
1741     /**
1742      * Reject an incoming call and manually add it to the Call Log.
1743      * @param incomingCall Incoming call that has been rejected
1744      */
rejectCallAndLog(Call incomingCall)1745     private void rejectCallAndLog(Call incomingCall) {
1746         if (incomingCall.getConnectionService() != null) {
1747             // Only reject the call if it has not already been destroyed.  If a call ends while
1748             // incoming call filtering is taking place, it is possible that the call has already
1749             // been destroyed, and as such it will be impossible to send the reject to the
1750             // associated ConnectionService.
1751             incomingCall.reject(false, null);
1752         } else {
1753             Log.i(this, "rejectCallAndLog - call already destroyed.");
1754         }
1755 
1756         // Since the call was not added to the list of calls, we have to call the missed
1757         // call notifier and the call logger manually.
1758         // Do we need missed call notification for direct to Voicemail calls?
1759         mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
1760                 true /*showNotificationForMissedCall*/);
1761     }
1762 
1763     /**
1764      * Adds the specified call to the main list of live calls.
1765      *
1766      * @param call The call to add.
1767      */
addCall(Call call)1768     private void addCall(Call call) {
1769         Trace.beginSection("addCall");
1770         Log.v(this, "addCall(%s)", call);
1771         call.addListener(this);
1772         mCalls.add(call);
1773 
1774         // Specifies the time telecom finished routing the call. This is used by the dialer for
1775         // analytics.
1776         Bundle extras = call.getIntentExtras();
1777         extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
1778                 SystemClock.elapsedRealtime());
1779 
1780         updateCanAddCall();
1781         // onCallAdded for calls which immediately take the foreground (like the first call).
1782         for (CallsManagerListener listener : mListeners) {
1783             if (Log.SYSTRACE_DEBUG) {
1784                 Trace.beginSection(listener.getClass().toString() + " addCall");
1785             }
1786             listener.onCallAdded(call);
1787             if (Log.SYSTRACE_DEBUG) {
1788                 Trace.endSection();
1789             }
1790         }
1791         Trace.endSection();
1792     }
1793 
removeCall(Call call)1794     private void removeCall(Call call) {
1795         Trace.beginSection("removeCall");
1796         Log.v(this, "removeCall(%s)", call);
1797 
1798         call.setParentCall(null);  // need to clean up parent relationship before destroying.
1799         call.removeListener(this);
1800         call.clearConnectionService();
1801 
1802         boolean shouldNotify = false;
1803         if (mCalls.contains(call)) {
1804             mCalls.remove(call);
1805             shouldNotify = true;
1806         }
1807 
1808         call.destroy();
1809 
1810         // Only broadcast changes for calls that are being tracked.
1811         if (shouldNotify) {
1812             updateCanAddCall();
1813             for (CallsManagerListener listener : mListeners) {
1814                 if (Log.SYSTRACE_DEBUG) {
1815                     Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
1816                 }
1817                 listener.onCallRemoved(call);
1818                 if (Log.SYSTRACE_DEBUG) {
1819                     Trace.endSection();
1820                 }
1821             }
1822         }
1823         Trace.endSection();
1824     }
1825 
1826     /**
1827      * Sets the specified state on the specified call.
1828      *
1829      * @param call The call.
1830      * @param newState The new state of the call.
1831      */
setCallState(Call call, int newState, String tag)1832     private void setCallState(Call call, int newState, String tag) {
1833         if (call == null) {
1834             return;
1835         }
1836         int oldState = call.getState();
1837         Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
1838                 CallState.toString(newState), call);
1839         if (newState != oldState) {
1840             // Unfortunately, in the telephony world the radio is king. So if the call notifies
1841             // us that the call is in a particular state, we allow it even if it doesn't make
1842             // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
1843             // TODO: Consider putting a stop to the above and turning CallState
1844             // into a well-defined state machine.
1845             // TODO: Define expected state transitions here, and log when an
1846             // unexpected transition occurs.
1847             call.setState(newState, tag);
1848             maybeShowErrorDialogOnDisconnect(call);
1849 
1850             Trace.beginSection("onCallStateChanged");
1851             // Only broadcast state change for calls that are being tracked.
1852             if (mCalls.contains(call)) {
1853                 updateCanAddCall();
1854                 for (CallsManagerListener listener : mListeners) {
1855                     if (Log.SYSTRACE_DEBUG) {
1856                         Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
1857                     }
1858                     listener.onCallStateChanged(call, oldState, newState);
1859                     if (Log.SYSTRACE_DEBUG) {
1860                         Trace.endSection();
1861                     }
1862                 }
1863             }
1864             Trace.endSection();
1865         }
1866     }
1867 
updateCanAddCall()1868     private void updateCanAddCall() {
1869         boolean newCanAddCall = canAddCall();
1870         if (newCanAddCall != mCanAddCall) {
1871             mCanAddCall = newCanAddCall;
1872             for (CallsManagerListener listener : mListeners) {
1873                 if (Log.SYSTRACE_DEBUG) {
1874                     Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
1875                 }
1876                 listener.onCanAddCallChanged(mCanAddCall);
1877                 if (Log.SYSTRACE_DEBUG) {
1878                     Trace.endSection();
1879                 }
1880             }
1881         }
1882     }
1883 
isPotentialMMICode(Uri handle)1884     private boolean isPotentialMMICode(Uri handle) {
1885         return (handle != null && handle.getSchemeSpecificPart() != null
1886                 && handle.getSchemeSpecificPart().contains("#"));
1887     }
1888 
1889     /**
1890      * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
1891      * MMI codes which can be dialed when one or more calls are in progress.
1892      * <P>
1893      * Checks for numbers formatted similar to the MMI codes defined in:
1894      * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)}
1895      *
1896      * @param handle The URI to call.
1897      * @return {@code True} if the URI represents a number which could be an in-call MMI code.
1898      */
isPotentialInCallMMICode(Uri handle)1899     private boolean isPotentialInCallMMICode(Uri handle) {
1900         if (handle != null && handle.getSchemeSpecificPart() != null &&
1901                 handle.getScheme() != null &&
1902                 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
1903 
1904             String dialedNumber = handle.getSchemeSpecificPart();
1905             return (dialedNumber.equals("0") ||
1906                     (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
1907                     (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
1908                     dialedNumber.equals("3") ||
1909                     dialedNumber.equals("4") ||
1910                     dialedNumber.equals("5"));
1911         }
1912         return false;
1913     }
1914 
getNumCallsWithState(int... states)1915     private int getNumCallsWithState(int... states) {
1916         int count = 0;
1917         for (int state : states) {
1918             for (Call call : mCalls) {
1919                 if (call.getParentCall() == null && call.getState() == state &&
1920                         !call.isExternalCall()) {
1921 
1922                     count++;
1923                 }
1924             }
1925         }
1926         return count;
1927     }
1928 
hasMaximumLiveCalls()1929     private boolean hasMaximumLiveCalls() {
1930         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
1931     }
1932 
hasMaximumHoldingCalls()1933     private boolean hasMaximumHoldingCalls() {
1934         return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
1935     }
1936 
hasMaximumRingingCalls()1937     private boolean hasMaximumRingingCalls() {
1938         return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
1939     }
1940 
hasMaximumOutgoingCalls()1941     private boolean hasMaximumOutgoingCalls() {
1942         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
1943     }
1944 
hasMaximumDialingCalls()1945     private boolean hasMaximumDialingCalls() {
1946         return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(CallState.DIALING, CallState.PULLING);
1947     }
1948 
makeRoomForOutgoingCall(Call call, boolean isEmergency)1949     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
1950         if (hasMaximumLiveCalls()) {
1951             // NOTE: If the amount of live calls changes beyond 1, this logic will probably
1952             // have to change.
1953             Call liveCall = getFirstCallWithState(LIVE_CALL_STATES);
1954             Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
1955                    liveCall);
1956 
1957             if (call == liveCall) {
1958                 // If the call is already the foreground call, then we are golden.
1959                 // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT
1960                 // state since the call was already populated into the list.
1961                 return true;
1962             }
1963 
1964             if (hasMaximumOutgoingCalls()) {
1965                 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
1966                 if (isEmergency && !outgoingCall.isEmergencyCall()) {
1967                     // Disconnect the current outgoing call if it's not an emergency call. If the
1968                     // user tries to make two outgoing calls to different emergency call numbers,
1969                     // we will try to connect the first outgoing call.
1970                     call.getAnalytics().setCallIsAdditional(true);
1971                     outgoingCall.getAnalytics().setCallIsInterrupted(true);
1972                     outgoingCall.disconnect();
1973                     return true;
1974                 }
1975                 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
1976                     // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT}
1977                     // state, just disconnect it since the user has explicitly started a new call.
1978                     call.getAnalytics().setCallIsAdditional(true);
1979                     outgoingCall.getAnalytics().setCallIsInterrupted(true);
1980                     outgoingCall.disconnect();
1981                     return true;
1982                 }
1983                 return false;
1984             }
1985 
1986             if (hasMaximumHoldingCalls()) {
1987                 // There is no more room for any more calls, unless it's an emergency.
1988                 if (isEmergency) {
1989                     // Kill the current active call, this is easier then trying to disconnect a
1990                     // holding call and hold an active call.
1991                     call.getAnalytics().setCallIsAdditional(true);
1992                     liveCall.getAnalytics().setCallIsInterrupted(true);
1993                     liveCall.disconnect();
1994                     return true;
1995                 }
1996                 return false;  // No more room!
1997             }
1998 
1999             // We have room for at least one more holding call at this point.
2000 
2001             // TODO: Remove once b/23035408 has been corrected.
2002             // If the live call is a conference, it will not have a target phone account set.  This
2003             // means the check to see if the live call has the same target phone account as the new
2004             // call will not cause us to bail early.  As a result, we'll end up holding the
2005             // ongoing conference call.  However, the ConnectionService is already doing that.  This
2006             // has caused problems with some carriers.  As a workaround until b/23035408 is
2007             // corrected, we will try and get the target phone account for one of the conference's
2008             // children and use that instead.
2009             PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount();
2010             if (liveCallPhoneAccount == null && liveCall.isConference() &&
2011                     !liveCall.getChildCalls().isEmpty()) {
2012                 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall);
2013                 Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " +
2014                         liveCallPhoneAccount);
2015             }
2016 
2017             // First thing, if we are trying to make a call with the same phone account as the live
2018             // call, then allow it so that the connection service can make its own decision about
2019             // how to handle the new call relative to the current one.
2020             if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) {
2021                 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches.");
2022                 call.getAnalytics().setCallIsAdditional(true);
2023                 liveCall.getAnalytics().setCallIsInterrupted(true);
2024                 return true;
2025             } else if (call.getTargetPhoneAccount() == null) {
2026                 // Without a phone account, we can't say reliably that the call will fail.
2027                 // If the user chooses the same phone account as the live call, then it's
2028                 // still possible that the call can be made (like with CDMA calls not supporting
2029                 // hold but they still support adding a call by going immediately into conference
2030                 // mode). Return true here and we'll run this code again after user chooses an
2031                 // account.
2032                 return true;
2033             }
2034 
2035             // Try to hold the live call before attempting the new outgoing call.
2036             if (liveCall.can(Connection.CAPABILITY_HOLD)) {
2037                 Log.i(this, "makeRoomForOutgoingCall: holding live call.");
2038                 call.getAnalytics().setCallIsAdditional(true);
2039                 liveCall.getAnalytics().setCallIsInterrupted(true);
2040                 liveCall.hold();
2041                 return true;
2042             }
2043 
2044             // The live call cannot be held so we're out of luck here.  There's no room.
2045             return false;
2046         }
2047         return true;
2048     }
2049 
2050     /**
2051      * Given a call, find the first non-null phone account handle of its children.
2052      *
2053      * @param parentCall The parent call.
2054      * @return The first non-null phone account handle of the children, or {@code null} if none.
2055      */
getFirstChildPhoneAccount(Call parentCall)2056     private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) {
2057         for (Call childCall : parentCall.getChildCalls()) {
2058             PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount();
2059             if (childPhoneAccount != null) {
2060                 return childPhoneAccount;
2061             }
2062         }
2063         return null;
2064     }
2065 
2066     /**
2067      * Checks to see if the call should be on speakerphone and if so, set it.
2068      */
maybeMoveToSpeakerPhone(Call call)2069     private void maybeMoveToSpeakerPhone(Call call) {
2070         if (call.getStartWithSpeakerphoneOn()) {
2071             setAudioRoute(CallAudioState.ROUTE_SPEAKER);
2072             call.setStartWithSpeakerphoneOn(false);
2073         }
2074     }
2075 
2076     /**
2077      * Creates a new call for an existing connection.
2078      *
2079      * @param callId The id of the new call.
2080      * @param connection The connection information.
2081      * @return The new call.
2082      */
createCallForExistingConnection(String callId, ParcelableConnection connection)2083     Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
2084         boolean isDowngradedConference = (connection.getConnectionProperties()
2085                 & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
2086         Call call = new Call(
2087                 callId,
2088                 mContext,
2089                 this,
2090                 mLock,
2091                 mConnectionServiceRepository,
2092                 mContactsAsyncHelper,
2093                 mCallerInfoAsyncQueryFactory,
2094                 mPhoneNumberUtilsAdapter,
2095                 connection.getHandle() /* handle */,
2096                 null /* gatewayInfo */,
2097                 null /* connectionManagerPhoneAccount */,
2098                 connection.getPhoneAccount(), /* targetPhoneAccountHandle */
2099                 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
2100                 false /* forceAttachToExistingConnection */,
2101                 isDowngradedConference /* isConference */,
2102                 connection.getConnectTimeMillis() /* connectTimeMillis */);
2103 
2104         call.initAnalytics();
2105         call.getAnalytics().setCreatedFromExistingConnection(true);
2106 
2107         setCallState(call, Call.getStateFromConnectionState(connection.getState()),
2108                 "existing connection");
2109         call.setConnectionCapabilities(connection.getConnectionCapabilities());
2110         call.setConnectionProperties(connection.getConnectionProperties());
2111         call.setCallerDisplayName(connection.getCallerDisplayName(),
2112                 connection.getCallerDisplayNamePresentation());
2113         call.addListener(this);
2114 
2115         // In case this connection was added via a ConnectionManager, keep track of the original
2116         // Connection ID as created by the originating ConnectionService.
2117         Bundle extras = connection.getExtras();
2118         if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
2119             call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
2120         }
2121         addCall(call);
2122 
2123         return call;
2124     }
2125 
2126     /**
2127      * Determines whether Telecom already knows about a Connection added via the
2128      * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
2129      * Connection)} API via a ConnectionManager.
2130      *
2131      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
2132      * @param originalConnectionId The new connection ID to check.
2133      * @return {@code true} if this connection is already known by Telecom.
2134      */
getAlreadyAddedConnection(String originalConnectionId)2135     Call getAlreadyAddedConnection(String originalConnectionId) {
2136         Optional<Call> existingCall = mCalls.stream()
2137                 .filter(call -> originalConnectionId.equals(call.getOriginalConnectionId()) ||
2138                             originalConnectionId.equals(call.getId()))
2139                 .findFirst();
2140 
2141         if (existingCall.isPresent()) {
2142             Log.i(this, "isExistingConnectionAlreadyAdded - call %s already added with id %s",
2143                     originalConnectionId, existingCall.get().getId());
2144             return existingCall.get();
2145         }
2146 
2147         return null;
2148     }
2149 
2150     /**
2151      * @return A new unique telecom call Id.
2152      */
getNextCallId()2153     private String getNextCallId() {
2154         synchronized(mLock) {
2155             return TELECOM_CALL_ID_PREFIX + (++mCallId);
2156         }
2157     }
2158 
2159     /**
2160      * Callback when foreground user is switched. We will reload missed call in all profiles
2161      * including the user itself. There may be chances that profiles are not started yet.
2162      */
onUserSwitch(UserHandle userHandle)2163     void onUserSwitch(UserHandle userHandle) {
2164         mCurrentUserHandle = userHandle;
2165         mMissedCallNotifier.setCurrentUserHandle(userHandle);
2166         final UserManager userManager = UserManager.get(mContext);
2167         List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier());
2168         for (UserInfo profile : profiles) {
2169             reloadMissedCallsOfUser(profile.getUserHandle());
2170         }
2171     }
2172 
2173     /**
2174      * Because there may be chances that profiles are not started yet though its parent user is
2175      * switched, we reload missed calls of profile that are just started here.
2176      */
onUserStarting(UserHandle userHandle)2177     void onUserStarting(UserHandle userHandle) {
2178         if (UserUtil.isProfile(mContext, userHandle)) {
2179             reloadMissedCallsOfUser(userHandle);
2180         }
2181     }
2182 
getLock()2183     public TelecomSystem.SyncRoot getLock() {
2184         return mLock;
2185     }
2186 
reloadMissedCallsOfUser(UserHandle userHandle)2187     private void reloadMissedCallsOfUser(UserHandle userHandle) {
2188         mMissedCallNotifier.reloadFromDatabase(
2189                 mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, userHandle);
2190     }
2191 
2192     /**
2193      * Dumps the state of the {@link CallsManager}.
2194      *
2195      * @param pw The {@code IndentingPrintWriter} to write the state to.
2196      */
dump(IndentingPrintWriter pw)2197     public void dump(IndentingPrintWriter pw) {
2198         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
2199         if (mCalls != null) {
2200             pw.println("mCalls: ");
2201             pw.increaseIndent();
2202             for (Call call : mCalls) {
2203                 pw.println(call);
2204             }
2205             pw.decreaseIndent();
2206         }
2207 
2208         if (mCallAudioManager != null) {
2209             pw.println("mCallAudioManager:");
2210             pw.increaseIndent();
2211             mCallAudioManager.dump(pw);
2212             pw.decreaseIndent();
2213         }
2214 
2215         if (mTtyManager != null) {
2216             pw.println("mTtyManager:");
2217             pw.increaseIndent();
2218             mTtyManager.dump(pw);
2219             pw.decreaseIndent();
2220         }
2221 
2222         if (mInCallController != null) {
2223             pw.println("mInCallController:");
2224             pw.increaseIndent();
2225             mInCallController.dump(pw);
2226             pw.decreaseIndent();
2227         }
2228 
2229         if (mConnectionServiceRepository != null) {
2230             pw.println("mConnectionServiceRepository:");
2231             pw.increaseIndent();
2232             mConnectionServiceRepository.dump(pw);
2233             pw.decreaseIndent();
2234         }
2235     }
2236 
2237     /**
2238     * For some disconnected causes, we show a dialog when it's a mmi code or potential mmi code.
2239     *
2240     * @param call The call.
2241     */
maybeShowErrorDialogOnDisconnect(Call call)2242     private void maybeShowErrorDialogOnDisconnect(Call call) {
2243         if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle())
2244                 || isPotentialInCallMMICode(call.getHandle()))) {
2245             DisconnectCause disconnectCause = call.getDisconnectCause();
2246             if (!TextUtils.isEmpty(disconnectCause.getDescription()) && (disconnectCause.getCode()
2247                     == DisconnectCause.ERROR)) {
2248                 Intent errorIntent = new Intent(mContext, ErrorDialogActivity.class);
2249                 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_STRING_EXTRA,
2250                         disconnectCause.getDescription());
2251                 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
2252                 mContext.startActivityAsUser(errorIntent, UserHandle.CURRENT);
2253             }
2254         }
2255     }
2256 
setIntentExtrasAndStartTime(Call call, Bundle extras)2257     private void setIntentExtrasAndStartTime(Call call, Bundle extras) {
2258       // Create our own instance to modify (since extras may be Bundle.EMPTY)
2259       extras = new Bundle(extras);
2260 
2261       // Specifies the time telecom began routing the call. This is used by the dialer for
2262       // analytics.
2263       extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS,
2264               SystemClock.elapsedRealtime());
2265 
2266       call.setIntentExtras(extras);
2267     }
2268 }
2269