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