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