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.content.Context;
20 import android.net.Uri;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.Trace;
24 import android.provider.CallLog.Calls;
25 import android.telecom.AudioState;
26 import android.telecom.CallState;
27 import android.telecom.Conference;
28 import android.telecom.Connection;
29 import android.telecom.DisconnectCause;
30 import android.telecom.GatewayInfo;
31 import android.telecom.ParcelableConference;
32 import android.telecom.ParcelableConnection;
33 import android.telecom.PhoneAccount;
34 import android.telecom.PhoneAccountHandle;
35 import android.telecom.TelecomManager;
36 import android.telecom.VideoProfile;
37 import android.telephony.PhoneNumberUtils;
38 import android.telephony.TelephonyManager;
39 
40 import com.android.internal.util.IndentingPrintWriter;
41 
42 import java.util.Collection;
43 import java.util.Collections;
44 import java.util.HashSet;
45 import java.util.List;
46 import java.util.Objects;
47 import java.util.Set;
48 import java.util.concurrent.ConcurrentHashMap;
49 
50 /**
51  * Singleton.
52  *
53  * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
54  * access from other packages specifically refraining from passing the CallsManager instance
55  * beyond the com.android.server.telecom package boundary.
56  */
57 public final class CallsManager extends Call.ListenerBase {
58 
59     // TODO: Consider renaming this CallsManagerPlugin.
60     interface CallsManagerListener {
onCallAdded(Call call)61         void onCallAdded(Call call);
onCallRemoved(Call call)62         void onCallRemoved(Call call);
onCallStateChanged(Call call, int oldState, int newState)63         void onCallStateChanged(Call call, int oldState, int newState);
onConnectionServiceChanged( Call call, ConnectionServiceWrapper oldService, ConnectionServiceWrapper newService)64         void onConnectionServiceChanged(
65                 Call call,
66                 ConnectionServiceWrapper oldService,
67                 ConnectionServiceWrapper newService);
onIncomingCallAnswered(Call call)68         void onIncomingCallAnswered(Call call);
onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)69         void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall)70         void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall);
onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState)71         void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState);
onRingbackRequested(Call call, boolean ringback)72         void onRingbackRequested(Call call, boolean ringback);
onIsConferencedChanged(Call call)73         void onIsConferencedChanged(Call call);
onIsVoipAudioModeChanged(Call call)74         void onIsVoipAudioModeChanged(Call call);
onVideoStateChanged(Call call)75         void onVideoStateChanged(Call call);
onCanAddCallChanged(boolean canAddCall)76         void onCanAddCallChanged(boolean canAddCall);
77     }
78 
79     /**
80      * Singleton instance of the {@link CallsManager}, initialized from {@link TelecomService}.
81      */
82     private static CallsManager sInstance = null;
83 
84     private static final String TAG = "CallsManager";
85 
86     private static final int MAXIMUM_LIVE_CALLS = 1;
87     private static final int MAXIMUM_HOLD_CALLS = 1;
88     private static final int MAXIMUM_RINGING_CALLS = 1;
89     private static final int MAXIMUM_OUTGOING_CALLS = 1;
90     private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
91 
92     private static final int[] OUTGOING_CALL_STATES =
93             {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING};
94 
95     private static final int[] LIVE_CALL_STATES =
96             {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE};
97 
98     /**
99      * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
100      * calls are added to the map and removed when the calls move to the disconnected state.
101      *
102      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
103      * load factor before resizing, 1 means we only expect a single thread to
104      * access the map so make only a single shard
105      */
106     private final Set<Call> mCalls = Collections.newSetFromMap(
107             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
108 
109     private final ConnectionServiceRepository mConnectionServiceRepository;
110     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
111     private final InCallController mInCallController;
112     private final CallAudioManager mCallAudioManager;
113     private final Ringer mRinger;
114     private final InCallWakeLockController mInCallWakeLockController;
115     // For this set initial table size to 16 because we add 13 listeners in
116     // the CallsManager constructor.
117     private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
118             new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
119     private final HeadsetMediaButton mHeadsetMediaButton;
120     private final WiredHeadsetManager mWiredHeadsetManager;
121     private final TtyManager mTtyManager;
122     private final ProximitySensorManager mProximitySensorManager;
123     private final PhoneStateBroadcaster mPhoneStateBroadcaster;
124     private final CallLogManager mCallLogManager;
125     private final Context mContext;
126     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
127     private final MissedCallNotifier mMissedCallNotifier;
128     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
129     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
130     /* Handler tied to thread in which CallManager was initialized. */
131     private final Handler mHandler = new Handler();
132 
133     private boolean mCanAddCall = true;
134 
135     /**
136      * The call the user is currently interacting with. This is the call that should have audio
137      * focus and be visible in the in-call UI.
138      */
139     private Call mForegroundCall;
140 
141     private Runnable mStopTone;
142 
143     /** Singleton accessor. */
getInstance()144     static CallsManager getInstance() {
145         return sInstance;
146     }
147 
148     /**
149      * Sets the static singleton instance.
150      *
151      * @param instance The instance to set.
152      */
initialize(CallsManager instance)153     static void initialize(CallsManager instance) {
154         sInstance = instance;
155     }
156 
157     /**
158      * Initializes the required Telecom components.
159      */
CallsManager(Context context, MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar)160      CallsManager(Context context, MissedCallNotifier missedCallNotifier,
161              PhoneAccountRegistrar phoneAccountRegistrar) {
162         mContext = context;
163         mPhoneAccountRegistrar = phoneAccountRegistrar;
164         mMissedCallNotifier = missedCallNotifier;
165         StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
166         mWiredHeadsetManager = new WiredHeadsetManager(context);
167         mCallAudioManager = new CallAudioManager(context, statusBarNotifier, mWiredHeadsetManager);
168         InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager);
169         mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
170         mHeadsetMediaButton = new HeadsetMediaButton(context, this);
171         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
172         mProximitySensorManager = new ProximitySensorManager(context);
173         mPhoneStateBroadcaster = new PhoneStateBroadcaster();
174         mCallLogManager = new CallLogManager(context);
175         mInCallController = new InCallController(context);
176         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
177         mConnectionServiceRepository = new ConnectionServiceRepository(mPhoneAccountRegistrar,
178                 context);
179         mInCallWakeLockController = new InCallWakeLockController(context, this);
180 
181         mListeners.add(statusBarNotifier);
182         mListeners.add(mCallLogManager);
183         mListeners.add(mPhoneStateBroadcaster);
184         mListeners.add(mInCallController);
185         mListeners.add(mRinger);
186         mListeners.add(new RingbackPlayer(this, playerFactory));
187         mListeners.add(new InCallToneMonitor(playerFactory, this));
188         mListeners.add(mCallAudioManager);
189         mListeners.add(missedCallNotifier);
190         mListeners.add(mDtmfLocalTonePlayer);
191         mListeners.add(mHeadsetMediaButton);
192         mListeners.add(RespondViaSmsManager.getInstance());
193         mListeners.add(mProximitySensorManager);
194     }
195 
196     @Override
onSuccessfulOutgoingCall(Call call, int callState)197     public void onSuccessfulOutgoingCall(Call call, int callState) {
198         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
199 
200         setCallState(call, callState);
201         if (!mCalls.contains(call)) {
202             // Call was not added previously in startOutgoingCall due to it being a potential MMI
203             // code, so add it now.
204             addCall(call);
205         }
206 
207         // The call's ConnectionService has been updated.
208         for (CallsManagerListener listener : mListeners) {
209             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
210         }
211 
212         markCallAsDialing(call);
213     }
214 
215     @Override
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)216     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
217         Log.v(this, "onFailedOutgoingCall, call: %s", call);
218 
219         markCallAsRemoved(call);
220     }
221 
222     @Override
onSuccessfulIncomingCall(Call incomingCall)223     public void onSuccessfulIncomingCall(Call incomingCall) {
224         Log.d(this, "onSuccessfulIncomingCall");
225         setCallState(incomingCall, CallState.RINGING);
226 
227         if (hasMaximumRingingCalls()) {
228             incomingCall.reject(false, null);
229             // since the call was not added to the list of calls, we have to call the missed
230             // call notifier and the call logger manually.
231             mMissedCallNotifier.showMissedCallNotification(incomingCall);
232             mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
233         } else {
234             addCall(incomingCall);
235         }
236     }
237 
238     @Override
onFailedIncomingCall(Call call)239     public void onFailedIncomingCall(Call call) {
240         setCallState(call, CallState.DISCONNECTED);
241         call.removeListener(this);
242     }
243 
244     @Override
onSuccessfulUnknownCall(Call call, int callState)245     public void onSuccessfulUnknownCall(Call call, int callState) {
246         setCallState(call, callState);
247         Log.i(this, "onSuccessfulUnknownCall for call %s", call);
248         addCall(call);
249     }
250 
251     @Override
onFailedUnknownCall(Call call)252     public void onFailedUnknownCall(Call call) {
253         Log.i(this, "onFailedUnknownCall for call %s", call);
254         setCallState(call, CallState.DISCONNECTED);
255         call.removeListener(this);
256     }
257 
258     @Override
onRingbackRequested(Call call, boolean ringback)259     public void onRingbackRequested(Call call, boolean ringback) {
260         for (CallsManagerListener listener : mListeners) {
261             listener.onRingbackRequested(call, ringback);
262         }
263     }
264 
265     @Override
onPostDialWait(Call call, String remaining)266     public void onPostDialWait(Call call, String remaining) {
267         mInCallController.onPostDialWait(call, remaining);
268     }
269 
270     @Override
onPostDialChar(final Call call, char nextChar)271     public void onPostDialChar(final Call call, char nextChar) {
272         if (PhoneNumberUtils.is12Key(nextChar)) {
273             // Play tone if it is one of the dialpad digits, canceling out the previously queued
274             // up stopTone runnable since playing a new tone automatically stops the previous tone.
275             if (mStopTone != null) {
276                 mHandler.removeCallbacks(mStopTone);
277             }
278 
279             mDtmfLocalTonePlayer.playTone(call, nextChar);
280 
281             mStopTone = new Runnable() {
282                 @Override
283                 public void run() {
284                     // Set a timeout to stop the tone in case there isn't another tone to follow.
285                     mDtmfLocalTonePlayer.stopTone(call);
286                 }
287             };
288             mHandler.postDelayed(
289                     mStopTone,
290                     Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
291         } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
292                 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
293             // Stop the tone if a tone is playing, removing any other stopTone callbacks since
294             // the previous tone is being stopped anyway.
295             if (mStopTone != null) {
296                 mHandler.removeCallbacks(mStopTone);
297             }
298             mDtmfLocalTonePlayer.stopTone(call);
299         } else {
300             Log.w(this, "onPostDialChar: invalid value %d", nextChar);
301         }
302     }
303 
304     @Override
onParentChanged(Call call)305     public void onParentChanged(Call call) {
306         // parent-child relationship affects which call should be foreground, so do an update.
307         updateCallsManagerState();
308         for (CallsManagerListener listener : mListeners) {
309             listener.onIsConferencedChanged(call);
310         }
311     }
312 
313     @Override
onChildrenChanged(Call call)314     public void onChildrenChanged(Call call) {
315         // parent-child relationship affects which call should be foreground, so do an update.
316         updateCallsManagerState();
317         for (CallsManagerListener listener : mListeners) {
318             listener.onIsConferencedChanged(call);
319         }
320     }
321 
322     @Override
onIsVoipAudioModeChanged(Call call)323     public void onIsVoipAudioModeChanged(Call call) {
324         for (CallsManagerListener listener : mListeners) {
325             listener.onIsVoipAudioModeChanged(call);
326         }
327     }
328 
329     @Override
onVideoStateChanged(Call call)330     public void onVideoStateChanged(Call call) {
331         for (CallsManagerListener listener : mListeners) {
332             listener.onVideoStateChanged(call);
333         }
334     }
335 
336     @Override
onCanceledViaNewOutgoingCallBroadcast(final Call call)337     public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
338         mPendingCallsToDisconnect.add(call);
339         mHandler.postDelayed(new Runnable() {
340             @Override
341             public void run() {
342                 if (mPendingCallsToDisconnect.remove(call)) {
343                     Log.i(this, "Delayed disconnection of call: %s", call);
344                     call.disconnect();
345                 }
346             }
347         }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
348 
349         return true;
350     }
351 
getCalls()352     Collection<Call> getCalls() {
353         return Collections.unmodifiableCollection(mCalls);
354     }
355 
getForegroundCall()356     Call getForegroundCall() {
357         return mForegroundCall;
358     }
359 
getRinger()360     Ringer getRinger() {
361         return mRinger;
362     }
363 
getInCallController()364     InCallController getInCallController() {
365         return mInCallController;
366     }
367 
hasEmergencyCall()368     boolean hasEmergencyCall() {
369         for (Call call : mCalls) {
370             if (call.isEmergencyCall()) {
371                 return true;
372             }
373         }
374         return false;
375     }
376 
hasVideoCall()377     boolean hasVideoCall() {
378         for (Call call : mCalls) {
379             if (call.getVideoState() != VideoProfile.VideoState.AUDIO_ONLY) {
380                 return true;
381             }
382         }
383         return false;
384     }
385 
getAudioState()386     AudioState getAudioState() {
387         return mCallAudioManager.getAudioState();
388     }
389 
isTtySupported()390     boolean isTtySupported() {
391         return mTtyManager.isTtySupported();
392     }
393 
getCurrentTtyMode()394     int getCurrentTtyMode() {
395         return mTtyManager.getCurrentTtyMode();
396     }
397 
addListener(CallsManagerListener listener)398     void addListener(CallsManagerListener listener) {
399         mListeners.add(listener);
400     }
401 
removeListener(CallsManagerListener listener)402     void removeListener(CallsManagerListener listener) {
403         mListeners.remove(listener);
404     }
405 
406     /**
407      * Starts the process to attach the call to a connection service.
408      *
409      * @param phoneAccountHandle The phone account which contains the component name of the
410      *        connection service to use for this call.
411      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
412      */
processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras)413     void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
414         Log.d(this, "processIncomingCallIntent");
415         Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
416         Call call = new Call(
417                 mContext,
418                 mConnectionServiceRepository,
419                 handle,
420                 null /* gatewayInfo */,
421                 null /* connectionManagerPhoneAccount */,
422                 phoneAccountHandle,
423                 true /* isIncoming */,
424                 false /* isConference */);
425 
426         call.setExtras(extras);
427         // TODO: Move this to be a part of addCall()
428         call.addListener(this);
429         call.startCreateConnection(mPhoneAccountRegistrar);
430     }
431 
addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras)432     void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
433         Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
434         Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
435         Call call = new Call(
436                 mContext,
437                 mConnectionServiceRepository,
438                 handle,
439                 null /* gatewayInfo */,
440                 null /* connectionManagerPhoneAccount */,
441                 phoneAccountHandle,
442                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
443                 // to the existing connection instead of trying to create a new one.
444                 true /* isIncoming */,
445                 false /* isConference */);
446         call.setIsUnknown(true);
447         call.setExtras(extras);
448         call.addListener(this);
449         call.startCreateConnection(mPhoneAccountRegistrar);
450     }
451 
getNewOutgoingCall(Uri handle)452     private Call getNewOutgoingCall(Uri handle) {
453         // First check to see if we can reuse any of the calls that are waiting to disconnect.
454         // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
455         Call reusedCall = null;
456         for (Call pendingCall : mPendingCallsToDisconnect) {
457             if (reusedCall == null && Objects.equals(pendingCall.getHandle(), handle)) {
458                 mPendingCallsToDisconnect.remove(pendingCall);
459                 Log.i(this, "Reusing disconnected call %s", pendingCall);
460                 reusedCall = pendingCall;
461             } else {
462                 pendingCall.disconnect();
463             }
464         }
465         if (reusedCall != null) {
466             return reusedCall;
467         }
468 
469         // Create a call with original handle. The handle may be changed when the call is attached
470         // to a connection service, but in most cases will remain the same.
471         return new Call(
472                 mContext,
473                 mConnectionServiceRepository,
474                 handle,
475                 null /* gatewayInfo */,
476                 null /* connectionManagerPhoneAccount */,
477                 null /* phoneAccountHandle */,
478                 false /* isIncoming */,
479                 false /* isConference */);
480     }
481 
482     /**
483      * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
484      *
485      * @param handle Handle to connect the call with.
486      * @param phoneAccountHandle The phone account which contains the component name of the
487      *        connection service to use for this call.
488      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
489      */
startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras)490     Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
491         Call call = getNewOutgoingCall(handle);
492 
493         List<PhoneAccountHandle> accounts =
494                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme());
495 
496         Log.v(this, "startOutgoingCall found accounts = " + accounts);
497 
498         if (mForegroundCall != null && mForegroundCall.getTargetPhoneAccount() != null) {
499             // If there is an ongoing call, use the same phone account to place this new call.
500             phoneAccountHandle = mForegroundCall.getTargetPhoneAccount();
501         }
502 
503         // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
504         // as if a phoneAccount was not specified (does the default behavior instead).
505         // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
506         if (phoneAccountHandle != null) {
507             if (!accounts.contains(phoneAccountHandle)) {
508                 phoneAccountHandle = null;
509             }
510         }
511 
512         if (phoneAccountHandle == null) {
513             // No preset account, check if default exists that supports the URI scheme for the
514             // handle.
515             PhoneAccountHandle defaultAccountHandle =
516                     mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(
517                             handle.getScheme());
518             if (defaultAccountHandle != null) {
519                 phoneAccountHandle = defaultAccountHandle;
520             }
521         }
522 
523         call.setTargetPhoneAccount(phoneAccountHandle);
524 
525         boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
526                 call.getHandle());
527         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
528 
529         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
530         // a call, or cancel this call altogether.
531         if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) {
532             // just cancel at this point.
533             Log.i(this, "No remaining room for outgoing call: %s", call);
534             if (mCalls.contains(call)) {
535                 // This call can already exist if it is a reused call,
536                 // See {@link #getNewOutgoingCall}.
537                 call.disconnect();
538             }
539             return null;
540         }
541 
542         boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
543                 !isEmergencyCall;
544 
545         if (needsAccountSelection) {
546             // This is the state where the user is expected to select an account
547             call.setState(CallState.PRE_DIAL_WAIT);
548             extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
549         } else {
550             call.setState(CallState.CONNECTING);
551         }
552 
553         call.setExtras(extras);
554 
555         // Do not add the call if it is a potential MMI code.
556         if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
557             call.addListener(this);
558         } else if (!mCalls.contains(call)) {
559             // We check if mCalls already contains the call because we could potentially be reusing
560             // a call which was previously added (See {@link #getNewOutgoingCall}).
561             addCall(call);
562         }
563 
564         return call;
565     }
566 
567     /**
568      * Attempts to issue/connect the specified call.
569      *
570      * @param handle Handle to connect the call with.
571      * @param gatewayInfo Optional gateway information that can be used to route the call to the
572      *        actual dialed handle via a gateway provider. May be null.
573      * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
574      * @param videoState The desired video state for the outgoing call.
575      */
placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)576     void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
577             int videoState) {
578         if (call == null) {
579             // don't do anything if the call no longer exists
580             Log.i(this, "Canceling unknown call.");
581             return;
582         }
583 
584         final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
585 
586         if (gatewayInfo == null) {
587             Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
588         } else {
589             Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
590                     Log.pii(uriHandle), Log.pii(handle));
591         }
592 
593         call.setHandle(uriHandle);
594         call.setGatewayInfo(gatewayInfo);
595         call.setStartWithSpeakerphoneOn(speakerphoneOn);
596         call.setVideoState(videoState);
597 
598         boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
599                 call.getHandle());
600         if (isEmergencyCall) {
601             // Emergency -- CreateConnectionProcessor will choose accounts automatically
602             call.setTargetPhoneAccount(null);
603         }
604 
605         if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
606             // If the account has been set, proceed to place the outgoing call.
607             // Otherwise the connection will be initiated when the account is set by the user.
608             call.startCreateConnection(mPhoneAccountRegistrar);
609         }
610     }
611 
612     /**
613      * Attempts to start a conference call for the specified call.
614      *
615      * @param call The call to conference.
616      * @param otherCall The other call to conference with.
617      */
conference(Call call, Call otherCall)618     void conference(Call call, Call otherCall) {
619         call.conferenceWith(otherCall);
620     }
621 
622     /**
623      * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
624      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
625      * the user opting to answer said call.
626      *
627      * @param call The call to answer.
628      * @param videoState The video state in which to answer the call.
629      */
answerCall(Call call, int videoState)630     void answerCall(Call call, int videoState) {
631         if (!mCalls.contains(call)) {
632             Log.i(this, "Request to answer a non-existent call %s", call);
633         } else {
634             // If the foreground call is not the ringing call and it is currently isActive() or
635             // STATE_DIALING, put it on hold before answering the call.
636             if (mForegroundCall != null && mForegroundCall != call &&
637                     (mForegroundCall.isActive() ||
638                      mForegroundCall.getState() == CallState.DIALING)) {
639                 if (0 == (mForegroundCall.getConnectionCapabilities()
640                         & Connection.CAPABILITY_HOLD)) {
641                     // This call does not support hold.  If it is from a different connection
642                     // service, then disconnect it, otherwise allow the connection service to
643                     // figure out the right states.
644                     if (mForegroundCall.getConnectionService() != call.getConnectionService()) {
645                         mForegroundCall.disconnect();
646                     }
647                 } else {
648                     Call heldCall = getHeldCall();
649                     if (heldCall != null) {
650                         Log.v(this, "Disconnecting held call %s before holding active call.",
651                                 heldCall);
652                         heldCall.disconnect();
653                     }
654 
655                     Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
656                             mForegroundCall, call);
657                     mForegroundCall.hold();
658                 }
659                 // TODO: Wait until we get confirmation of the active call being
660                 // on-hold before answering the new call.
661                 // TODO: Import logic from CallManager.acceptCall()
662             }
663 
664             for (CallsManagerListener listener : mListeners) {
665                 listener.onIncomingCallAnswered(call);
666             }
667 
668             // We do not update the UI until we get confirmation of the answer() through
669             // {@link #markCallAsActive}.
670             call.answer(videoState);
671         }
672     }
673 
674     /**
675      * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
676      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
677      * the user opting to reject said call.
678      */
rejectCall(Call call, boolean rejectWithMessage, String textMessage)679     void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
680         if (!mCalls.contains(call)) {
681             Log.i(this, "Request to reject a non-existent call %s", call);
682         } else {
683             for (CallsManagerListener listener : mListeners) {
684                 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
685             }
686             call.reject(rejectWithMessage, textMessage);
687         }
688     }
689 
690     /**
691      * Instructs Telecom to play the specified DTMF tone within the specified call.
692      *
693      * @param digit The DTMF digit to play.
694      */
playDtmfTone(Call call, char digit)695     void playDtmfTone(Call call, char digit) {
696         if (!mCalls.contains(call)) {
697             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
698         } else {
699             call.playDtmfTone(digit);
700             mDtmfLocalTonePlayer.playTone(call, digit);
701         }
702     }
703 
704     /**
705      * Instructs Telecom to stop the currently playing DTMF tone, if any.
706      */
stopDtmfTone(Call call)707     void stopDtmfTone(Call call) {
708         if (!mCalls.contains(call)) {
709             Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
710         } else {
711             call.stopDtmfTone();
712             mDtmfLocalTonePlayer.stopTone(call);
713         }
714     }
715 
716     /**
717      * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
718      */
postDialContinue(Call call, boolean proceed)719     void postDialContinue(Call call, boolean proceed) {
720         if (!mCalls.contains(call)) {
721             Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
722         } else {
723             call.postDialContinue(proceed);
724         }
725     }
726 
727     /**
728      * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
729      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
730      * the user hitting the end-call button.
731      */
disconnectCall(Call call)732     void disconnectCall(Call call) {
733         Log.v(this, "disconnectCall %s", call);
734 
735         if (!mCalls.contains(call)) {
736             Log.w(this, "Unknown call (%s) asked to disconnect", call);
737         } else {
738             mLocallyDisconnectingCalls.add(call);
739             call.disconnect();
740         }
741     }
742 
743     /**
744      * Instructs Telecom to disconnect all calls.
745      */
disconnectAllCalls()746     void disconnectAllCalls() {
747         Log.v(this, "disconnectAllCalls");
748 
749         for (Call call : mCalls) {
750             disconnectCall(call);
751         }
752     }
753 
754 
755     /**
756      * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
757      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
758      * the user hitting the hold button during an active call.
759      */
holdCall(Call call)760     void holdCall(Call call) {
761         if (!mCalls.contains(call)) {
762             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
763         } else {
764             Log.d(this, "Putting call on hold: (%s)", call);
765             call.hold();
766         }
767     }
768 
769     /**
770      * Instructs Telecom to release the specified call from hold. Intended to be invoked by
771      * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
772      * by the user hitting the hold button during a held call.
773      */
unholdCall(Call call)774     void unholdCall(Call call) {
775         if (!mCalls.contains(call)) {
776             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
777         } else {
778             Log.d(this, "unholding call: (%s)", call);
779             for (Call c : mCalls) {
780                 // Only attempt to hold parent calls and not the individual children.
781                 if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
782                     c.hold();
783                 }
784             }
785             call.unhold();
786         }
787     }
788 
789     /** Called by the in-call UI to change the mute state. */
mute(boolean shouldMute)790     void mute(boolean shouldMute) {
791         mCallAudioManager.mute(shouldMute);
792     }
793 
794     /**
795       * Called by the in-call UI to change the audio route, for example to change from earpiece to
796       * speaker phone.
797       */
setAudioRoute(int route)798     void setAudioRoute(int route) {
799         mCallAudioManager.setAudioRoute(route);
800     }
801 
802     /** Called by the in-call UI to turn the proximity sensor on. */
turnOnProximitySensor()803     void turnOnProximitySensor() {
804         mProximitySensorManager.turnOn();
805     }
806 
807     /**
808      * Called by the in-call UI to turn the proximity sensor off.
809      * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
810      *        the screen will be kept off until the proximity sensor goes negative.
811      */
turnOffProximitySensor(boolean screenOnImmediately)812     void turnOffProximitySensor(boolean screenOnImmediately) {
813         mProximitySensorManager.turnOff(screenOnImmediately);
814     }
815 
phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault)816     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
817         if (!mCalls.contains(call)) {
818             Log.i(this, "Attempted to add account to unknown call %s", call);
819         } else {
820             // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
821             // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the
822             // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
823             // respecting a rewritten number or a canceled number. This is unlikely since
824             // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
825             // a phone account from the in-call UI.
826             call.setTargetPhoneAccount(account);
827 
828             // Note: emergency calls never go through account selection dialog so they never
829             // arrive here.
830             if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
831                 call.startCreateConnection(mPhoneAccountRegistrar);
832             } else {
833                 call.disconnect();
834             }
835 
836             if (setDefault) {
837                 mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account);
838             }
839         }
840     }
841 
842     /** Called when the audio state changes. */
onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState)843     void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) {
844         Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
845         for (CallsManagerListener listener : mListeners) {
846             listener.onAudioStateChanged(oldAudioState, newAudioState);
847         }
848     }
849 
markCallAsRinging(Call call)850     void markCallAsRinging(Call call) {
851         setCallState(call, CallState.RINGING);
852     }
853 
markCallAsDialing(Call call)854     void markCallAsDialing(Call call) {
855         setCallState(call, CallState.DIALING);
856     }
857 
markCallAsActive(Call call)858     void markCallAsActive(Call call) {
859         setCallState(call, CallState.ACTIVE);
860 
861         if (call.getStartWithSpeakerphoneOn()) {
862             setAudioRoute(AudioState.ROUTE_SPEAKER);
863         }
864     }
865 
markCallAsOnHold(Call call)866     void markCallAsOnHold(Call call) {
867         setCallState(call, CallState.ON_HOLD);
868     }
869 
870     /**
871      * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
872      * last live call, then also disconnect from the in-call controller.
873      *
874      * @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}.
875      */
markCallAsDisconnected(Call call, DisconnectCause disconnectCause)876     void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
877         call.setDisconnectCause(disconnectCause);
878         setCallState(call, CallState.DISCONNECTED);
879     }
880 
881     /**
882      * Removes an existing disconnected call, and notifies the in-call app.
883      */
markCallAsRemoved(Call call)884     void markCallAsRemoved(Call call) {
885         removeCall(call);
886         if (mLocallyDisconnectingCalls.contains(call)) {
887             mLocallyDisconnectingCalls.remove(call);
888             if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
889                 mForegroundCall.unhold();
890             }
891         }
892     }
893 
894     /**
895      * Cleans up any calls currently associated with the specified connection service when the
896      * service binder disconnects unexpectedly.
897      *
898      * @param service The connection service that disconnected.
899      */
handleConnectionServiceDeath(ConnectionServiceWrapper service)900     void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
901         if (service != null) {
902             for (Call call : mCalls) {
903                 if (call.getConnectionService() == service) {
904                     if (call.getState() != CallState.DISCONNECTED) {
905                         markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
906                     }
907                     markCallAsRemoved(call);
908                 }
909             }
910         }
911     }
912 
hasAnyCalls()913     boolean hasAnyCalls() {
914         return !mCalls.isEmpty();
915     }
916 
hasActiveOrHoldingCall()917     boolean hasActiveOrHoldingCall() {
918         return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
919     }
920 
hasRingingCall()921     boolean hasRingingCall() {
922         return getFirstCallWithState(CallState.RINGING) != null;
923     }
924 
onMediaButton(int type)925     boolean onMediaButton(int type) {
926         if (hasAnyCalls()) {
927             if (HeadsetMediaButton.SHORT_PRESS == type) {
928                 Call ringingCall = getFirstCallWithState(CallState.RINGING);
929                 if (ringingCall == null) {
930                     mCallAudioManager.toggleMute();
931                     return true;
932                 } else {
933                     ringingCall.answer(ringingCall.getVideoState());
934                     return true;
935                 }
936             } else if (HeadsetMediaButton.LONG_PRESS == type) {
937                 Log.d(this, "handleHeadsetHook: longpress -> hangup");
938                 Call callToHangup = getFirstCallWithState(
939                         CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
940                 if (callToHangup != null) {
941                     callToHangup.disconnect();
942                     return true;
943                 }
944             }
945         }
946         return false;
947     }
948 
949     /**
950      * Returns true if telecom supports adding another top-level call.
951      */
canAddCall()952     boolean canAddCall() {
953         if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
954             return false;
955         }
956 
957         int count = 0;
958         for (Call call : mCalls) {
959             if (call.isEmergencyCall()) {
960                 // We never support add call if one of the calls is an emergency call.
961                 return false;
962             } else if (call.getParentCall() == null) {
963                 count++;
964             }
965 
966             // We do not check states for canAddCall. We treat disconnected calls the same
967             // and wait until they are removed instead. If we didn't count disconnected calls,
968             // we could put InCallServices into a state where they are showing two calls but
969             // also support add-call. Technically it's right, but overall looks better (UI-wise)
970             // and acts better if we wait until the call is removed.
971             if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
972                 return false;
973             }
974         }
975         return true;
976     }
977 
getRingingCall()978     Call getRingingCall() {
979         return getFirstCallWithState(CallState.RINGING);
980     }
981 
getActiveCall()982     Call getActiveCall() {
983         return getFirstCallWithState(CallState.ACTIVE);
984     }
985 
getDialingCall()986     Call getDialingCall() {
987         return getFirstCallWithState(CallState.DIALING);
988     }
989 
getHeldCall()990     Call getHeldCall() {
991         return getFirstCallWithState(CallState.ON_HOLD);
992     }
993 
getNumHeldCalls()994     int getNumHeldCalls() {
995         int count = 0;
996         for (Call call : mCalls) {
997             if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
998                 count++;
999             }
1000         }
1001         return count;
1002     }
1003 
getFirstCallWithState(int... states)1004     Call getFirstCallWithState(int... states) {
1005         return getFirstCallWithState(null, states);
1006     }
1007 
1008     /**
1009      * Returns the first call that it finds with the given states. The states are treated as having
1010      * priority order so that any call with the first state will be returned before any call with
1011      * states listed later in the parameter list.
1012      *
1013      * @param callToSkip Call that this method should skip while searching
1014      */
getFirstCallWithState(Call callToSkip, int... states)1015     Call getFirstCallWithState(Call callToSkip, int... states) {
1016         for (int currentState : states) {
1017             // check the foreground first
1018             if (mForegroundCall != null && mForegroundCall.getState() == currentState) {
1019                 return mForegroundCall;
1020             }
1021 
1022             for (Call call : mCalls) {
1023                 if (Objects.equals(callToSkip, call)) {
1024                     continue;
1025                 }
1026 
1027                 // Only operate on top-level calls
1028                 if (call.getParentCall() != null) {
1029                     continue;
1030                 }
1031 
1032                 if (currentState == call.getState()) {
1033                     return call;
1034                 }
1035             }
1036         }
1037         return null;
1038     }
1039 
createConferenceCall( PhoneAccountHandle phoneAccount, ParcelableConference parcelableConference)1040     Call createConferenceCall(
1041             PhoneAccountHandle phoneAccount,
1042             ParcelableConference parcelableConference) {
1043 
1044         // If the parceled conference specifies a connect time, use it; otherwise default to 0,
1045         // which is the default value for new Calls.
1046         long connectTime =
1047                 parcelableConference.getConnectTimeMillis() ==
1048                         Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
1049                         parcelableConference.getConnectTimeMillis();
1050 
1051         Call call = new Call(
1052                 mContext,
1053                 mConnectionServiceRepository,
1054                 null /* handle */,
1055                 null /* gatewayInfo */,
1056                 null /* connectionManagerPhoneAccount */,
1057                 phoneAccount,
1058                 false /* isIncoming */,
1059                 true /* isConference */,
1060                 connectTime);
1061 
1062         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()));
1063         call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
1064 
1065         // TODO: Move this to be a part of addCall()
1066         call.addListener(this);
1067         addCall(call);
1068         return call;
1069     }
1070 
1071     /**
1072      * @return the call state currently tracked by {@link PhoneStateBroadcaster}
1073      */
getCallState()1074     int getCallState() {
1075         return mPhoneStateBroadcaster.getCallState();
1076     }
1077 
1078     /**
1079      * Retrieves the {@link PhoneAccountRegistrar}.
1080      *
1081      * @return The {@link PhoneAccountRegistrar}.
1082      */
getPhoneAccountRegistrar()1083     PhoneAccountRegistrar getPhoneAccountRegistrar() {
1084         return mPhoneAccountRegistrar;
1085     }
1086 
1087     /**
1088      * Retrieves the {@link MissedCallNotifier}
1089      * @return The {@link MissedCallNotifier}.
1090      */
getMissedCallNotifier()1091     MissedCallNotifier getMissedCallNotifier() {
1092         return mMissedCallNotifier;
1093     }
1094 
1095     /**
1096      * Adds the specified call to the main list of live calls.
1097      *
1098      * @param call The call to add.
1099      */
addCall(Call call)1100     private void addCall(Call call) {
1101         Trace.beginSection("addCall");
1102         Log.v(this, "addCall(%s)", call);
1103         call.addListener(this);
1104         mCalls.add(call);
1105 
1106         // TODO: Update mForegroundCall prior to invoking
1107         // onCallAdded for calls which immediately take the foreground (like the first call).
1108         for (CallsManagerListener listener : mListeners) {
1109             if (Log.SYSTRACE_DEBUG) {
1110                 Trace.beginSection(listener.getClass().toString() + " addCall");
1111             }
1112             listener.onCallAdded(call);
1113             if (Log.SYSTRACE_DEBUG) {
1114                 Trace.endSection();
1115             }
1116         }
1117         updateCallsManagerState();
1118         Trace.endSection();
1119     }
1120 
removeCall(Call call)1121     private void removeCall(Call call) {
1122         Trace.beginSection("removeCall");
1123         Log.v(this, "removeCall(%s)", call);
1124 
1125         call.setParentCall(null);  // need to clean up parent relationship before destroying.
1126         call.removeListener(this);
1127         call.clearConnectionService();
1128 
1129         boolean shouldNotify = false;
1130         if (mCalls.contains(call)) {
1131             mCalls.remove(call);
1132             shouldNotify = true;
1133         }
1134 
1135         // Only broadcast changes for calls that are being tracked.
1136         if (shouldNotify) {
1137             for (CallsManagerListener listener : mListeners) {
1138                 if (Log.SYSTRACE_DEBUG) {
1139                     Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
1140                 }
1141                 listener.onCallRemoved(call);
1142                 if (Log.SYSTRACE_DEBUG) {
1143                     Trace.endSection();
1144                 }
1145             }
1146             updateCallsManagerState();
1147         }
1148         Trace.endSection();
1149     }
1150 
1151     /**
1152      * Sets the specified state on the specified call.
1153      *
1154      * @param call The call.
1155      * @param newState The new state of the call.
1156      */
setCallState(Call call, int newState)1157     private void setCallState(Call call, int newState) {
1158         if (call == null) {
1159             return;
1160         }
1161         int oldState = call.getState();
1162         Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
1163                 CallState.toString(newState), call);
1164         if (newState != oldState) {
1165             // Unfortunately, in the telephony world the radio is king. So if the call notifies
1166             // us that the call is in a particular state, we allow it even if it doesn't make
1167             // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
1168             // TODO: Consider putting a stop to the above and turning CallState
1169             // into a well-defined state machine.
1170             // TODO: Define expected state transitions here, and log when an
1171             // unexpected transition occurs.
1172             call.setState(newState);
1173 
1174             Trace.beginSection("onCallStateChanged");
1175             // Only broadcast state change for calls that are being tracked.
1176             if (mCalls.contains(call)) {
1177                 for (CallsManagerListener listener : mListeners) {
1178                     if (Log.SYSTRACE_DEBUG) {
1179                         Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
1180                     }
1181                     listener.onCallStateChanged(call, oldState, newState);
1182                     if (Log.SYSTRACE_DEBUG) {
1183                         Trace.endSection();
1184                     }
1185                 }
1186                 updateCallsManagerState();
1187             }
1188             Trace.endSection();
1189         }
1190     }
1191 
1192     /**
1193      * Checks which call should be visible to the user and have audio focus.
1194      */
updateForegroundCall()1195     private void updateForegroundCall() {
1196         Trace.beginSection("updateForegroundCall");
1197         Call newForegroundCall = null;
1198         for (Call call : mCalls) {
1199             // TODO: Foreground-ness needs to be explicitly set. No call, regardless
1200             // of its state will be foreground by default and instead the connection service should
1201             // be notified when its calls enter and exit foreground state. Foreground will mean that
1202             // the call should play audio and listen to microphone if it wants.
1203 
1204             // Only top-level calls can be in foreground
1205             if (call.getParentCall() != null) {
1206                 continue;
1207             }
1208 
1209             // Active calls have priority.
1210             if (call.isActive()) {
1211                 newForegroundCall = call;
1212                 break;
1213             }
1214 
1215             if (call.isAlive() || call.getState() == CallState.RINGING) {
1216                 newForegroundCall = call;
1217                 // Don't break in case there's an active call that has priority.
1218             }
1219         }
1220 
1221         if (newForegroundCall != mForegroundCall) {
1222             Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
1223             Call oldForegroundCall = mForegroundCall;
1224             mForegroundCall = newForegroundCall;
1225 
1226             for (CallsManagerListener listener : mListeners) {
1227                 if (Log.SYSTRACE_DEBUG) {
1228                     Trace.beginSection(listener.getClass().toString() + " updateForegroundCall");
1229                 }
1230                 listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
1231                 if (Log.SYSTRACE_DEBUG) {
1232                     Trace.endSection();
1233                 }
1234             }
1235         }
1236         Trace.endSection();
1237     }
1238 
updateCanAddCall()1239     private void updateCanAddCall() {
1240         boolean newCanAddCall = canAddCall();
1241         if (newCanAddCall != mCanAddCall) {
1242             mCanAddCall = newCanAddCall;
1243             for (CallsManagerListener listener : mListeners) {
1244                 if (Log.SYSTRACE_DEBUG) {
1245                     Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
1246                 }
1247                 listener.onCanAddCallChanged(mCanAddCall);
1248                 if (Log.SYSTRACE_DEBUG) {
1249                     Trace.endSection();
1250                 }
1251             }
1252         }
1253     }
1254 
updateCallsManagerState()1255     private void updateCallsManagerState() {
1256         updateForegroundCall();
1257         updateCanAddCall();
1258     }
1259 
isPotentialMMICode(Uri handle)1260     private boolean isPotentialMMICode(Uri handle) {
1261         return (handle != null && handle.getSchemeSpecificPart() != null
1262                 && handle.getSchemeSpecificPart().contains("#"));
1263     }
1264 
1265     /**
1266      * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
1267      * MMI codes which can be dialed when one or more calls are in progress.
1268      * <P>
1269      * Checks for numbers formatted similar to the MMI codes defined in:
1270      * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)}
1271      * and
1272      * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)}
1273      *
1274      * @param handle The URI to call.
1275      * @return {@code True} if the URI represents a number which could be an in-call MMI code.
1276      */
isPotentialInCallMMICode(Uri handle)1277     private boolean isPotentialInCallMMICode(Uri handle) {
1278         if (handle != null && handle.getSchemeSpecificPart() != null &&
1279                 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
1280 
1281             String dialedNumber = handle.getSchemeSpecificPart();
1282             return (dialedNumber.equals("0") ||
1283                     (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
1284                     (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
1285                     dialedNumber.equals("3") ||
1286                     dialedNumber.equals("4") ||
1287                     dialedNumber.equals("5"));
1288         }
1289         return false;
1290     }
1291 
getNumCallsWithState(int... states)1292     private int getNumCallsWithState(int... states) {
1293         int count = 0;
1294         for (int state : states) {
1295             for (Call call : mCalls) {
1296                 if (call.getParentCall() == null && call.getState() == state) {
1297                     count++;
1298                 }
1299             }
1300         }
1301         return count;
1302     }
1303 
hasMaximumLiveCalls()1304     private boolean hasMaximumLiveCalls() {
1305         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
1306     }
1307 
hasMaximumHoldingCalls()1308     private boolean hasMaximumHoldingCalls() {
1309         return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
1310     }
1311 
hasMaximumRingingCalls()1312     private boolean hasMaximumRingingCalls() {
1313         return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
1314     }
1315 
hasMaximumOutgoingCalls()1316     private boolean hasMaximumOutgoingCalls() {
1317         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
1318     }
1319 
makeRoomForOutgoingCall(Call call, boolean isEmergency)1320     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
1321         if (hasMaximumLiveCalls()) {
1322             // NOTE: If the amount of live calls changes beyond 1, this logic will probably
1323             // have to change.
1324             Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES);
1325             Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
1326                    liveCall);
1327 
1328             if (call == liveCall) {
1329                 // If the call is already the foreground call, then we are golden.
1330                 // This can happen after the user selects an account in the PRE_DIAL_WAIT
1331                 // state since the call was already populated into the list.
1332                 return true;
1333             }
1334 
1335             if (hasMaximumOutgoingCalls()) {
1336                 // Disconnect the current outgoing call if it's not an emergency call. If the user
1337                 // tries to make two outgoing calls to different emergency call numbers, we will try
1338                 // to connect the first outgoing call.
1339                 if (isEmergency) {
1340                     Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
1341                     if (!outgoingCall.isEmergencyCall()) {
1342                         outgoingCall.disconnect();
1343                         return true;
1344                     }
1345                 }
1346                 return false;
1347             }
1348 
1349             if (hasMaximumHoldingCalls()) {
1350                 // There is no more room for any more calls, unless it's an emergency.
1351                 if (isEmergency) {
1352                     // Kill the current active call, this is easier then trying to disconnect a
1353                     // holding call and hold an active call.
1354                     liveCall.disconnect();
1355                     return true;
1356                 }
1357                 return false;  // No more room!
1358             }
1359 
1360             // We have room for at least one more holding call at this point.
1361 
1362             // First thing, if we are trying to make a call with the same phone account as the live
1363             // call, then allow it so that the connection service can make its own decision about
1364             // how to handle the new call relative to the current one.
1365             if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) {
1366                 return true;
1367             } else if (call.getTargetPhoneAccount() == null) {
1368                 // Without a phone account, we can't say reliably that the call will fail.
1369                 // If the user chooses the same phone account as the live call, then it's
1370                 // still possible that the call can be made (like with CDMA calls not supporting
1371                 // hold but they still support adding a call by going immediately into conference
1372                 // mode). Return true here and we'll run this code again after user chooses an
1373                 // account.
1374                 return true;
1375             }
1376 
1377             // Try to hold the live call before attempting the new outgoing call.
1378             if (liveCall.can(Connection.CAPABILITY_HOLD)) {
1379                 liveCall.hold();
1380                 return true;
1381             }
1382 
1383             // The live call cannot be held so we're out of luck here.  There's no room.
1384             return false;
1385         }
1386         return true;
1387     }
1388 
1389     /**
1390      * Creates a new call for an existing connection.
1391      *
1392      * @param callId The id of the new call.
1393      * @param connection The connection information.
1394      * @return The new call.
1395      */
createCallForExistingConnection(String callId, ParcelableConnection connection)1396     Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
1397         Call call = new Call(
1398                 mContext,
1399                 mConnectionServiceRepository,
1400                 connection.getHandle() /* handle */,
1401                 null /* gatewayInfo */,
1402                 null /* connectionManagerPhoneAccount */,
1403                 connection.getPhoneAccount(), /* targetPhoneAccountHandle */
1404                 false /* isIncoming */,
1405                 false /* isConference */);
1406 
1407         setCallState(call, Call.getStateFromConnectionState(connection.getState()));
1408         call.setConnectionCapabilities(connection.getConnectionCapabilities());
1409         call.setCallerDisplayName(connection.getCallerDisplayName(),
1410                 connection.getCallerDisplayNamePresentation());
1411 
1412         call.addListener(this);
1413         addCall(call);
1414 
1415         return call;
1416     }
1417 
1418     /**
1419      * Dumps the state of the {@link CallsManager}.
1420      *
1421      * @param pw The {@code IndentingPrintWriter} to write the state to.
1422      */
dump(IndentingPrintWriter pw)1423     public void dump(IndentingPrintWriter pw) {
1424         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1425         if (mCalls != null) {
1426             pw.println("mCalls: ");
1427             pw.increaseIndent();
1428             for (Call call : mCalls) {
1429                 pw.println(call);
1430             }
1431             pw.decreaseIndent();
1432         }
1433         pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall));
1434 
1435         if (mCallAudioManager != null) {
1436             pw.println("mCallAudioManager:");
1437             pw.increaseIndent();
1438             mCallAudioManager.dump(pw);
1439             pw.decreaseIndent();
1440         }
1441 
1442         if (mTtyManager != null) {
1443             pw.println("mTtyManager:");
1444             pw.increaseIndent();
1445             mTtyManager.dump(pw);
1446             pw.decreaseIndent();
1447         }
1448 
1449         if (mInCallController != null) {
1450             pw.println("mInCallController:");
1451             pw.increaseIndent();
1452             mInCallController.dump(pw);
1453             pw.decreaseIndent();
1454         }
1455 
1456         if (mConnectionServiceRepository != null) {
1457             pw.println("mConnectionServiceRepository:");
1458             pw.increaseIndent();
1459             mConnectionServiceRepository.dump(pw);
1460             pw.decreaseIndent();
1461         }
1462     }
1463 }
1464