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