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