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