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