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