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