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