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