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