CallsManager.java revision a469f76c1710a945933910840f11f9fda0445d82
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.net.Uri;
21import android.os.Bundle;
22import android.os.Handler;
23import android.os.Trace;
24import android.provider.CallLog.Calls;
25import android.telecom.AudioState;
26import android.telecom.CallState;
27import android.telecom.Connection;
28import android.telecom.DisconnectCause;
29import android.telecom.GatewayInfo;
30import android.telecom.ParcelableConference;
31import android.telecom.ParcelableConnection;
32import android.telecom.PhoneAccount;
33import android.telecom.PhoneAccountHandle;
34import android.telecom.TelecomManager;
35import android.telecom.VideoProfile;
36import android.telephony.PhoneNumberUtils;
37import android.telephony.TelephonyManager;
38
39import com.android.internal.util.IndentingPrintWriter;
40
41import java.util.Collection;
42import java.util.Collections;
43import java.util.HashSet;
44import java.util.List;
45import java.util.Objects;
46import java.util.Set;
47import java.util.concurrent.ConcurrentHashMap;
48
49/**
50 * Singleton.
51 *
52 * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
53 * access from other packages specifically refraining from passing the CallsManager instance
54 * beyond the com.android.server.telecom package boundary.
55 */
56public final class CallsManager extends Call.ListenerBase {
57
58    // TODO: Consider renaming this CallsManagerPlugin.
59    interface CallsManagerListener {
60        void onCallAdded(Call call);
61        void onCallRemoved(Call call);
62        void onCallStateChanged(Call call, int oldState, int newState);
63        void onConnectionServiceChanged(
64                Call call,
65                ConnectionServiceWrapper oldService,
66                ConnectionServiceWrapper newService);
67        void onIncomingCallAnswered(Call call);
68        void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
69        void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall);
70        void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState);
71        void onRingbackRequested(Call call, boolean ringback);
72        void onIsConferencedChanged(Call call);
73        void onIsVoipAudioModeChanged(Call call);
74        void onVideoStateChanged(Call call);
75        void onCanAddCallChanged(boolean canAddCall);
76    }
77
78    /**
79     * Singleton instance of the {@link CallsManager}, initialized from {@link TelecomService}.
80     */
81    private static CallsManager sInstance = null;
82
83    private static final String TAG = "CallsManager";
84
85    private static final int MAXIMUM_LIVE_CALLS = 1;
86    private static final int MAXIMUM_HOLD_CALLS = 1;
87    private static final int MAXIMUM_RINGING_CALLS = 1;
88    private static final int MAXIMUM_OUTGOING_CALLS = 1;
89    private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
90
91    private static final int[] OUTGOING_CALL_STATES =
92            {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING};
93
94    private static final int[] LIVE_CALL_STATES =
95            {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE};
96
97    /**
98     * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
99     * calls are added to the map and removed when the calls move to the disconnected state.
100     *
101     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
102     * load factor before resizing, 1 means we only expect a single thread to
103     * access the map so make only a single shard
104     */
105    private final Set<Call> mCalls = Collections.newSetFromMap(
106            new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
107
108    private final ConnectionServiceRepository mConnectionServiceRepository;
109    private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
110    private final InCallController mInCallController;
111    private final CallAudioManager mCallAudioManager;
112    private final Ringer mRinger;
113    private final InCallWakeLockController mInCallWakeLockController;
114    // For this set initial table size to 16 because we add 13 listeners in
115    // the CallsManager constructor.
116    private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
117            new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
118    private final HeadsetMediaButton mHeadsetMediaButton;
119    private final WiredHeadsetManager mWiredHeadsetManager;
120    private final TtyManager mTtyManager;
121    private final ProximitySensorManager mProximitySensorManager;
122    private final PhoneStateBroadcaster mPhoneStateBroadcaster;
123    private final CallLogManager mCallLogManager;
124    private final Context mContext;
125    private final PhoneAccountRegistrar mPhoneAccountRegistrar;
126    private final MissedCallNotifier mMissedCallNotifier;
127    private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
128    private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
129    /* Handler tied to thread in which CallManager was initialized. */
130    private final Handler mHandler = new Handler();
131
132    private boolean mCanAddCall = true;
133
134    /**
135     * The call the user is currently interacting with. This is the call that should have audio
136     * focus and be visible in the in-call UI.
137     */
138    private Call mForegroundCall;
139
140    private Runnable mStopTone;
141
142    /** Singleton accessor. */
143    static CallsManager getInstance() {
144        return sInstance;
145    }
146
147    /**
148     * Sets the static singleton instance.
149     *
150     * @param instance The instance to set.
151     */
152    static void initialize(CallsManager instance) {
153        sInstance = instance;
154    }
155
156    /**
157     * Initializes the required Telecom components.
158     */
159     CallsManager(Context context, MissedCallNotifier missedCallNotifier,
160             PhoneAccountRegistrar phoneAccountRegistrar) {
161        mContext = context;
162        mPhoneAccountRegistrar = phoneAccountRegistrar;
163        mMissedCallNotifier = missedCallNotifier;
164        StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
165        mWiredHeadsetManager = new WiredHeadsetManager(context);
166        mCallAudioManager = new CallAudioManager(context, statusBarNotifier, mWiredHeadsetManager);
167        InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager);
168        mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
169        mHeadsetMediaButton = new HeadsetMediaButton(context, this);
170        mTtyManager = new TtyManager(context, mWiredHeadsetManager);
171        mProximitySensorManager = new ProximitySensorManager(context);
172        mPhoneStateBroadcaster = new PhoneStateBroadcaster();
173        mCallLogManager = new CallLogManager(context);
174        mInCallController = new InCallController(context);
175        mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
176        mConnectionServiceRepository = new ConnectionServiceRepository(mPhoneAccountRegistrar,
177                context);
178        mInCallWakeLockController = new InCallWakeLockController(context, this);
179
180        mListeners.add(statusBarNotifier);
181        mListeners.add(mCallLogManager);
182        mListeners.add(mPhoneStateBroadcaster);
183        mListeners.add(mInCallController);
184        mListeners.add(mRinger);
185        mListeners.add(new RingbackPlayer(this, playerFactory));
186        mListeners.add(new InCallToneMonitor(playerFactory, this));
187        mListeners.add(mCallAudioManager);
188        mListeners.add(missedCallNotifier);
189        mListeners.add(mDtmfLocalTonePlayer);
190        mListeners.add(mHeadsetMediaButton);
191        mListeners.add(RespondViaSmsManager.getInstance());
192        mListeners.add(mProximitySensorManager);
193    }
194
195    @Override
196    public void onSuccessfulOutgoingCall(Call call, int callState) {
197        Log.v(this, "onSuccessfulOutgoingCall, %s", call);
198
199        setCallState(call, callState);
200        if (!mCalls.contains(call)) {
201            // Call was not added previously in startOutgoingCall due to it being a potential MMI
202            // code, so add it now.
203            addCall(call);
204        }
205
206        // The call's ConnectionService has been updated.
207        for (CallsManagerListener listener : mListeners) {
208            listener.onConnectionServiceChanged(call, null, call.getConnectionService());
209        }
210
211        markCallAsDialing(call);
212    }
213
214    @Override
215    public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
216        Log.v(this, "onFailedOutgoingCall, call: %s", call);
217
218        markCallAsRemoved(call);
219    }
220
221    @Override
222    public void onSuccessfulIncomingCall(Call incomingCall) {
223        Log.d(this, "onSuccessfulIncomingCall");
224        setCallState(incomingCall, CallState.RINGING);
225
226        if (hasMaximumRingingCalls()) {
227            incomingCall.reject(false, null);
228            // since the call was not added to the list of calls, we have to call the missed
229            // call notifier and the call logger manually.
230            mMissedCallNotifier.showMissedCallNotification(incomingCall);
231            mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
232        } else {
233            addCall(incomingCall);
234        }
235    }
236
237    @Override
238    public void onFailedIncomingCall(Call call) {
239        setCallState(call, CallState.DISCONNECTED);
240        call.removeListener(this);
241    }
242
243    @Override
244    public void onSuccessfulUnknownCall(Call call, int callState) {
245        setCallState(call, callState);
246        Log.i(this, "onSuccessfulUnknownCall for call %s", call);
247        addCall(call);
248    }
249
250    @Override
251    public void onFailedUnknownCall(Call call) {
252        Log.i(this, "onFailedUnknownCall for call %s", call);
253        setCallState(call, CallState.DISCONNECTED);
254        call.removeListener(this);
255    }
256
257    @Override
258    public void onRingbackRequested(Call call, boolean ringback) {
259        for (CallsManagerListener listener : mListeners) {
260            listener.onRingbackRequested(call, ringback);
261        }
262    }
263
264    @Override
265    public void onPostDialWait(Call call, String remaining) {
266        mInCallController.onPostDialWait(call, remaining);
267    }
268
269    @Override
270    public void onPostDialChar(final Call call, char nextChar) {
271        if (PhoneNumberUtils.is12Key(nextChar)) {
272            // Play tone if it is one of the dialpad digits, canceling out the previously queued
273            // up stopTone runnable since playing a new tone automatically stops the previous tone.
274            if (mStopTone != null) {
275                mHandler.removeCallbacks(mStopTone);
276            }
277
278            mDtmfLocalTonePlayer.playTone(call, nextChar);
279
280            mStopTone = new Runnable() {
281                @Override
282                public void run() {
283                    // Set a timeout to stop the tone in case there isn't another tone to follow.
284                    mDtmfLocalTonePlayer.stopTone(call);
285                }
286            };
287            mHandler.postDelayed(
288                    mStopTone,
289                    Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
290        } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
291                nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
292            // Stop the tone if a tone is playing, removing any other stopTone callbacks since
293            // the previous tone is being stopped anyway.
294            if (mStopTone != null) {
295                mHandler.removeCallbacks(mStopTone);
296            }
297            mDtmfLocalTonePlayer.stopTone(call);
298        } else {
299            Log.w(this, "onPostDialChar: invalid value %d", nextChar);
300        }
301    }
302
303    @Override
304    public void onParentChanged(Call call) {
305        // parent-child relationship affects which call should be foreground, so do an update.
306        updateCallsManagerState();
307        for (CallsManagerListener listener : mListeners) {
308            listener.onIsConferencedChanged(call);
309        }
310    }
311
312    @Override
313    public void onChildrenChanged(Call call) {
314        // parent-child relationship affects which call should be foreground, so do an update.
315        updateCallsManagerState();
316        for (CallsManagerListener listener : mListeners) {
317            listener.onIsConferencedChanged(call);
318        }
319    }
320
321    @Override
322    public void onIsVoipAudioModeChanged(Call call) {
323        for (CallsManagerListener listener : mListeners) {
324            listener.onIsVoipAudioModeChanged(call);
325        }
326    }
327
328    @Override
329    public void onVideoStateChanged(Call call) {
330        for (CallsManagerListener listener : mListeners) {
331            listener.onVideoStateChanged(call);
332        }
333    }
334
335    @Override
336    public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
337        mPendingCallsToDisconnect.add(call);
338        mHandler.postDelayed(new Runnable() {
339            @Override
340            public void run() {
341                if (mPendingCallsToDisconnect.remove(call)) {
342                    Log.i(this, "Delayed disconnection of call: %s", call);
343                    call.disconnect();
344                }
345            }
346        }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
347
348        return true;
349    }
350
351    Collection<Call> getCalls() {
352        return Collections.unmodifiableCollection(mCalls);
353    }
354
355    Call getForegroundCall() {
356        return mForegroundCall;
357    }
358
359    Ringer getRinger() {
360        return mRinger;
361    }
362
363    InCallController getInCallController() {
364        return mInCallController;
365    }
366
367    boolean hasEmergencyCall() {
368        for (Call call : mCalls) {
369            if (call.isEmergencyCall()) {
370                return true;
371            }
372        }
373        return false;
374    }
375
376    boolean hasVideoCall() {
377        for (Call call : mCalls) {
378            if (call.getVideoState() != VideoProfile.VideoState.AUDIO_ONLY) {
379                return true;
380            }
381        }
382        return false;
383    }
384
385    AudioState getAudioState() {
386        return mCallAudioManager.getAudioState();
387    }
388
389    boolean isTtySupported() {
390        return mTtyManager.isTtySupported();
391    }
392
393    int getCurrentTtyMode() {
394        return mTtyManager.getCurrentTtyMode();
395    }
396
397    void addListener(CallsManagerListener listener) {
398        mListeners.add(listener);
399    }
400
401    void removeListener(CallsManagerListener listener) {
402        mListeners.remove(listener);
403    }
404
405    /**
406     * Starts the process to attach the call to a connection service.
407     *
408     * @param phoneAccountHandle The phone account which contains the component name of the
409     *        connection service to use for this call.
410     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
411     */
412    void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
413        Log.d(this, "processIncomingCallIntent");
414        Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
415        Call call = new Call(
416                mContext,
417                mConnectionServiceRepository,
418                handle,
419                null /* gatewayInfo */,
420                null /* connectionManagerPhoneAccount */,
421                phoneAccountHandle,
422                true /* isIncoming */,
423                false /* isConference */);
424
425        call.setExtras(extras);
426        // TODO: Move this to be a part of addCall()
427        call.addListener(this);
428        call.startCreateConnection(mPhoneAccountRegistrar);
429    }
430
431    void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
432        Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
433        Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
434        Call call = new Call(
435                mContext,
436                mConnectionServiceRepository,
437                handle,
438                null /* gatewayInfo */,
439                null /* connectionManagerPhoneAccount */,
440                phoneAccountHandle,
441                // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
442                // to the existing connection instead of trying to create a new one.
443                true /* isIncoming */,
444                false /* isConference */);
445        call.setIsUnknown(true);
446        call.setExtras(extras);
447        call.addListener(this);
448        call.startCreateConnection(mPhoneAccountRegistrar);
449    }
450
451    private Call getNewOutgoingCall(Uri handle) {
452        // First check to see if we can reuse any of the calls that are waiting to disconnect.
453        // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
454        Call reusedCall = null;
455        for (Call pendingCall : mPendingCallsToDisconnect) {
456            if (reusedCall == null && Objects.equals(pendingCall.getHandle(), handle)) {
457                mPendingCallsToDisconnect.remove(pendingCall);
458                Log.i(this, "Reusing disconnected call %s", pendingCall);
459                reusedCall = pendingCall;
460            } else {
461                pendingCall.disconnect();
462            }
463        }
464        if (reusedCall != null) {
465            return reusedCall;
466        }
467
468        // Create a call with original handle. The handle may be changed when the call is attached
469        // to a connection service, but in most cases will remain the same.
470        return new Call(
471                mContext,
472                mConnectionServiceRepository,
473                handle,
474                null /* gatewayInfo */,
475                null /* connectionManagerPhoneAccount */,
476                null /* phoneAccountHandle */,
477                false /* isIncoming */,
478                false /* isConference */);
479    }
480
481    /**
482     * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
483     *
484     * @param handle Handle to connect the call with.
485     * @param phoneAccountHandle The phone account which contains the component name of the
486     *        connection service to use for this call.
487     * @param extras The optional extras Bundle passed with the intent used for the incoming call.
488     */
489    Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
490        Call call = getNewOutgoingCall(handle);
491
492        List<PhoneAccountHandle> accounts =
493                mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme());
494
495        Log.v(this, "startOutgoingCall found accounts = " + accounts);
496
497        if (mForegroundCall != null && mForegroundCall.getTargetPhoneAccount() != null) {
498            // If there is an ongoing call, use the same phone account to place this new call.
499            phoneAccountHandle = mForegroundCall.getTargetPhoneAccount();
500        }
501
502        // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
503        // as if a phoneAccount was not specified (does the default behavior instead).
504        // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
505        if (phoneAccountHandle != null) {
506            if (!accounts.contains(phoneAccountHandle)) {
507                phoneAccountHandle = null;
508            }
509        }
510
511        if (phoneAccountHandle == null) {
512            // No preset account, check if default exists that supports the URI scheme for the
513            // handle.
514            PhoneAccountHandle defaultAccountHandle =
515                    mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(
516                            handle.getScheme());
517            if (defaultAccountHandle != null) {
518                phoneAccountHandle = defaultAccountHandle;
519            }
520        }
521
522        call.setTargetPhoneAccount(phoneAccountHandle);
523
524        boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
525                call.getHandle());
526        boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
527
528        // Do not support any more live calls.  Our options are to move a call to hold, disconnect
529        // a call, or cancel this call altogether.
530        if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) {
531            // just cancel at this point.
532            Log.i(this, "No remaining room for outgoing call: %s", call);
533            if (mCalls.contains(call)) {
534                // This call can already exist if it is a reused call,
535                // See {@link #getNewOutgoingCall}.
536                call.disconnect();
537            }
538            return null;
539        }
540
541        boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
542                !isEmergencyCall;
543
544        if (needsAccountSelection) {
545            // This is the state where the user is expected to select an account
546            call.setState(CallState.PRE_DIAL_WAIT);
547            extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
548        } else {
549            call.setState(CallState.CONNECTING);
550        }
551
552        call.setExtras(extras);
553
554        // Do not add the call if it is a potential MMI code.
555        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
556            call.addListener(this);
557        } else if (!mCalls.contains(call)) {
558            // We check if mCalls already contains the call because we could potentially be reusing
559            // a call which was previously added (See {@link #getNewOutgoingCall}).
560            addCall(call);
561        }
562
563        return call;
564    }
565
566    /**
567     * Attempts to issue/connect the specified call.
568     *
569     * @param handle Handle to connect the call with.
570     * @param gatewayInfo Optional gateway information that can be used to route the call to the
571     *        actual dialed handle via a gateway provider. May be null.
572     * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
573     * @param videoState The desired video state for the outgoing call.
574     */
575    void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
576            int videoState) {
577        if (call == null) {
578            // don't do anything if the call no longer exists
579            Log.i(this, "Canceling unknown call.");
580            return;
581        }
582
583        final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
584
585        if (gatewayInfo == null) {
586            Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
587        } else {
588            Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
589                    Log.pii(uriHandle), Log.pii(handle));
590        }
591
592        call.setHandle(uriHandle);
593        call.setGatewayInfo(gatewayInfo);
594        call.setStartWithSpeakerphoneOn(speakerphoneOn);
595        call.setVideoState(videoState);
596
597        boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
598                call.getHandle());
599        if (isEmergencyCall) {
600            // Emergency -- CreateConnectionProcessor will choose accounts automatically
601            call.setTargetPhoneAccount(null);
602        }
603
604        if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
605            // If the account has been set, proceed to place the outgoing call.
606            // Otherwise the connection will be initiated when the account is set by the user.
607            call.startCreateConnection(mPhoneAccountRegistrar);
608        }
609    }
610
611    /**
612     * Attempts to start a conference call for the specified call.
613     *
614     * @param call The call to conference.
615     * @param otherCall The other call to conference with.
616     */
617    void conference(Call call, Call otherCall) {
618        call.conferenceWith(otherCall);
619    }
620
621    /**
622     * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
623     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
624     * the user opting to answer said call.
625     *
626     * @param call The call to answer.
627     * @param videoState The video state in which to answer the call.
628     */
629    void answerCall(Call call, int videoState) {
630        if (!mCalls.contains(call)) {
631            Log.i(this, "Request to answer a non-existent call %s", call);
632        } else {
633            // If the foreground call is not the ringing call and it is currently isActive() or
634            // STATE_DIALING, put it on hold before answering the call.
635            if (mForegroundCall != null && mForegroundCall != call &&
636                    (mForegroundCall.isActive() ||
637                     mForegroundCall.getState() == CallState.DIALING)) {
638                if (0 == (mForegroundCall.getConnectionCapabilities()
639                        & Connection.CAPABILITY_HOLD)) {
640                    // This call does not support hold.  If it is from a different connection
641                    // service, then disconnect it, otherwise allow the connection service to
642                    // figure out the right states.
643                    if (mForegroundCall.getConnectionService() != call.getConnectionService()) {
644                        mForegroundCall.disconnect();
645                    }
646                } else {
647                    Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
648                            mForegroundCall, call);
649                    mForegroundCall.hold();
650                }
651                // TODO: Wait until we get confirmation of the active call being
652                // on-hold before answering the new call.
653                // TODO: Import logic from CallManager.acceptCall()
654            }
655
656            for (CallsManagerListener listener : mListeners) {
657                listener.onIncomingCallAnswered(call);
658            }
659
660            // We do not update the UI until we get confirmation of the answer() through
661            // {@link #markCallAsActive}.
662            call.answer(videoState);
663        }
664    }
665
666    /**
667     * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
668     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
669     * the user opting to reject said call.
670     */
671    void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
672        if (!mCalls.contains(call)) {
673            Log.i(this, "Request to reject a non-existent call %s", call);
674        } else {
675            for (CallsManagerListener listener : mListeners) {
676                listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
677            }
678            call.reject(rejectWithMessage, textMessage);
679        }
680    }
681
682    /**
683     * Instructs Telecom to play the specified DTMF tone within the specified call.
684     *
685     * @param digit The DTMF digit to play.
686     */
687    void playDtmfTone(Call call, char digit) {
688        if (!mCalls.contains(call)) {
689            Log.i(this, "Request to play DTMF in a non-existent call %s", call);
690        } else {
691            call.playDtmfTone(digit);
692            mDtmfLocalTonePlayer.playTone(call, digit);
693        }
694    }
695
696    /**
697     * Instructs Telecom to stop the currently playing DTMF tone, if any.
698     */
699    void stopDtmfTone(Call call) {
700        if (!mCalls.contains(call)) {
701            Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
702        } else {
703            call.stopDtmfTone();
704            mDtmfLocalTonePlayer.stopTone(call);
705        }
706    }
707
708    /**
709     * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
710     */
711    void postDialContinue(Call call, boolean proceed) {
712        if (!mCalls.contains(call)) {
713            Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
714        } else {
715            call.postDialContinue(proceed);
716        }
717    }
718
719    /**
720     * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
721     * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
722     * the user hitting the end-call button.
723     */
724    void disconnectCall(Call call) {
725        Log.v(this, "disconnectCall %s", call);
726
727        if (!mCalls.contains(call)) {
728            Log.w(this, "Unknown call (%s) asked to disconnect", call);
729        } else {
730            mLocallyDisconnectingCalls.add(call);
731            call.disconnect();
732        }
733    }
734
735    /**
736     * Instructs Telecom to disconnect all calls.
737     */
738    void disconnectAllCalls() {
739        Log.v(this, "disconnectAllCalls");
740
741        for (Call call : mCalls) {
742            disconnectCall(call);
743        }
744    }
745
746
747    /**
748     * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
749     * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
750     * the user hitting the hold button during an active call.
751     */
752    void holdCall(Call call) {
753        if (!mCalls.contains(call)) {
754            Log.w(this, "Unknown call (%s) asked to be put on hold", call);
755        } else {
756            Log.d(this, "Putting call on hold: (%s)", call);
757            call.hold();
758        }
759    }
760
761    /**
762     * Instructs Telecom to release the specified call from hold. Intended to be invoked by
763     * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
764     * by the user hitting the hold button during a held call.
765     */
766    void unholdCall(Call call) {
767        if (!mCalls.contains(call)) {
768            Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
769        } else {
770            Log.d(this, "unholding call: (%s)", call);
771            for (Call c : mCalls) {
772                if (c != null && c.isAlive() && c != call) {
773                    c.hold();
774                }
775            }
776            call.unhold();
777        }
778    }
779
780    /** Called by the in-call UI to change the mute state. */
781    void mute(boolean shouldMute) {
782        mCallAudioManager.mute(shouldMute);
783    }
784
785    /**
786      * Called by the in-call UI to change the audio route, for example to change from earpiece to
787      * speaker phone.
788      */
789    void setAudioRoute(int route) {
790        mCallAudioManager.setAudioRoute(route);
791    }
792
793    /** Called by the in-call UI to turn the proximity sensor on. */
794    void turnOnProximitySensor() {
795        mProximitySensorManager.turnOn();
796    }
797
798    /**
799     * Called by the in-call UI to turn the proximity sensor off.
800     * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
801     *        the screen will be kept off until the proximity sensor goes negative.
802     */
803    void turnOffProximitySensor(boolean screenOnImmediately) {
804        mProximitySensorManager.turnOff(screenOnImmediately);
805    }
806
807    void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
808        if (!mCalls.contains(call)) {
809            Log.i(this, "Attempted to add account to unknown call %s", call);
810        } else {
811            // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
812            // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the
813            // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
814            // respecting a rewritten number or a canceled number. This is unlikely since
815            // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
816            // a phone account from the in-call UI.
817            call.setTargetPhoneAccount(account);
818
819            // Note: emergency calls never go through account selection dialog so they never
820            // arrive here.
821            if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
822                call.startCreateConnection(mPhoneAccountRegistrar);
823            } else {
824                call.disconnect();
825            }
826
827            if (setDefault) {
828                mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account);
829            }
830        }
831    }
832
833    /** Called when the audio state changes. */
834    void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) {
835        Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
836        for (CallsManagerListener listener : mListeners) {
837            listener.onAudioStateChanged(oldAudioState, newAudioState);
838        }
839    }
840
841    void markCallAsRinging(Call call) {
842        setCallState(call, CallState.RINGING);
843    }
844
845    void markCallAsDialing(Call call) {
846        setCallState(call, CallState.DIALING);
847    }
848
849    void markCallAsActive(Call call) {
850        setCallState(call, CallState.ACTIVE);
851
852        if (call.getStartWithSpeakerphoneOn()) {
853            setAudioRoute(AudioState.ROUTE_SPEAKER);
854        }
855    }
856
857    void markCallAsOnHold(Call call) {
858        setCallState(call, CallState.ON_HOLD);
859    }
860
861    /**
862     * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
863     * last live call, then also disconnect from the in-call controller.
864     *
865     * @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}.
866     */
867    void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
868        call.setDisconnectCause(disconnectCause);
869        setCallState(call, CallState.DISCONNECTED);
870    }
871
872    /**
873     * Removes an existing disconnected call, and notifies the in-call app.
874     */
875    void markCallAsRemoved(Call call) {
876        removeCall(call);
877        if (mLocallyDisconnectingCalls.contains(call)) {
878            mLocallyDisconnectingCalls.remove(call);
879            if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
880                mForegroundCall.unhold();
881            }
882        }
883    }
884
885    /**
886     * Cleans up any calls currently associated with the specified connection service when the
887     * service binder disconnects unexpectedly.
888     *
889     * @param service The connection service that disconnected.
890     */
891    void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
892        if (service != null) {
893            for (Call call : mCalls) {
894                if (call.getConnectionService() == service) {
895                    if (call.getState() != CallState.DISCONNECTED) {
896                        markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
897                    }
898                    markCallAsRemoved(call);
899                }
900            }
901        }
902    }
903
904    boolean hasAnyCalls() {
905        return !mCalls.isEmpty();
906    }
907
908    boolean hasActiveOrHoldingCall() {
909        return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
910    }
911
912    boolean hasRingingCall() {
913        return getFirstCallWithState(CallState.RINGING) != null;
914    }
915
916    boolean onMediaButton(int type) {
917        if (hasAnyCalls()) {
918            if (HeadsetMediaButton.SHORT_PRESS == type) {
919                Call ringingCall = getFirstCallWithState(CallState.RINGING);
920                if (ringingCall == null) {
921                    mCallAudioManager.toggleMute();
922                    return true;
923                } else {
924                    ringingCall.answer(ringingCall.getVideoState());
925                    return true;
926                }
927            } else if (HeadsetMediaButton.LONG_PRESS == type) {
928                Log.d(this, "handleHeadsetHook: longpress -> hangup");
929                Call callToHangup = getFirstCallWithState(
930                        CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
931                if (callToHangup != null) {
932                    callToHangup.disconnect();
933                    return true;
934                }
935            }
936        }
937        return false;
938    }
939
940    /**
941     * Returns true if telecom supports adding another top-level call.
942     */
943    boolean canAddCall() {
944        if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
945            return false;
946        }
947
948        int count = 0;
949        for (Call call : mCalls) {
950            if (call.isEmergencyCall()) {
951                // We never support add call if one of the calls is an emergency call.
952                return false;
953            } else if (call.getParentCall() == null) {
954                count++;
955            }
956
957            // We do not check states for canAddCall. We treat disconnected calls the same
958            // and wait until they are removed instead. If we didn't count disconnected calls,
959            // we could put InCallServices into a state where they are showing two calls but
960            // also support add-call. Technically it's right, but overall looks better (UI-wise)
961            // and acts better if we wait until the call is removed.
962            if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
963                return false;
964            }
965        }
966        return true;
967    }
968
969    Call getRingingCall() {
970        return getFirstCallWithState(CallState.RINGING);
971    }
972
973    Call getActiveCall() {
974        return getFirstCallWithState(CallState.ACTIVE);
975    }
976
977    Call getDialingCall() {
978        return getFirstCallWithState(CallState.DIALING);
979    }
980
981    Call getHeldCall() {
982        return getFirstCallWithState(CallState.ON_HOLD);
983    }
984
985    int getNumHeldCalls() {
986        int count = 0;
987        for (Call call : mCalls) {
988            if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
989                count++;
990            }
991        }
992        return count;
993    }
994
995    Call getFirstCallWithState(int... states) {
996        return getFirstCallWithState(null, states);
997    }
998
999    /**
1000     * Returns the first call that it finds with the given states. The states are treated as having
1001     * priority order so that any call with the first state will be returned before any call with
1002     * states listed later in the parameter list.
1003     *
1004     * @param callToSkip Call that this method should skip while searching
1005     */
1006    Call getFirstCallWithState(Call callToSkip, int... states) {
1007        for (int currentState : states) {
1008            // check the foreground first
1009            if (mForegroundCall != null && mForegroundCall.getState() == currentState) {
1010                return mForegroundCall;
1011            }
1012
1013            for (Call call : mCalls) {
1014                if (Objects.equals(callToSkip, call)) {
1015                    continue;
1016                }
1017
1018                // Only operate on top-level calls
1019                if (call.getParentCall() != null) {
1020                    continue;
1021                }
1022
1023                if (currentState == call.getState()) {
1024                    return call;
1025                }
1026            }
1027        }
1028        return null;
1029    }
1030
1031    Call createConferenceCall(
1032            PhoneAccountHandle phoneAccount,
1033            ParcelableConference parcelableConference) {
1034        Call call = new Call(
1035                mContext,
1036                mConnectionServiceRepository,
1037                null /* handle */,
1038                null /* gatewayInfo */,
1039                null /* connectionManagerPhoneAccount */,
1040                phoneAccount,
1041                false /* isIncoming */,
1042                true /* isConference */);
1043
1044        setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()));
1045        call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
1046
1047        // TODO: Move this to be a part of addCall()
1048        call.addListener(this);
1049        addCall(call);
1050        return call;
1051    }
1052
1053    /**
1054     * @return the call state currently tracked by {@link PhoneStateBroadcaster}
1055     */
1056    int getCallState() {
1057        return mPhoneStateBroadcaster.getCallState();
1058    }
1059
1060    /**
1061     * Retrieves the {@link PhoneAccountRegistrar}.
1062     *
1063     * @return The {@link PhoneAccountRegistrar}.
1064     */
1065    PhoneAccountRegistrar getPhoneAccountRegistrar() {
1066        return mPhoneAccountRegistrar;
1067    }
1068
1069    /**
1070     * Retrieves the {@link MissedCallNotifier}
1071     * @return The {@link MissedCallNotifier}.
1072     */
1073    MissedCallNotifier getMissedCallNotifier() {
1074        return mMissedCallNotifier;
1075    }
1076
1077    /**
1078     * Adds the specified call to the main list of live calls.
1079     *
1080     * @param call The call to add.
1081     */
1082    private void addCall(Call call) {
1083        Trace.beginSection("addCall");
1084        Log.v(this, "addCall(%s)", call);
1085        call.addListener(this);
1086        mCalls.add(call);
1087
1088        // TODO: Update mForegroundCall prior to invoking
1089        // onCallAdded for calls which immediately take the foreground (like the first call).
1090        for (CallsManagerListener listener : mListeners) {
1091            if (Log.SYSTRACE_DEBUG) {
1092                Trace.beginSection(listener.getClass().toString() + " addCall");
1093            }
1094            listener.onCallAdded(call);
1095            if (Log.SYSTRACE_DEBUG) {
1096                Trace.endSection();
1097            }
1098        }
1099        updateCallsManagerState();
1100        Trace.endSection();
1101    }
1102
1103    private void removeCall(Call call) {
1104        Trace.beginSection("removeCall");
1105        Log.v(this, "removeCall(%s)", call);
1106
1107        call.setParentCall(null);  // need to clean up parent relationship before destroying.
1108        call.removeListener(this);
1109        call.clearConnectionService();
1110
1111        boolean shouldNotify = false;
1112        if (mCalls.contains(call)) {
1113            mCalls.remove(call);
1114            shouldNotify = true;
1115        }
1116
1117        // Only broadcast changes for calls that are being tracked.
1118        if (shouldNotify) {
1119            for (CallsManagerListener listener : mListeners) {
1120                if (Log.SYSTRACE_DEBUG) {
1121                    Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
1122                }
1123                listener.onCallRemoved(call);
1124                if (Log.SYSTRACE_DEBUG) {
1125                    Trace.endSection();
1126                }
1127            }
1128            updateCallsManagerState();
1129        }
1130        Trace.endSection();
1131    }
1132
1133    /**
1134     * Sets the specified state on the specified call.
1135     *
1136     * @param call The call.
1137     * @param newState The new state of the call.
1138     */
1139    private void setCallState(Call call, int newState) {
1140        if (call == null) {
1141            return;
1142        }
1143        int oldState = call.getState();
1144        Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
1145                CallState.toString(newState), call);
1146        if (newState != oldState) {
1147            // Unfortunately, in the telephony world the radio is king. So if the call notifies
1148            // us that the call is in a particular state, we allow it even if it doesn't make
1149            // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
1150            // TODO: Consider putting a stop to the above and turning CallState
1151            // into a well-defined state machine.
1152            // TODO: Define expected state transitions here, and log when an
1153            // unexpected transition occurs.
1154            call.setState(newState);
1155
1156            Trace.beginSection("onCallStateChanged");
1157            // Only broadcast state change for calls that are being tracked.
1158            if (mCalls.contains(call)) {
1159                for (CallsManagerListener listener : mListeners) {
1160                    if (Log.SYSTRACE_DEBUG) {
1161                        Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
1162                    }
1163                    listener.onCallStateChanged(call, oldState, newState);
1164                    if (Log.SYSTRACE_DEBUG) {
1165                        Trace.endSection();
1166                    }
1167                }
1168                updateCallsManagerState();
1169            }
1170            Trace.endSection();
1171        }
1172    }
1173
1174    /**
1175     * Checks which call should be visible to the user and have audio focus.
1176     */
1177    private void updateForegroundCall() {
1178        Trace.beginSection("updateForegroundCall");
1179        Call newForegroundCall = null;
1180        for (Call call : mCalls) {
1181            // TODO: Foreground-ness needs to be explicitly set. No call, regardless
1182            // of its state will be foreground by default and instead the connection service should
1183            // be notified when its calls enter and exit foreground state. Foreground will mean that
1184            // the call should play audio and listen to microphone if it wants.
1185
1186            // Only top-level calls can be in foreground
1187            if (call.getParentCall() != null) {
1188                continue;
1189            }
1190
1191            // Active calls have priority.
1192            if (call.isActive()) {
1193                newForegroundCall = call;
1194                break;
1195            }
1196
1197            if (call.isAlive() || call.getState() == CallState.RINGING) {
1198                newForegroundCall = call;
1199                // Don't break in case there's an active call that has priority.
1200            }
1201        }
1202
1203        if (newForegroundCall != mForegroundCall) {
1204            Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
1205            Call oldForegroundCall = mForegroundCall;
1206            mForegroundCall = newForegroundCall;
1207
1208            for (CallsManagerListener listener : mListeners) {
1209                if (Log.SYSTRACE_DEBUG) {
1210                    Trace.beginSection(listener.getClass().toString() + " updateForegroundCall");
1211                }
1212                listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
1213                if (Log.SYSTRACE_DEBUG) {
1214                    Trace.endSection();
1215                }
1216            }
1217        }
1218        Trace.endSection();
1219    }
1220
1221    private void updateCanAddCall() {
1222        boolean newCanAddCall = canAddCall();
1223        if (newCanAddCall != mCanAddCall) {
1224            mCanAddCall = newCanAddCall;
1225            for (CallsManagerListener listener : mListeners) {
1226                if (Log.SYSTRACE_DEBUG) {
1227                    Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
1228                }
1229                listener.onCanAddCallChanged(mCanAddCall);
1230                if (Log.SYSTRACE_DEBUG) {
1231                    Trace.endSection();
1232                }
1233            }
1234        }
1235    }
1236
1237    private void updateCallsManagerState() {
1238        updateForegroundCall();
1239        updateCanAddCall();
1240    }
1241
1242    private boolean isPotentialMMICode(Uri handle) {
1243        return (handle != null && handle.getSchemeSpecificPart() != null
1244                && handle.getSchemeSpecificPart().contains("#"));
1245    }
1246
1247    /**
1248     * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
1249     * MMI codes which can be dialed when one or more calls are in progress.
1250     * <P>
1251     * Checks for numbers formatted similar to the MMI codes defined in:
1252     * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)}
1253     * and
1254     * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)}
1255     *
1256     * @param handle The URI to call.
1257     * @return {@code True} if the URI represents a number which could be an in-call MMI code.
1258     */
1259    private boolean isPotentialInCallMMICode(Uri handle) {
1260        if (handle != null && handle.getSchemeSpecificPart() != null &&
1261                handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
1262
1263            String dialedNumber = handle.getSchemeSpecificPart();
1264            return (dialedNumber.equals("0") ||
1265                    (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
1266                    (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
1267                    dialedNumber.equals("3") ||
1268                    dialedNumber.equals("4") ||
1269                    dialedNumber.equals("5"));
1270        }
1271        return false;
1272    }
1273
1274    private int getNumCallsWithState(int... states) {
1275        int count = 0;
1276        for (int state : states) {
1277            for (Call call : mCalls) {
1278                if (call.getParentCall() == null && call.getState() == state) {
1279                    count++;
1280                }
1281            }
1282        }
1283        return count;
1284    }
1285
1286    private boolean hasMaximumLiveCalls() {
1287        return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
1288    }
1289
1290    private boolean hasMaximumHoldingCalls() {
1291        return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
1292    }
1293
1294    private boolean hasMaximumRingingCalls() {
1295        return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
1296    }
1297
1298    private boolean hasMaximumOutgoingCalls() {
1299        return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
1300    }
1301
1302    private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
1303        if (hasMaximumLiveCalls()) {
1304            // NOTE: If the amount of live calls changes beyond 1, this logic will probably
1305            // have to change.
1306            Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES);
1307
1308            if (call == liveCall) {
1309                // If the call is already the foreground call, then we are golden.
1310                // This can happen after the user selects an account in the PRE_DIAL_WAIT
1311                // state since the call was already populated into the list.
1312                return true;
1313            }
1314
1315            if (hasMaximumOutgoingCalls()) {
1316                // Disconnect the current outgoing call if it's not an emergency call. If the user
1317                // tries to make two outgoing calls to different emergency call numbers, we will try
1318                // to connect the first outgoing call.
1319                if (isEmergency) {
1320                    Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
1321                    if (!outgoingCall.isEmergencyCall()) {
1322                        outgoingCall.disconnect();
1323                        return true;
1324                    }
1325                }
1326                return false;
1327            }
1328
1329            if (hasMaximumHoldingCalls()) {
1330                // There is no more room for any more calls, unless it's an emergency.
1331                if (isEmergency) {
1332                    // Kill the current active call, this is easier then trying to disconnect a
1333                    // holding call and hold an active call.
1334                    liveCall.disconnect();
1335                    return true;
1336                }
1337                return false;  // No more room!
1338            }
1339
1340            // We have room for at least one more holding call at this point.
1341
1342            // First thing, if we are trying to make a call with the same phone account as the live
1343            // call, then allow it so that the connection service can make its own decision about
1344            // how to handle the new call relative to the current one.
1345            if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) {
1346                return true;
1347            } else if (call.getTargetPhoneAccount() == null) {
1348                // Without a phone account, we can't say reliably that the call will fail.
1349                // If the user chooses the same phone account as the live call, then it's
1350                // still possible that the call can be made (like with CDMA calls not supporting
1351                // hold but they still support adding a call by going immediately into conference
1352                // mode). Return true here and we'll run this code again after user chooses an
1353                // account.
1354                return true;
1355            }
1356
1357            // Try to hold the live call before attempting the new outgoing call.
1358            if (liveCall.can(Connection.CAPABILITY_HOLD)) {
1359                liveCall.hold();
1360                return true;
1361            }
1362
1363            // The live call cannot be held so we're out of luck here.  There's no room.
1364            return false;
1365        }
1366        return true;
1367    }
1368
1369    /**
1370     * Creates a new call for an existing connection.
1371     *
1372     * @param callId The id of the new call.
1373     * @param connection The connection information.
1374     * @return The new call.
1375     */
1376    Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
1377        Call call = new Call(
1378                mContext,
1379                mConnectionServiceRepository,
1380                connection.getHandle() /* handle */,
1381                null /* gatewayInfo */,
1382                null /* connectionManagerPhoneAccount */,
1383                connection.getPhoneAccount(), /* targetPhoneAccountHandle */
1384                false /* isIncoming */,
1385                false /* isConference */);
1386
1387        setCallState(call, Call.getStateFromConnectionState(connection.getState()));
1388        call.setConnectionCapabilities(connection.getConnectionCapabilities());
1389        call.setCallerDisplayName(connection.getCallerDisplayName(),
1390                connection.getCallerDisplayNamePresentation());
1391
1392        call.addListener(this);
1393        addCall(call);
1394
1395        return call;
1396    }
1397
1398    /**
1399     * Dumps the state of the {@link CallsManager}.
1400     *
1401     * @param pw The {@code IndentingPrintWriter} to write the state to.
1402     */
1403    public void dump(IndentingPrintWriter pw) {
1404        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1405        if (mCalls != null) {
1406            pw.println("mCalls: ");
1407            pw.increaseIndent();
1408            for (Call call : mCalls) {
1409                pw.println(call);
1410            }
1411            pw.decreaseIndent();
1412        }
1413        pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall));
1414
1415        if (mCallAudioManager != null) {
1416            pw.println("mCallAudioManager:");
1417            pw.increaseIndent();
1418            mCallAudioManager.dump(pw);
1419            pw.decreaseIndent();
1420        }
1421
1422        if (mTtyManager != null) {
1423            pw.println("mTtyManager:");
1424            pw.increaseIndent();
1425            mTtyManager.dump(pw);
1426            pw.decreaseIndent();
1427        }
1428
1429        if (mInCallController != null) {
1430            pw.println("mInCallController:");
1431            pw.increaseIndent();
1432            mInCallController.dump(pw);
1433            pw.decreaseIndent();
1434        }
1435
1436        if (mConnectionServiceRepository != null) {
1437            pw.println("mConnectionServiceRepository:");
1438            pw.increaseIndent();
1439            mConnectionServiceRepository.dump(pw);
1440            pw.decreaseIndent();
1441        }
1442    }
1443}
1444