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