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