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