CallsManager.java revision f5a32e4708729276b49226b2fdb819a46b7d2ce5
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);
404            }
405
406            mDtmfLocalTonePlayer.playTone(call, nextChar);
407
408            // TODO: Create a LockedRunnable class that does the synchronization automatically.
409            mStopTone = new Runnable() {
410                @Override
411                public void run() {
412                    synchronized (mLock) {
413                        // Set a timeout to stop the tone in case there isn't another tone to
414                        // follow.
415                        mDtmfLocalTonePlayer.stopTone(call);
416                    }
417                }
418            };
419            mHandler.postDelayed(
420                    mStopTone,
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);
428            }
429            mDtmfLocalTonePlayer.stopTone(call);
430        } else {
431            Log.w(this, "onPostDialChar: invalid value %d", nextChar);
432        }
433    }
434
435    @Override
436    public void onParentChanged(Call call) {
437        // parent-child relationship affects which call should be foreground, so do an update.
438        updateCallsManagerState();
439        for (CallsManagerListener listener : mListeners) {
440            listener.onIsConferencedChanged(call);
441        }
442    }
443
444    @Override
445    public void onChildrenChanged(Call call) {
446        // parent-child relationship affects which call should be foreground, so do an update.
447        updateCallsManagerState();
448        for (CallsManagerListener listener : mListeners) {
449            listener.onIsConferencedChanged(call);
450        }
451    }
452
453    @Override
454    public void onIsVoipAudioModeChanged(Call call) {
455        for (CallsManagerListener listener : mListeners) {
456            listener.onIsVoipAudioModeChanged(call);
457        }
458    }
459
460    @Override
461    public void onVideoStateChanged(Call call) {
462        for (CallsManagerListener listener : mListeners) {
463            listener.onVideoStateChanged(call);
464        }
465    }
466
467    @Override
468    public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
469        mPendingCallsToDisconnect.add(call);
470        mHandler.postDelayed(new Runnable() {
471            @Override
472            public void run() {
473                synchronized (mLock) {
474                    if (mPendingCallsToDisconnect.remove(call)) {
475                        Log.i(this, "Delayed disconnection of call: %s", call);
476                        call.disconnect();
477                    }
478                }
479            }
480        }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
481
482        return true;
483    }
484
485    /**
486     * Handles changes to the {@link Connection.VideoProvider} for a call.  Adds the
487     * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created
488     * in {@link Call#setVideoProvider(IVideoProvider)}.  This allows the {@link CallsManager} to
489     * respond to callbacks from the {@link VideoProviderProxy}.
490     *
491     * @param call The call.
492     */
493    @Override
494    public void onVideoCallProviderChanged(Call call) {
495        VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy();
496
497        if (videoProviderProxy == null) {
498            return;
499        }
500
501        videoProviderProxy.addListener(this);
502    }
503
504    /**
505     * Handles session modification requests received via the {@link TelecomVideoCallCallback} for
506     * a call.  Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session
507     * modification request.
508     *
509     * @param call The call.
510     * @param videoProfile The {@link VideoProfile}.
511     */
512    @Override
513    public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
514        int videoState = videoProfile != null ? videoProfile.getVideoState() :
515                VideoProfile.STATE_AUDIO_ONLY;
516        Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
517                .videoStateToString(videoState));
518
519        for (CallsManagerListener listener : mListeners) {
520            listener.onSessionModifyRequestReceived(call, videoProfile);
521        }
522    }
523
524    @VisibleForTesting
525    public Collection<Call> getCalls() {
526        return Collections.unmodifiableCollection(mCalls);
527    }
528
529    @VisibleForTesting
530    public Call getForegroundCall() {
531        if (mCallAudioManager == null) {
532            // Happens when getForegroundCall is called before full initialization.
533            return null;
534        }
535        return mCallAudioManager.getForegroundCall();
536    }
537
538    CallAudioManager getCallAudioManager() {
539        return mCallAudioManager;
540    }
541
542    InCallController getInCallController() {
543        return mInCallController;
544    }
545
546    boolean hasEmergencyCall() {
547        for (Call call : mCalls) {
548            if (call.isEmergencyCall()) {
549                return true;
550            }
551        }
552        return false;
553    }
554
555    boolean hasOnlyDisconnectedCalls() {
556        for (Call call : mCalls) {
557            if (!call.isDisconnected()) {
558                return false;
559            }
560        }
561        return true;
562    }
563
564    boolean hasVideoCall() {
565        for (Call call : mCalls) {
566            if (VideoProfile.isVideo(call.getVideoState())) {
567                return true;
568            }
569        }
570        return false;
571    }
572
573    CallAudioState getAudioState() {
574        return mCallAudioManager.getCallAudioState();
575    }
576
577    boolean isTtySupported() {
578        return mTtyManager.isTtySupported();
579    }
580
581    int getCurrentTtyMode() {
582        return mTtyManager.getCurrentTtyMode();
583    }
584
585    @VisibleForTesting
586    public void addListener(CallsManagerListener listener) {
587        mListeners.add(listener);
588    }
589
590    void removeListener(CallsManagerListener listener) {
591        mListeners.remove(listener);
592    }
593
594    /**
595     * Starts the process to attach the call to a connection service.
596     *
597     * @param phoneAccountHandle The phone account which contains the component name of the
598     *        connection service to use for this call.
599     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
600     */
601    void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
602        Log.d(this, "processIncomingCallIntent");
603        Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
604        if (handle == null) {
605            // Required for backwards compatibility
606            handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
607        }
608        Call call = new Call(
609                getNextCallId(),
610                mContext,
611                this,
612                mLock,
613                mConnectionServiceRepository,
614                mContactsAsyncHelper,
615                mCallerInfoAsyncQueryFactory,
616                handle,
617                null /* gatewayInfo */,
618                null /* connectionManagerPhoneAccount */,
619                phoneAccountHandle,
620                Call.CALL_DIRECTION_INCOMING /* callDirection */,
621                false /* forceAttachToExistingConnection */,
622                false /* isConference */
623        );
624
625        call.initAnalytics();
626        if (getForegroundCall() != null) {
627            getForegroundCall().getAnalytics().setCallIsInterrupted(true);
628            call.getAnalytics().setCallIsAdditional(true);
629        }
630
631        call.setIntentExtras(extras);
632        // TODO: Move this to be a part of addCall()
633        call.addListener(this);
634        call.startCreateConnection(mPhoneAccountRegistrar);
635    }
636
637    void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
638        Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
639        Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
640        Call call = new Call(
641                getNextCallId(),
642                mContext,
643                this,
644                mLock,
645                mConnectionServiceRepository,
646                mContactsAsyncHelper,
647                mCallerInfoAsyncQueryFactory,
648                handle,
649                null /* gatewayInfo */,
650                null /* connectionManagerPhoneAccount */,
651                phoneAccountHandle,
652                Call.CALL_DIRECTION_UNKNOWN /* callDirection */,
653                // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
654                // to the existing connection instead of trying to create a new one.
655                true /* forceAttachToExistingConnection */,
656                false /* isConference */
657        );
658        call.initAnalytics();
659
660        call.setIntentExtras(extras);
661        call.addListener(this);
662        call.startCreateConnection(mPhoneAccountRegistrar);
663    }
664
665    private boolean areHandlesEqual(Uri handle1, Uri handle2) {
666        if (handle1 == null || handle2 == null) {
667            return handle1 == handle2;
668        }
669
670        if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) {
671            return false;
672        }
673
674        final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart());
675        final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart());
676        return TextUtils.equals(number1, number2);
677    }
678
679    private Call reuseOutgoingCall(Uri handle) {
680        // Check to see if we can reuse any of the calls that are waiting to disconnect.
681        // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
682        Call reusedCall = null;
683        for (Call pendingCall : mPendingCallsToDisconnect) {
684            if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) {
685                mPendingCallsToDisconnect.remove(pendingCall);
686                Log.i(this, "Reusing disconnected call %s", pendingCall);
687                reusedCall = pendingCall;
688            } else {
689                Log.i(this, "Not reusing disconnected call %s", pendingCall);
690                pendingCall.disconnect();
691            }
692        }
693
694        return reusedCall;
695    }
696
697    /**
698     * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
699     *
700     * @param handle Handle to connect the call with.
701     * @param phoneAccountHandle The phone account which contains the component name of the
702     *        connection service to use for this call.
703     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
704     * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
705     */
706    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
707            UserHandle initiatingUser) {
708        boolean isReusedCall = true;
709        Call call = reuseOutgoingCall(handle);
710
711        // Create a call with original handle. The handle may be changed when the call is attached
712        // to a connection service, but in most cases will remain the same.
713        if (call == null) {
714            call = new Call(getNextCallId(), mContext,
715                    this,
716                    mLock,
717                    mConnectionServiceRepository,
718                    mContactsAsyncHelper,
719                    mCallerInfoAsyncQueryFactory,
720                    handle,
721                    null /* gatewayInfo */,
722                    null /* connectionManagerPhoneAccount */,
723                    null /* phoneAccountHandle */,
724                    Call.CALL_DIRECTION_OUTGOING /* callDirection */,
725                    false /* forceAttachToExistingConnection */,
726                    false /* isConference */
727            );
728            call.setInitiatingUser(initiatingUser);
729
730            call.initAnalytics();
731
732            isReusedCall = false;
733        }
734
735        List<PhoneAccountHandle> accounts =
736                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false,
737                        initiatingUser);
738        Log.v(this, "startOutgoingCall found accounts = " + accounts);
739
740        if (getForegroundCall() != null) {
741            Call ongoingCall = getForegroundCall();
742            // If there is an ongoing call, use the same phone account to place this new call.
743            // If the ongoing call is a conference call, we fetch the phone account from the
744            // child calls because we don't have targetPhoneAccount set on Conference calls.
745            // TODO: Set targetPhoneAccount for all conference calls (b/23035408).
746            if (ongoingCall.getTargetPhoneAccount() == null &&
747                    !ongoingCall.getChildCalls().isEmpty()) {
748                ongoingCall = ongoingCall.getChildCalls().get(0);
749            }
750            if (ongoingCall.getTargetPhoneAccount() != null) {
751                phoneAccountHandle = ongoingCall.getTargetPhoneAccount();
752            }
753        }
754
755        // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
756        // as if a phoneAccount was not specified (does the default behavior instead).
757        // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
758        if (phoneAccountHandle != null) {
759            if (!accounts.contains(phoneAccountHandle)) {
760                phoneAccountHandle = null;
761            }
762        }
763
764        if (phoneAccountHandle == null) {
765            // No preset account, check if default exists that supports the URI scheme for the
766            // handle.
767            phoneAccountHandle =
768                    mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(),
769                            initiatingUser);
770        }
771
772        call.setTargetPhoneAccount(phoneAccountHandle);
773
774        boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
775
776        // Do not support any more live calls.  Our options are to move a call to hold, disconnect
777        // a call, or cancel this call altogether. If a call is being reused, then it has already
778        // passed the makeRoomForOutgoingCall check once and will fail the second time due to the
779        // call transitioning into the CONNECTING state.
780        if (!isPotentialInCallMMICode && (!isReusedCall &&
781                !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
782            // just cancel at this point.
783            Log.i(this, "No remaining room for outgoing call: %s", call);
784            if (mCalls.contains(call)) {
785                // This call can already exist if it is a reused call,
786                // See {@link #reuseOutgoingCall}.
787                call.disconnect();
788            }
789            return null;
790        }
791
792        boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
793                !call.isEmergencyCall();
794
795        if (needsAccountSelection) {
796            // This is the state where the user is expected to select an account
797            call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
798            // Create our own instance to modify (since extras may be Bundle.EMPTY)
799            extras = new Bundle(extras);
800            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
801        } else {
802            call.setState(
803                    CallState.CONNECTING,
804                    phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
805        }
806
807        call.setIntentExtras(extras);
808
809        // Do not add the call if it is a potential MMI code.
810        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
811            call.addListener(this);
812        } else if (!mCalls.contains(call)) {
813            // We check if mCalls already contains the call because we could potentially be reusing
814            // a call which was previously added (See {@link #reuseOutgoingCall}).
815            addCall(call);
816        }
817
818        return call;
819    }
820
821    /**
822     * Attempts to issue/connect the specified call.
823     *
824     * @param handle Handle to connect the call with.
825     * @param gatewayInfo Optional gateway information that can be used to route the call to the
826     *        actual dialed handle via a gateway provider. May be null.
827     * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
828     * @param videoState The desired video state for the outgoing call.
829     */
830    @VisibleForTesting
831    public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
832            boolean speakerphoneOn, int videoState) {
833        if (call == null) {
834            // don't do anything if the call no longer exists
835            Log.i(this, "Canceling unknown call.");
836            return;
837        }
838
839        final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
840
841        if (gatewayInfo == null) {
842            Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
843        } else {
844            Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
845                    Log.pii(uriHandle), Log.pii(handle));
846        }
847
848        call.setHandle(uriHandle);
849        call.setGatewayInfo(gatewayInfo);
850
851        final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
852                R.bool.use_speaker_when_docked);
853        final boolean isDocked = mDockManager.isDocked();
854        final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabled(videoState);
855
856        // Auto-enable speakerphone if the originating intent specified to do so, if the call
857        // is a video call, of if using speaker when docked
858        call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
859                || (useSpeakerWhenDocked && isDocked));
860        call.setVideoState(videoState);
861
862        if (speakerphoneOn) {
863            Log.i(this, "%s Starting with speakerphone as requested", call);
864        } else if (useSpeakerWhenDocked && useSpeakerWhenDocked) {
865            Log.i(this, "%s Starting with speakerphone because car is docked.", call);
866        } else if (useSpeakerForVideoCall) {
867            Log.i(this, "%s Starting with speakerphone because its a video call.", call);
868        }
869
870        if (call.isEmergencyCall()) {
871            // Emergency -- CreateConnectionProcessor will choose accounts automatically
872            call.setTargetPhoneAccount(null);
873        }
874
875        final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
876                com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
877
878        if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
879            // If the account has been set, proceed to place the outgoing call.
880            // Otherwise the connection will be initiated when the account is set by the user.
881            call.startCreateConnection(mPhoneAccountRegistrar);
882        } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
883                requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false,
884                call.getInitiatingUser()).isEmpty()) {
885            // If there are no call capable accounts, disconnect the call.
886            markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
887                    "No registered PhoneAccounts"));
888            markCallAsRemoved(call);
889        }
890    }
891
892    /**
893     * Attempts to start a conference call for the specified call.
894     *
895     * @param call The call to conference.
896     * @param otherCall The other call to conference with.
897     */
898    @VisibleForTesting
899    public void conference(Call call, Call otherCall) {
900        call.conferenceWith(otherCall);
901    }
902
903    /**
904     * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
905     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
906     * the user opting to answer said call.
907     *
908     * @param call The call to answer.
909     * @param videoState The video state in which to answer the call.
910     */
911    @VisibleForTesting
912    public void answerCall(Call call, int videoState) {
913        if (!mCalls.contains(call)) {
914            Log.i(this, "Request to answer a non-existent call %s", call);
915        } else {
916            Call foregroundCall = getForegroundCall();
917            // If the foreground call is not the ringing call and it is currently isActive() or
918            // STATE_DIALING, put it on hold before answering the call.
919            if (foregroundCall != null && foregroundCall != call &&
920                    (foregroundCall.isActive() ||
921                     foregroundCall.getState() == CallState.DIALING)) {
922                if (0 == (foregroundCall.getConnectionCapabilities()
923                        & Connection.CAPABILITY_HOLD)) {
924                    // This call does not support hold.  If it is from a different connection
925                    // service, then disconnect it, otherwise allow the connection service to
926                    // figure out the right states.
927                    if (foregroundCall.getConnectionService() != call.getConnectionService()) {
928                        foregroundCall.disconnect();
929                    }
930                } else {
931                    Call heldCall = getHeldCall();
932                    if (heldCall != null) {
933                        Log.v(this, "Disconnecting held call %s before holding active call.",
934                                heldCall);
935                        heldCall.disconnect();
936                    }
937
938                    Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
939                            foregroundCall, call);
940                    foregroundCall.hold();
941                }
942                // TODO: Wait until we get confirmation of the active call being
943                // on-hold before answering the new call.
944                // TODO: Import logic from CallManager.acceptCall()
945            }
946
947            for (CallsManagerListener listener : mListeners) {
948                listener.onIncomingCallAnswered(call);
949            }
950
951            // We do not update the UI until we get confirmation of the answer() through
952            // {@link #markCallAsActive}.
953            call.answer(videoState);
954            if (isSpeakerphoneAutoEnabled(videoState)) {
955                call.setStartWithSpeakerphoneOn(true);
956            }
957        }
958    }
959
960    /**
961     * Determines if the speakerphone should be automatically enabled for the call.  Speakerphone
962     * should be enabled if the call is a video call and bluetooth or the wired headset are not in
963     * use.
964     *
965     * @param videoState The video state of the call.
966     * @return {@code true} if the speakerphone should be enabled.
967     */
968    private boolean isSpeakerphoneAutoEnabled(int videoState) {
969        return VideoProfile.isVideo(videoState) &&
970            !mWiredHeadsetManager.isPluggedIn() &&
971            !mBluetoothManager.isBluetoothAvailable() &&
972            isSpeakerEnabledForVideoCalls();
973    }
974
975    /**
976     * Determines if the speakerphone should be automatically enabled for video calls.
977     *
978     * @return {@code true} if the speakerphone should automatically be enabled.
979     */
980    private static boolean isSpeakerEnabledForVideoCalls() {
981        return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
982                PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
983                PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
984    }
985
986    /**
987     * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
988     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
989     * the user opting to reject said call.
990     */
991    @VisibleForTesting
992    public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
993        if (!mCalls.contains(call)) {
994            Log.i(this, "Request to reject a non-existent call %s", call);
995        } else {
996            for (CallsManagerListener listener : mListeners) {
997                listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
998            }
999            call.reject(rejectWithMessage, textMessage);
1000        }
1001    }
1002
1003    /**
1004     * Instructs Telecom to play the specified DTMF tone within the specified call.
1005     *
1006     * @param digit The DTMF digit to play.
1007     */
1008    @VisibleForTesting
1009    public void playDtmfTone(Call call, char digit) {
1010        if (!mCalls.contains(call)) {
1011            Log.i(this, "Request to play DTMF in a non-existent call %s", call);
1012        } else {
1013            call.playDtmfTone(digit);
1014            mDtmfLocalTonePlayer.playTone(call, digit);
1015        }
1016    }
1017
1018    /**
1019     * Instructs Telecom to stop the currently playing DTMF tone, if any.
1020     */
1021    @VisibleForTesting
1022    public void stopDtmfTone(Call call) {
1023        if (!mCalls.contains(call)) {
1024            Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
1025        } else {
1026            call.stopDtmfTone();
1027            mDtmfLocalTonePlayer.stopTone(call);
1028        }
1029    }
1030
1031    /**
1032     * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
1033     */
1034    void postDialContinue(Call call, boolean proceed) {
1035        if (!mCalls.contains(call)) {
1036            Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
1037        } else {
1038            call.postDialContinue(proceed);
1039        }
1040    }
1041
1042    /**
1043     * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
1044     * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
1045     * the user hitting the end-call button.
1046     */
1047    @VisibleForTesting
1048    public void disconnectCall(Call call) {
1049        Log.v(this, "disconnectCall %s", call);
1050
1051        if (!mCalls.contains(call)) {
1052            Log.w(this, "Unknown call (%s) asked to disconnect", call);
1053        } else {
1054            mLocallyDisconnectingCalls.add(call);
1055            call.disconnect();
1056        }
1057    }
1058
1059    /**
1060     * Instructs Telecom to disconnect all calls.
1061     */
1062    void disconnectAllCalls() {
1063        Log.v(this, "disconnectAllCalls");
1064
1065        for (Call call : mCalls) {
1066            disconnectCall(call);
1067        }
1068    }
1069
1070
1071    /**
1072     * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
1073     * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
1074     * the user hitting the hold button during an active call.
1075     */
1076    @VisibleForTesting
1077    public void holdCall(Call call) {
1078        if (!mCalls.contains(call)) {
1079            Log.w(this, "Unknown call (%s) asked to be put on hold", call);
1080        } else {
1081            Log.d(this, "Putting call on hold: (%s)", call);
1082            call.hold();
1083        }
1084    }
1085
1086    /**
1087     * Instructs Telecom to release the specified call from hold. Intended to be invoked by
1088     * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
1089     * by the user hitting the hold button during a held call.
1090     */
1091    @VisibleForTesting
1092    public void unholdCall(Call call) {
1093        if (!mCalls.contains(call)) {
1094            Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
1095        } else {
1096            Log.d(this, "unholding call: (%s)", call);
1097            for (Call c : mCalls) {
1098                // Only attempt to hold parent calls and not the individual children.
1099                if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
1100                    c.hold();
1101                }
1102            }
1103            call.unhold();
1104        }
1105    }
1106
1107    @Override
1108    public void onExtrasChanged(Call call) {
1109        if (call.getExtras() != null
1110                && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) {
1111
1112            Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get(
1113                    call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE));
1114            if (analyticsCallTechnology == null) {
1115                analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE;
1116            }
1117            call.getAnalytics().addCallTechnology(analyticsCallTechnology);
1118        }
1119    }
1120
1121    /** Called by the in-call UI to change the mute state. */
1122    void mute(boolean shouldMute) {
1123        mCallAudioManager.mute(shouldMute);
1124    }
1125
1126    /**
1127      * Called by the in-call UI to change the audio route, for example to change from earpiece to
1128      * speaker phone.
1129      */
1130    void setAudioRoute(int route) {
1131        mCallAudioManager.setAudioRoute(route);
1132    }
1133
1134    /** Called by the in-call UI to turn the proximity sensor on. */
1135    void turnOnProximitySensor() {
1136        mProximitySensorManager.turnOn();
1137    }
1138
1139    /**
1140     * Called by the in-call UI to turn the proximity sensor off.
1141     * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
1142     *        the screen will be kept off until the proximity sensor goes negative.
1143     */
1144    void turnOffProximitySensor(boolean screenOnImmediately) {
1145        mProximitySensorManager.turnOff(screenOnImmediately);
1146    }
1147
1148    void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
1149        if (!mCalls.contains(call)) {
1150            Log.i(this, "Attempted to add account to unknown call %s", call);
1151        } else {
1152            // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
1153            // the SELECT_PHONE_ACCOUNT sequence run in parallel, if the user selects an account before the
1154            // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
1155            // respecting a rewritten number or a canceled number. This is unlikely since
1156            // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
1157            // a phone account from the in-call UI.
1158            call.setTargetPhoneAccount(account);
1159
1160            // Note: emergency calls never go through account selection dialog so they never
1161            // arrive here.
1162            if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
1163                call.startCreateConnection(mPhoneAccountRegistrar);
1164            } else {
1165                call.disconnect();
1166            }
1167
1168            if (setDefault) {
1169                mPhoneAccountRegistrar
1170                        .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
1171            }
1172        }
1173    }
1174
1175    /** Called when the audio state changes. */
1176    @VisibleForTesting
1177    public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState
1178            newAudioState) {
1179        Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
1180        for (CallsManagerListener listener : mListeners) {
1181            listener.onCallAudioStateChanged(oldAudioState, newAudioState);
1182        }
1183    }
1184
1185    void markCallAsRinging(Call call) {
1186        setCallState(call, CallState.RINGING, "ringing set explicitly");
1187    }
1188
1189    void markCallAsDialing(Call call) {
1190        setCallState(call, CallState.DIALING, "dialing set explicitly");
1191        maybeMoveToSpeakerPhone(call);
1192    }
1193
1194    void markCallAsActive(Call call) {
1195        setCallState(call, CallState.ACTIVE, "active set explicitly");
1196        maybeMoveToSpeakerPhone(call);
1197    }
1198
1199    void markCallAsOnHold(Call call) {
1200        setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
1201    }
1202
1203    /**
1204     * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
1205     * last live call, then also disconnect from the in-call controller.
1206     *
1207     * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
1208     */
1209    void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
1210        call.setDisconnectCause(disconnectCause);
1211        setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly");
1212    }
1213
1214    /**
1215     * Removes an existing disconnected call, and notifies the in-call app.
1216     */
1217    void markCallAsRemoved(Call call) {
1218        removeCall(call);
1219        if (mLocallyDisconnectingCalls.contains(call)) {
1220            mLocallyDisconnectingCalls.remove(call);
1221            Call foregroundCall = getForegroundCall();
1222            if (foregroundCall != null && foregroundCall.getState() == CallState.ON_HOLD) {
1223                foregroundCall.unhold();
1224            }
1225        }
1226    }
1227
1228    /**
1229     * Cleans up any calls currently associated with the specified connection service when the
1230     * service binder disconnects unexpectedly.
1231     *
1232     * @param service The connection service that disconnected.
1233     */
1234    void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
1235        if (service != null) {
1236            for (Call call : mCalls) {
1237                if (call.getConnectionService() == service) {
1238                    if (call.getState() != CallState.DISCONNECTED) {
1239                        markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
1240                    }
1241                    markCallAsRemoved(call);
1242                }
1243            }
1244        }
1245    }
1246
1247    boolean hasAnyCalls() {
1248        return !mCalls.isEmpty();
1249    }
1250
1251    boolean hasActiveOrHoldingCall() {
1252        return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
1253    }
1254
1255    boolean hasRingingCall() {
1256        return getFirstCallWithState(CallState.RINGING) != null;
1257    }
1258
1259    boolean onMediaButton(int type) {
1260        if (hasAnyCalls()) {
1261            if (HeadsetMediaButton.SHORT_PRESS == type) {
1262                Call ringingCall = getFirstCallWithState(CallState.RINGING);
1263                if (ringingCall == null) {
1264                    mCallAudioManager.toggleMute();
1265                    return true;
1266                } else {
1267                    ringingCall.answer(ringingCall.getVideoState());
1268                    return true;
1269                }
1270            } else if (HeadsetMediaButton.LONG_PRESS == type) {
1271                Log.d(this, "handleHeadsetHook: longpress -> hangup");
1272                Call callToHangup = getFirstCallWithState(
1273                        CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
1274                if (callToHangup != null) {
1275                    callToHangup.disconnect();
1276                    return true;
1277                }
1278            }
1279        }
1280        return false;
1281    }
1282
1283    /**
1284     * Returns true if telecom supports adding another top-level call.
1285     */
1286    boolean canAddCall() {
1287        boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
1288                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
1289        if (!isDeviceProvisioned) {
1290            Log.d(TAG, "Device not provisioned, canAddCall is false.");
1291            return false;
1292        }
1293
1294        if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
1295            return false;
1296        }
1297
1298        int count = 0;
1299        for (Call call : mCalls) {
1300            if (call.isEmergencyCall()) {
1301                // We never support add call if one of the calls is an emergency call.
1302                return false;
1303            } else  if (!call.getChildCalls().isEmpty() && !call.can(Connection.CAPABILITY_HOLD)) {
1304                // This is to deal with CDMA conference calls. CDMA conference calls do not
1305                // allow the addition of another call when it is already in a 3 way conference.
1306                // So, we detect that it is a CDMA conference call by checking if the call has
1307                // some children and it does not support the CAPABILILTY_HOLD
1308                // TODO: This maybe cleaner if the lower layers can explicitly signal to telecom
1309                // about this limitation (b/22880180).
1310                return false;
1311            } else if (call.getParentCall() == null) {
1312                count++;
1313            }
1314
1315            // We do not check states for canAddCall. We treat disconnected calls the same
1316            // and wait until they are removed instead. If we didn't count disconnected calls,
1317            // we could put InCallServices into a state where they are showing two calls but
1318            // also support add-call. Technically it's right, but overall looks better (UI-wise)
1319            // and acts better if we wait until the call is removed.
1320            if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
1321                return false;
1322            }
1323        }
1324        return true;
1325    }
1326
1327    @VisibleForTesting
1328    public Call getRingingCall() {
1329        return getFirstCallWithState(CallState.RINGING);
1330    }
1331
1332    @VisibleForTesting
1333    public Call getActiveCall() {
1334        return getFirstCallWithState(CallState.ACTIVE);
1335    }
1336
1337    Call getDialingCall() {
1338        return getFirstCallWithState(CallState.DIALING);
1339    }
1340
1341    @VisibleForTesting
1342    public Call getHeldCall() {
1343        return getFirstCallWithState(CallState.ON_HOLD);
1344    }
1345
1346    @VisibleForTesting
1347    public int getNumHeldCalls() {
1348        int count = 0;
1349        for (Call call : mCalls) {
1350            if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
1351                count++;
1352            }
1353        }
1354        return count;
1355    }
1356
1357    @VisibleForTesting
1358    public Call getOutgoingCall() {
1359        return getFirstCallWithState(OUTGOING_CALL_STATES);
1360    }
1361
1362    @VisibleForTesting
1363    public Call getFirstCallWithState(int... states) {
1364        return getFirstCallWithState(null, states);
1365    }
1366
1367    /**
1368     * Returns the first call that it finds with the given states. The states are treated as having
1369     * priority order so that any call with the first state will be returned before any call with
1370     * states listed later in the parameter list.
1371     *
1372     * @param callToSkip Call that this method should skip while searching
1373     */
1374    Call getFirstCallWithState(Call callToSkip, int... states) {
1375        for (int currentState : states) {
1376            // check the foreground first
1377            Call foregroundCall = getForegroundCall();
1378            if (foregroundCall != null && foregroundCall.getState() == currentState) {
1379                return foregroundCall;
1380            }
1381
1382            for (Call call : mCalls) {
1383                if (Objects.equals(callToSkip, call)) {
1384                    continue;
1385                }
1386
1387                // Only operate on top-level calls
1388                if (call.getParentCall() != null) {
1389                    continue;
1390                }
1391
1392                if (currentState == call.getState()) {
1393                    return call;
1394                }
1395            }
1396        }
1397        return null;
1398    }
1399
1400    Call createConferenceCall(
1401            String callId,
1402            PhoneAccountHandle phoneAccount,
1403            ParcelableConference parcelableConference) {
1404
1405        // If the parceled conference specifies a connect time, use it; otherwise default to 0,
1406        // which is the default value for new Calls.
1407        long connectTime =
1408                parcelableConference.getConnectTimeMillis() ==
1409                        Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
1410                        parcelableConference.getConnectTimeMillis();
1411
1412        Call call = new Call(
1413                callId,
1414                mContext,
1415                this,
1416                mLock,
1417                mConnectionServiceRepository,
1418                mContactsAsyncHelper,
1419                mCallerInfoAsyncQueryFactory,
1420                null /* handle */,
1421                null /* gatewayInfo */,
1422                null /* connectionManagerPhoneAccount */,
1423                phoneAccount,
1424                Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
1425                false /* forceAttachToExistingConnection */,
1426                true /* isConference */,
1427                connectTime);
1428
1429        setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
1430                "new conference call");
1431        call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
1432        call.setVideoState(parcelableConference.getVideoState());
1433        call.setVideoProvider(parcelableConference.getVideoProvider());
1434        call.setStatusHints(parcelableConference.getStatusHints());
1435        call.setExtras(parcelableConference.getExtras());
1436
1437        // TODO: Move this to be a part of addCall()
1438        call.addListener(this);
1439        addCall(call);
1440        return call;
1441    }
1442
1443    /**
1444     * @return the call state currently tracked by {@link PhoneStateBroadcaster}
1445     */
1446    int getCallState() {
1447        return mPhoneStateBroadcaster.getCallState();
1448    }
1449
1450    /**
1451     * Retrieves the {@link PhoneAccountRegistrar}.
1452     *
1453     * @return The {@link PhoneAccountRegistrar}.
1454     */
1455    PhoneAccountRegistrar getPhoneAccountRegistrar() {
1456        return mPhoneAccountRegistrar;
1457    }
1458
1459    /**
1460     * Retrieves the {@link MissedCallNotifier}
1461     * @return The {@link MissedCallNotifier}.
1462     */
1463    MissedCallNotifier getMissedCallNotifier() {
1464        return mMissedCallNotifier;
1465    }
1466
1467    /**
1468     * Reject an incoming call and manually add it to the Call Log.
1469     * @param incomingCall Incoming call that has been rejected
1470     */
1471    private void rejectCallAndLog(Call incomingCall) {
1472        incomingCall.reject(false, null);
1473        // Since the call was not added to the list of calls, we have to call the missed
1474        // call notifier and the call logger manually.
1475        // Do we need missed call notification for direct to Voicemail calls?
1476        mMissedCallNotifier.showMissedCallNotification(incomingCall);
1477        mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
1478    }
1479
1480    /**
1481     * Adds the specified call to the main list of live calls.
1482     *
1483     * @param call The call to add.
1484     */
1485    private void addCall(Call call) {
1486        Trace.beginSection("addCall");
1487        Log.v(this, "addCall(%s)", call);
1488        call.addListener(this);
1489        mCalls.add(call);
1490
1491        updateCallsManagerState();
1492        // onCallAdded for calls which immediately take the foreground (like the first call).
1493        for (CallsManagerListener listener : mListeners) {
1494            if (Log.SYSTRACE_DEBUG) {
1495                Trace.beginSection(listener.getClass().toString() + " addCall");
1496            }
1497            listener.onCallAdded(call);
1498            if (Log.SYSTRACE_DEBUG) {
1499                Trace.endSection();
1500            }
1501        }
1502        Trace.endSection();
1503    }
1504
1505    private void removeCall(Call call) {
1506        Trace.beginSection("removeCall");
1507        Log.v(this, "removeCall(%s)", call);
1508
1509        call.setParentCall(null);  // need to clean up parent relationship before destroying.
1510        call.removeListener(this);
1511        call.clearConnectionService();
1512
1513        boolean shouldNotify = false;
1514        if (mCalls.contains(call)) {
1515            mCalls.remove(call);
1516            shouldNotify = true;
1517        }
1518
1519        call.destroy();
1520
1521        // Only broadcast changes for calls that are being tracked.
1522        if (shouldNotify) {
1523            updateCallsManagerState();
1524            for (CallsManagerListener listener : mListeners) {
1525                if (Log.SYSTRACE_DEBUG) {
1526                    Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
1527                }
1528                listener.onCallRemoved(call);
1529                if (Log.SYSTRACE_DEBUG) {
1530                    Trace.endSection();
1531                }
1532            }
1533        }
1534        Trace.endSection();
1535    }
1536
1537    /**
1538     * Sets the specified state on the specified call.
1539     *
1540     * @param call The call.
1541     * @param newState The new state of the call.
1542     */
1543    private void setCallState(Call call, int newState, String tag) {
1544        if (call == null) {
1545            return;
1546        }
1547        int oldState = call.getState();
1548        Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
1549                CallState.toString(newState), call);
1550        if (newState != oldState) {
1551            // Unfortunately, in the telephony world the radio is king. So if the call notifies
1552            // us that the call is in a particular state, we allow it even if it doesn't make
1553            // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
1554            // TODO: Consider putting a stop to the above and turning CallState
1555            // into a well-defined state machine.
1556            // TODO: Define expected state transitions here, and log when an
1557            // unexpected transition occurs.
1558            call.setState(newState, tag);
1559
1560            Trace.beginSection("onCallStateChanged");
1561            // Only broadcast state change for calls that are being tracked.
1562            if (mCalls.contains(call)) {
1563                updateCallsManagerState();
1564                for (CallsManagerListener listener : mListeners) {
1565                    if (Log.SYSTRACE_DEBUG) {
1566                        Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
1567                    }
1568                    listener.onCallStateChanged(call, oldState, newState);
1569                    if (Log.SYSTRACE_DEBUG) {
1570                        Trace.endSection();
1571                    }
1572                }
1573            }
1574            Trace.endSection();
1575        }
1576    }
1577
1578    private void updateCanAddCall() {
1579        boolean newCanAddCall = canAddCall();
1580        if (newCanAddCall != mCanAddCall) {
1581            mCanAddCall = newCanAddCall;
1582            for (CallsManagerListener listener : mListeners) {
1583                if (Log.SYSTRACE_DEBUG) {
1584                    Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
1585                }
1586                listener.onCanAddCallChanged(mCanAddCall);
1587                if (Log.SYSTRACE_DEBUG) {
1588                    Trace.endSection();
1589                }
1590            }
1591        }
1592    }
1593
1594    private void updateCallsManagerState() {
1595        updateCanAddCall();
1596    }
1597
1598    private boolean isPotentialMMICode(Uri handle) {
1599        return (handle != null && handle.getSchemeSpecificPart() != null
1600                && handle.getSchemeSpecificPart().contains("#"));
1601    }
1602
1603    /**
1604     * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
1605     * MMI codes which can be dialed when one or more calls are in progress.
1606     * <P>
1607     * Checks for numbers formatted similar to the MMI codes defined in:
1608     * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)}
1609     *
1610     * @param handle The URI to call.
1611     * @return {@code True} if the URI represents a number which could be an in-call MMI code.
1612     */
1613    private boolean isPotentialInCallMMICode(Uri handle) {
1614        if (handle != null && handle.getSchemeSpecificPart() != null &&
1615                handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
1616
1617            String dialedNumber = handle.getSchemeSpecificPart();
1618            return (dialedNumber.equals("0") ||
1619                    (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
1620                    (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
1621                    dialedNumber.equals("3") ||
1622                    dialedNumber.equals("4") ||
1623                    dialedNumber.equals("5"));
1624        }
1625        return false;
1626    }
1627
1628    private int getNumCallsWithState(int... states) {
1629        int count = 0;
1630        for (int state : states) {
1631            for (Call call : mCalls) {
1632                if (call.getParentCall() == null && call.getState() == state) {
1633                    count++;
1634                }
1635            }
1636        }
1637        return count;
1638    }
1639
1640    private boolean hasMaximumLiveCalls() {
1641        return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
1642    }
1643
1644    private boolean hasMaximumHoldingCalls() {
1645        return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
1646    }
1647
1648    private boolean hasMaximumRingingCalls() {
1649        return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
1650    }
1651
1652    private boolean hasMaximumOutgoingCalls() {
1653        return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
1654    }
1655
1656    private boolean hasMaximumDialingCalls() {
1657        return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(CallState.DIALING);
1658    }
1659
1660    private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
1661        if (hasMaximumLiveCalls()) {
1662            // NOTE: If the amount of live calls changes beyond 1, this logic will probably
1663            // have to change.
1664            Call liveCall = getFirstCallWithState(LIVE_CALL_STATES);
1665            Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
1666                   liveCall);
1667
1668            if (call == liveCall) {
1669                // If the call is already the foreground call, then we are golden.
1670                // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT
1671                // state since the call was already populated into the list.
1672                return true;
1673            }
1674
1675            if (hasMaximumOutgoingCalls()) {
1676                Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
1677                if (isEmergency && !outgoingCall.isEmergencyCall()) {
1678                    // Disconnect the current outgoing call if it's not an emergency call. If the
1679                    // user tries to make two outgoing calls to different emergency call numbers,
1680                    // we will try to connect the first outgoing call.
1681                    call.getAnalytics().setCallIsAdditional(true);
1682                    outgoingCall.getAnalytics().setCallIsInterrupted(true);
1683                    outgoingCall.disconnect();
1684                    return true;
1685                }
1686                if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
1687                    // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT}
1688                    // state, just disconnect it since the user has explicitly started a new call.
1689                    call.getAnalytics().setCallIsAdditional(true);
1690                    outgoingCall.getAnalytics().setCallIsInterrupted(true);
1691                    outgoingCall.disconnect();
1692                    return true;
1693                }
1694                return false;
1695            }
1696
1697            if (hasMaximumHoldingCalls()) {
1698                // There is no more room for any more calls, unless it's an emergency.
1699                if (isEmergency) {
1700                    // Kill the current active call, this is easier then trying to disconnect a
1701                    // holding call and hold an active call.
1702                    call.getAnalytics().setCallIsAdditional(true);
1703                    liveCall.getAnalytics().setCallIsInterrupted(true);
1704                    liveCall.disconnect();
1705                    return true;
1706                }
1707                return false;  // No more room!
1708            }
1709
1710            // We have room for at least one more holding call at this point.
1711
1712            // TODO: Remove once b/23035408 has been corrected.
1713            // If the live call is a conference, it will not have a target phone account set.  This
1714            // means the check to see if the live call has the same target phone account as the new
1715            // call will not cause us to bail early.  As a result, we'll end up holding the
1716            // ongoing conference call.  However, the ConnectionService is already doing that.  This
1717            // has caused problems with some carriers.  As a workaround until b/23035408 is
1718            // corrected, we will try and get the target phone account for one of the conference's
1719            // children and use that instead.
1720            PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount();
1721            if (liveCallPhoneAccount == null && liveCall.isConference() &&
1722                    !liveCall.getChildCalls().isEmpty()) {
1723                liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall);
1724                Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " +
1725                        liveCallPhoneAccount);
1726            }
1727
1728            // First thing, if we are trying to make a call with the same phone account as the live
1729            // call, then allow it so that the connection service can make its own decision about
1730            // how to handle the new call relative to the current one.
1731            if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) {
1732                Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches.");
1733                call.getAnalytics().setCallIsAdditional(true);
1734                liveCall.getAnalytics().setCallIsInterrupted(true);
1735                return true;
1736            } else if (call.getTargetPhoneAccount() == null) {
1737                // Without a phone account, we can't say reliably that the call will fail.
1738                // If the user chooses the same phone account as the live call, then it's
1739                // still possible that the call can be made (like with CDMA calls not supporting
1740                // hold but they still support adding a call by going immediately into conference
1741                // mode). Return true here and we'll run this code again after user chooses an
1742                // account.
1743                return true;
1744            }
1745
1746            // Try to hold the live call before attempting the new outgoing call.
1747            if (liveCall.can(Connection.CAPABILITY_HOLD)) {
1748                Log.i(this, "makeRoomForOutgoingCall: holding live call.");
1749                call.getAnalytics().setCallIsAdditional(true);
1750                liveCall.getAnalytics().setCallIsInterrupted(true);
1751                liveCall.hold();
1752                return true;
1753            }
1754
1755            // The live call cannot be held so we're out of luck here.  There's no room.
1756            return false;
1757        }
1758        return true;
1759    }
1760
1761    /**
1762     * Given a call, find the first non-null phone account handle of its children.
1763     *
1764     * @param parentCall The parent call.
1765     * @return The first non-null phone account handle of the children, or {@code null} if none.
1766     */
1767    private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) {
1768        for (Call childCall : parentCall.getChildCalls()) {
1769            PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount();
1770            if (childPhoneAccount != null) {
1771                return childPhoneAccount;
1772            }
1773        }
1774        return null;
1775    }
1776
1777    /**
1778     * Checks to see if the call should be on speakerphone and if so, set it.
1779     */
1780    private void maybeMoveToSpeakerPhone(Call call) {
1781        if (call.getStartWithSpeakerphoneOn()) {
1782            setAudioRoute(CallAudioState.ROUTE_SPEAKER);
1783            call.setStartWithSpeakerphoneOn(false);
1784        }
1785    }
1786
1787    /**
1788     * Creates a new call for an existing connection.
1789     *
1790     * @param callId The id of the new call.
1791     * @param connection The connection information.
1792     * @return The new call.
1793     */
1794    Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
1795        Call call = new Call(
1796                callId,
1797                mContext,
1798                this,
1799                mLock,
1800                mConnectionServiceRepository,
1801                mContactsAsyncHelper,
1802                mCallerInfoAsyncQueryFactory,
1803                connection.getHandle() /* handle */,
1804                null /* gatewayInfo */,
1805                null /* connectionManagerPhoneAccount */,
1806                connection.getPhoneAccount(), /* targetPhoneAccountHandle */
1807                Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
1808                false /* forceAttachToExistingConnection */,
1809                false /* isConference */,
1810                connection.getConnectTimeMillis() /* connectTimeMillis */);
1811
1812        call.initAnalytics();
1813        call.getAnalytics().setCreatedFromExistingConnection(true);
1814
1815        setCallState(call, Call.getStateFromConnectionState(connection.getState()),
1816                "existing connection");
1817        call.setConnectionCapabilities(connection.getConnectionCapabilities());
1818        call.setCallerDisplayName(connection.getCallerDisplayName(),
1819                connection.getCallerDisplayNamePresentation());
1820
1821        call.addListener(this);
1822        addCall(call);
1823
1824        return call;
1825    }
1826
1827    /**
1828     * @return A new unique telecom call Id.
1829     */
1830    private String getNextCallId() {
1831        synchronized(mLock) {
1832            return TELECOM_CALL_ID_PREFIX + (++mCallId);
1833        }
1834    }
1835
1836    /**
1837     * Callback when foreground user is switched. We will reload missed call in all profiles
1838     * including the user itself. There may be chances that profiles are not started yet.
1839     */
1840    void onUserSwitch(UserHandle userHandle) {
1841        mMissedCallNotifier.setCurrentUserHandle(userHandle);
1842        final UserManager userManager = UserManager.get(mContext);
1843        List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier());
1844        for (UserInfo profile : profiles) {
1845            reloadMissedCallsOfUser(profile.getUserHandle());
1846        }
1847    }
1848
1849    /**
1850     * Because there may be chances that profiles are not started yet though its parent user is
1851     * switched, we reload missed calls of profile that are just started here.
1852     */
1853    void onUserStarting(UserHandle userHandle) {
1854        if (UserUtil.isProfile(mContext, userHandle)) {
1855            reloadMissedCallsOfUser(userHandle);
1856        }
1857    }
1858
1859    private void reloadMissedCallsOfUser(UserHandle userHandle) {
1860        mMissedCallNotifier.reloadFromDatabase(
1861                mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, userHandle);
1862    }
1863
1864    /**
1865     * Dumps the state of the {@link CallsManager}.
1866     *
1867     * @param pw The {@code IndentingPrintWriter} to write the state to.
1868     */
1869    public void dump(IndentingPrintWriter pw) {
1870        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1871        if (mCalls != null) {
1872            pw.println("mCalls: ");
1873            pw.increaseIndent();
1874            for (Call call : mCalls) {
1875                pw.println(call);
1876            }
1877            pw.decreaseIndent();
1878        }
1879
1880        if (mCallAudioManager != null) {
1881            pw.println("mCallAudioManager:");
1882            pw.increaseIndent();
1883            mCallAudioManager.dump(pw);
1884            pw.decreaseIndent();
1885        }
1886
1887        if (mTtyManager != null) {
1888            pw.println("mTtyManager:");
1889            pw.increaseIndent();
1890            mTtyManager.dump(pw);
1891            pw.decreaseIndent();
1892        }
1893
1894        if (mInCallController != null) {
1895            pw.println("mInCallController:");
1896            pw.increaseIndent();
1897            mInCallController.dump(pw);
1898            pw.decreaseIndent();
1899        }
1900
1901        if (mConnectionServiceRepository != null) {
1902            pw.println("mConnectionServiceRepository:");
1903            pw.increaseIndent();
1904            mConnectionServiceRepository.dump(pw);
1905            pw.decreaseIndent();
1906        }
1907    }
1908}
1909