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