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