• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.annotation.NonNull;
20 import android.content.Context;
21 import android.media.IAudioService;
22 import android.media.ToneGenerator;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.UserHandle;
26 import android.telecom.CallAudioState;
27 import android.telecom.Log;
28 import android.telecom.VideoProfile;
29 import android.util.SparseArray;
30 
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.internal.util.IndentingPrintWriter;
33 import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder;
34 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
35 import com.android.server.telecom.flags.FeatureFlags;
36 
37 import java.util.Collection;
38 import java.util.HashSet;
39 import java.util.Set;
40 import java.util.LinkedHashSet;
41 import java.util.concurrent.CompletableFuture;
42 import java.util.stream.Collectors;
43 
44 
45 public class CallAudioManager extends CallsManagerListenerBase {
46 
47     public interface AudioServiceFactory {
getAudioService()48         IAudioService getAudioService();
49     }
50 
51     private final String LOG_TAG = CallAudioManager.class.getSimpleName();
52 
53     private final LinkedHashSet<Call> mActiveDialingOrConnectingCalls;
54     private final LinkedHashSet<Call> mRingingCalls;
55     private final LinkedHashSet<Call> mHoldingCalls;
56     private final LinkedHashSet<Call> mAudioProcessingCalls;
57     private final Set<Call> mCalls;
58     private final SparseArray<LinkedHashSet<Call>> mCallStateToCalls;
59 
60     private final CallAudioRouteAdapter mCallAudioRouteAdapter;
61     private final CallAudioModeStateMachine mCallAudioModeStateMachine;
62     private final BluetoothStateReceiver mBluetoothStateReceiver;
63     private final CallsManager mCallsManager;
64     private final InCallTonePlayer.Factory mPlayerFactory;
65     private final Ringer mRinger;
66     private final RingbackPlayer mRingbackPlayer;
67     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
68     private final FeatureFlags mFeatureFlags;
69 
70     private Call mStreamingCall;
71     private Call mForegroundCall;
72     private CompletableFuture<Boolean> mCallRingingFuture;
73     private Thread mBtIcsBindingThread;
74     private boolean mIsTonePlaying = false;
75     private boolean mIsDisconnectedTonePlaying = false;
76     private InCallTonePlayer mHoldTonePlayer;
77     private final HandlerThread mHandlerThread;
78     private final Handler mHandler;
79 
CallAudioManager(CallAudioRouteAdapter callAudioRouteAdapter, CallsManager callsManager, CallAudioModeStateMachine callAudioModeStateMachine, InCallTonePlayer.Factory playerFactory, Ringer ringer, RingbackPlayer ringbackPlayer, BluetoothStateReceiver bluetoothStateReceiver, DtmfLocalTonePlayer dtmfLocalTonePlayer, FeatureFlags featureFlags)80     public CallAudioManager(CallAudioRouteAdapter callAudioRouteAdapter,
81             CallsManager callsManager,
82             CallAudioModeStateMachine callAudioModeStateMachine,
83             InCallTonePlayer.Factory playerFactory,
84             Ringer ringer,
85             RingbackPlayer ringbackPlayer,
86             BluetoothStateReceiver bluetoothStateReceiver,
87             DtmfLocalTonePlayer dtmfLocalTonePlayer,
88             FeatureFlags featureFlags) {
89         mActiveDialingOrConnectingCalls = new LinkedHashSet<>(1);
90         mRingingCalls = new LinkedHashSet<>(1);
91         mHoldingCalls = new LinkedHashSet<>(1);
92         mAudioProcessingCalls = new LinkedHashSet<>(1);
93         mStreamingCall = null;
94         mCalls = new HashSet<>();
95         mCallStateToCalls = new SparseArray<LinkedHashSet<Call>>() {{
96             put(CallState.CONNECTING, mActiveDialingOrConnectingCalls);
97             put(CallState.ACTIVE, mActiveDialingOrConnectingCalls);
98             put(CallState.DIALING, mActiveDialingOrConnectingCalls);
99             put(CallState.PULLING, mActiveDialingOrConnectingCalls);
100             put(CallState.RINGING, mRingingCalls);
101             put(CallState.ON_HOLD, mHoldingCalls);
102             put(CallState.SIMULATED_RINGING, mRingingCalls);
103             put(CallState.AUDIO_PROCESSING, mAudioProcessingCalls);
104         }};
105 
106         mCallAudioRouteAdapter = callAudioRouteAdapter;
107         mCallAudioModeStateMachine = callAudioModeStateMachine;
108         mCallsManager = callsManager;
109         mPlayerFactory = playerFactory;
110         mRinger = ringer;
111         mRingbackPlayer = ringbackPlayer;
112         mBluetoothStateReceiver = bluetoothStateReceiver;
113         mDtmfLocalTonePlayer = dtmfLocalTonePlayer;
114         mFeatureFlags = featureFlags;
115         mHandlerThread = new HandlerThread(this.getClass().getSimpleName());
116         mHandlerThread.start();
117         mHandler = new Handler(mHandlerThread.getLooper());
118 
119         mPlayerFactory.setCallAudioManager(this);
120         mCallAudioModeStateMachine.setCallAudioManager(this);
121         mCallAudioRouteAdapter.setCallAudioManager(this);
122     }
123 
124     @Override
onCallStateChanged(Call call, int oldState, int newState)125     public void onCallStateChanged(Call call, int oldState, int newState) {
126         if (shouldIgnoreCallForAudio(call)) {
127             // No audio management for calls in a conference, or external calls.
128             return;
129         }
130         if (oldState == newState) {
131             // State did not change, so no need to do anything.
132             return;
133         }
134         Log.i(this, "onCallStateChanged: Call state changed for TC@%s: %s -> %s", call.getId(),
135                 CallState.toString(oldState), CallState.toString(newState));
136 
137         removeCallFromAllBins(call);
138         HashSet<Call> newBinForCall = getBinForCall(call);
139         if (newBinForCall != null) {
140             newBinForCall.add(call);
141         }
142         sendCallStatusToBluetoothStateReceiver();
143 
144         updateForegroundCall();
145         if (shouldPlayDisconnectTone(oldState, newState)) {
146             playToneForDisconnectedCall(call);
147         } else {
148             if (newState == CallState.DISCONNECTED) {
149                 // This call is not disconnected, but it won't generate a disconnect tone, so
150                 // complete the future to ensure we unbind from BT promptly.
151                 completeDisconnectToneFuture(call);
152             }
153         }
154 
155         onCallLeavingState(call, oldState);
156         onCallEnteringState(call, newState);
157     }
158 
159     @Override
onCallAdded(Call call)160     public void onCallAdded(Call call) {
161         if (shouldIgnoreCallForAudio(call)) {
162             return; // Don't do audio handling for calls in a conference, or external calls.
163         }
164 
165         addCall(call);
166     }
167 
168     @Override
onCallRemoved(Call call)169     public void onCallRemoved(Call call) {
170         if (mStreamingCall == call) {
171             mStreamingCall = null;
172         }
173         if (shouldIgnoreCallForAudio(call)) {
174             return; // Don't do audio handling for calls in a conference, or external calls.
175         }
176 
177         removeCall(call);
178     }
179 
addCall(Call call)180     private void addCall(Call call) {
181         if (mCalls.contains(call)) {
182             Log.w(LOG_TAG, "Call TC@%s is being added twice.", call.getId());
183             return; // No guarantees that the same call won't get added twice.
184         }
185 
186         Log.d(LOG_TAG, "Call added with id TC@%s in state %s", call.getId(),
187                 CallState.toString(call.getState()));
188 
189         HashSet<Call> newBinForCall = getBinForCall(call);
190         if (newBinForCall != null) {
191             newBinForCall.add(call);
192         }
193         updateForegroundCall();
194         mCalls.add(call);
195         sendCallStatusToBluetoothStateReceiver();
196 
197         onCallEnteringState(call, call.getState());
198     }
199 
removeCall(Call call)200     private void removeCall(Call call) {
201         if (!mCalls.contains(call)) {
202             return; // No guarantees that the same call won't get removed twice.
203         }
204 
205         Log.d(LOG_TAG, "Call removed with id TC@%s in state %s", call.getId(),
206                 CallState.toString(call.getState()));
207 
208         removeCallFromAllBins(call);
209 
210         updateForegroundCall();
211         mCalls.remove(call);
212         sendCallStatusToBluetoothStateReceiver();
213 
214         onCallLeavingState(call, call.getState());
215     }
216 
sendCallStatusToBluetoothStateReceiver()217     private void sendCallStatusToBluetoothStateReceiver() {
218         // We're in a call if there are calls in mCalls that are not in mAudioProcessingCalls.
219         boolean isInCall = !mAudioProcessingCalls.containsAll(mCalls);
220         mBluetoothStateReceiver.setIsInCall(isInCall);
221     }
222 
223     /**
224      * Handles changes to the external state of a call.  External calls which become regular calls
225      * should be tracked, and regular calls which become external should no longer be tracked.
226      *
227      * @param call The call.
228      * @param isExternalCall {@code True} if the call is now external, {@code false} if it is now
229      *      a regular call.
230      */
231     @Override
onExternalCallChanged(Call call, boolean isExternalCall)232     public void onExternalCallChanged(Call call, boolean isExternalCall) {
233         if (isExternalCall) {
234             Log.d(LOG_TAG, "Removing call which became external ID %s", call.getId());
235             removeCall(call);
236         } else if (!isExternalCall) {
237             Log.d(LOG_TAG, "Adding external call which was pulled with ID %s", call.getId());
238             addCall(call);
239 
240             if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(call.getVideoState())) {
241                 // When pulling a video call, automatically enable the speakerphone.
242                 Log.d(LOG_TAG, "Switching to speaker because external video call %s was pulled." +
243                         call.getId());
244                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
245                         CallAudioRouteStateMachine.SWITCH_SPEAKER);
246             }
247         }
248     }
249 
250     /**
251      * Handles the changes to the streaming state of a call.
252      * @param call The call
253      * @param isStreaming {@code true} if the call is streaming, {@code false} otherwise
254      */
255     @Override
onCallStreamingStateChanged(Call call, boolean isStreaming)256     public void onCallStreamingStateChanged(Call call, boolean isStreaming) {
257         if (isStreaming) {
258             if (mStreamingCall == null) {
259                 mStreamingCall = call;
260                 mCallAudioModeStateMachine.sendMessageWithArgs(
261                         CallAudioModeStateMachine.START_CALL_STREAMING,
262                         makeArgsForModeStateMachine());
263             } else {
264                 Log.w(LOG_TAG, "Unexpected streaming call request for call %s while call "
265                         + "%s is streaming.", call.getId(), mStreamingCall.getId());
266             }
267         } else {
268             if (mStreamingCall == call) {
269                 mStreamingCall = null;
270                 mCallAudioModeStateMachine.sendMessageWithArgs(
271                         CallAudioModeStateMachine.STOP_CALL_STREAMING,
272                         makeArgsForModeStateMachine());
273             } else {
274                 Log.w(LOG_TAG, "Unexpected call streaming stop request for call %s while this call "
275                         + "is not streaming.", call.getId());
276             }
277         }
278     }
279 
280     /**
281      * Determines if {@link CallAudioManager} should do any audio routing operations for a call.
282      * We ignore child calls of a conference and external calls for audio routing purposes.
283      *
284      * @param call The call to check.
285      * @return {@code true} if the call should be ignored for audio routing, {@code false}
286      * otherwise
287      */
shouldIgnoreCallForAudio(Call call)288     private boolean shouldIgnoreCallForAudio(Call call) {
289         return call.getParentCall() != null || call.isExternalCall();
290     }
291 
292     @Override
onIncomingCallAnswered(Call call)293     public void onIncomingCallAnswered(Call call) {
294         if (!mCalls.contains(call)) {
295             return;
296         }
297 
298         // Turn off mute when a new incoming call is answered iff it's not a handover.
299         if (!call.isHandoverInProgress()) {
300             mute(false /* shouldMute */);
301         }
302 
303         maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call);
304     }
305 
306     @Override
onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)307     public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
308         if (videoProfile == null) {
309             return;
310         }
311 
312         if (call != mForegroundCall) {
313             // We only play tones for foreground calls.
314             return;
315         }
316 
317         int previousVideoState = call.getVideoState();
318         int newVideoState = videoProfile.getVideoState();
319         Log.v(this, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
320                 .videoStateToString(newVideoState));
321 
322         boolean isUpgradeRequest = !VideoProfile.isReceptionEnabled(previousVideoState) &&
323                 VideoProfile.isReceptionEnabled(newVideoState);
324 
325         if (isUpgradeRequest) {
326             mPlayerFactory.createPlayer(call, InCallTonePlayer.TONE_VIDEO_UPGRADE).startTone();
327         }
328     }
329 
playRttUpgradeTone(Call call)330     public void playRttUpgradeTone(Call call) {
331         if (call != mForegroundCall) {
332             // We only play tones for foreground calls.
333             return;
334         }
335         mPlayerFactory.createPlayer(call, InCallTonePlayer.TONE_RTT_REQUEST).startTone();
336     }
337 
338     /**
339      * Play or stop a call hold tone for a call.  Triggered via
340      * {@link Connection#sendConnectionEvent(String)} when the
341      * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
342      * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
343      *
344      * @param call The call which requested the hold tone.
345      */
346     @Override
onHoldToneRequested(Call call)347     public void onHoldToneRequested(Call call) {
348         maybePlayHoldTone(call);
349     }
350 
351     @Override
onIsVoipAudioModeChanged(Call call)352     public void onIsVoipAudioModeChanged(Call call) {
353         if (call != mForegroundCall) {
354             return;
355         }
356         mCallAudioModeStateMachine.sendMessageWithArgs(
357                 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE,
358                 makeArgsForModeStateMachine());
359     }
360 
361     @Override
onRingbackRequested(Call call, boolean shouldRingback)362     public void onRingbackRequested(Call call, boolean shouldRingback) {
363         if (call == mForegroundCall && shouldRingback) {
364             mRingbackPlayer.startRingbackForCall(call);
365         } else {
366             mRingbackPlayer.stopRingbackForCall(call);
367         }
368     }
369 
370     @Override
onIncomingCallRejected(Call call, boolean rejectWithMessage, String message)371     public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String message) {
372         maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call);
373     }
374 
375     @Override
onIsConferencedChanged(Call call)376     public void onIsConferencedChanged(Call call) {
377         // This indicates a conferencing change, which shouldn't impact any audio mode stuff.
378         Call parentCall = call.getParentCall();
379         if (parentCall == null) {
380             // Indicates that the call should be tracked for audio purposes. Treat it as if it were
381             // just added.
382             Log.i(LOG_TAG, "Call TC@" + call.getId() + " left conference and will" +
383                             " now be tracked by CallAudioManager.");
384             onCallAdded(call);
385         } else {
386             // The call joined a conference, so stop tracking it.
387             removeCallFromAllBins(call);
388             updateForegroundCall();
389             mCalls.remove(call);
390         }
391     }
392 
393     @Override
onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs, ConnectionServiceWrapper newCs)394     public void onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs,
395             ConnectionServiceWrapper newCs) {
396         mCallAudioRouteAdapter.sendMessageWithSessionInfo(
397                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
398     }
399 
400     @Override
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)401     public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
402         if (call != getForegroundCall()) {
403             Log.d(LOG_TAG, "Ignoring video state change from %s to %s for call %s -- not " +
404                     "foreground.", VideoProfile.videoStateToString(previousVideoState),
405                     VideoProfile.videoStateToString(newVideoState), call.getId());
406             return;
407         }
408 
409         if (!VideoProfile.isVideo(previousVideoState) &&
410                 mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) {
411             Log.d(LOG_TAG, "Switching to speaker because call %s transitioned video state from %s" +
412                     " to %s", call.getId(), VideoProfile.videoStateToString(previousVideoState),
413                     VideoProfile.videoStateToString(newVideoState));
414             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
415                     CallAudioRouteStateMachine.SWITCH_SPEAKER);
416         }
417     }
418 
getCallAudioState()419     public CallAudioState getCallAudioState() {
420         return mCallAudioRouteAdapter.getCurrentCallAudioState();
421     }
422 
getPossiblyHeldForegroundCall()423     public Call getPossiblyHeldForegroundCall() {
424         return mForegroundCall;
425     }
426 
getForegroundCall()427     public Call getForegroundCall() {
428         if (mForegroundCall != null && mForegroundCall.getState() != CallState.ON_HOLD) {
429             return mForegroundCall;
430         }
431         return null;
432     }
433 
434     @VisibleForTesting
toggleMute()435     public void toggleMute() {
436         // Don't mute if there are any emergency calls.
437         if (mCallsManager.isInEmergencyCall()) {
438             Log.v(this, "ignoring toggleMute for emergency call");
439             return;
440         }
441         mCallAudioRouteAdapter.sendMessageWithSessionInfo(
442                 CallAudioRouteStateMachine.TOGGLE_MUTE);
443     }
444 
445     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
onRingerModeChange()446     public void onRingerModeChange() {
447         if (mFeatureFlags.ensureInCarRinging()) {
448             // Stop the current ringtone before attempting to start the new ringtone:
449             stopRinging();
450         }
451         mCallAudioModeStateMachine.sendMessageWithArgs(
452                 CallAudioModeStateMachine.RINGER_MODE_CHANGE, makeArgsForModeStateMachine());
453     }
454 
455     @VisibleForTesting
mute(boolean shouldMute)456     public void mute(boolean shouldMute) {
457         Log.v(this, "mute, shouldMute: %b", shouldMute);
458 
459         // Don't mute if there are any emergency calls.
460         if (mCallsManager.isInEmergencyCall()) {
461             shouldMute = false;
462             Log.v(this, "ignoring mute for emergency call");
463         }
464 
465         mCallAudioRouteAdapter.sendMessageWithSessionInfo(shouldMute
466                 ? CallAudioRouteStateMachine.MUTE_ON : CallAudioRouteStateMachine.MUTE_OFF);
467     }
468 
469     /**
470      * Changed the audio route, for example from earpiece to speaker phone.
471      *
472      * @param route The new audio route to use. See {@link CallAudioState}.
473      * @param bluetoothAddress the address of the desired bluetooth device, if route is
474      * {@link CallAudioState#ROUTE_BLUETOOTH}.
475      */
476     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
setAudioRoute(int route, String bluetoothAddress)477     public void setAudioRoute(int route, String bluetoothAddress) {
478         Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
479         switch (route) {
480             case CallAudioState.ROUTE_BLUETOOTH:
481                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
482                         CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH, 0, bluetoothAddress);
483                 return;
484             case CallAudioState.ROUTE_SPEAKER:
485                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
486                         CallAudioRouteStateMachine.USER_SWITCH_SPEAKER);
487                 return;
488             case CallAudioState.ROUTE_WIRED_HEADSET:
489                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
490                         CallAudioRouteStateMachine.USER_SWITCH_HEADSET);
491                 return;
492             case CallAudioState.ROUTE_EARPIECE:
493                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
494                         CallAudioRouteStateMachine.USER_SWITCH_EARPIECE);
495                 return;
496             case CallAudioState.ROUTE_WIRED_OR_EARPIECE:
497                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
498                         CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
499                         CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE);
500                 return;
501             default:
502                 Log.w(this, "InCallService requested an invalid audio route: %d", route);
503         }
504     }
505 
506     /**
507      * Switch call audio routing to the baseline route, including bluetooth headsets if there are
508      * any connected.
509      */
switchBaseline()510     void switchBaseline() {
511         Log.i(this, "switchBaseline");
512         mCallAudioRouteAdapter.sendMessageWithSessionInfo(
513                 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
514                 CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE);
515     }
516 
silenceRingers(Context context, UserHandle callingUser, boolean hasCrossUserPermission)517     Set<UserHandle> silenceRingers(Context context, UserHandle callingUser,
518             boolean hasCrossUserPermission) {
519         // Store all users from calls that were silenced so that we can silence the
520         // InCallServices which are associated with those users.
521         Set<UserHandle> userHandles = new HashSet<>();
522         boolean allCallSilenced = true;
523         synchronized (mCallsManager.getLock()) {
524             for (Call call : mRingingCalls) {
525                 UserHandle userFromCall = call.getAssociatedUser();
526                 // Do not try to silence calls when calling user is different from the phone account
527                 // user, the account does not have CAPABILITY_MULTI_USER enabled, or if the user
528                 // does not have the INTERACT_ACROSS_USERS permission enabled.
529                 if (!hasCrossUserPermission && !mCallsManager
530                         .isCallVisibleForUser(call, callingUser)) {
531                     allCallSilenced = false;
532                     continue;
533                 }
534                 userHandles.add(userFromCall);
535                 call.silence();
536             }
537 
538             // If all the calls were silenced, we can stop the ringer.
539             if (allCallSilenced) {
540                 mRinger.stopRinging();
541                 mRinger.stopCallWaiting();
542             }
543         }
544         return userHandles;
545     }
546 
isRingtonePlaying()547     public boolean isRingtonePlaying() {
548         return mRinger.isRinging();
549     }
550 
551     @VisibleForTesting
startRinging()552     public boolean startRinging() {
553         synchronized (mCallsManager.getLock()) {
554             Call localForegroundCall = mForegroundCall;
555             boolean result = mRinger.startRinging(localForegroundCall,
556                     mCallAudioRouteAdapter.isHfpDeviceAvailable());
557             if (result) {
558                 localForegroundCall.setStartRingTime();
559             }
560             return result;
561         }
562     }
563 
564     @VisibleForTesting
startCallWaiting(String reason)565     public void startCallWaiting(String reason) {
566         synchronized (mCallsManager.getLock()) {
567             if (mRingingCalls.size() == 1) {
568                 mRinger.startCallWaiting(mRingingCalls.iterator().next(), reason);
569             }
570         }
571     }
572 
573     @VisibleForTesting
stopRinging()574     public void stopRinging() {
575         synchronized (mCallsManager.getLock()) {
576             mRinger.stopRinging();
577         }
578     }
579 
580     @VisibleForTesting
stopCallWaiting()581     public void stopCallWaiting() {
582         synchronized (mCallsManager.getLock()) {
583             mRinger.stopCallWaiting();
584         }
585     }
586 
587     @VisibleForTesting
setCallAudioRouteFocusState(int focusState)588     public void setCallAudioRouteFocusState(int focusState) {
589         if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
590             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
591                     CallAudioRouteStateMachine.SWITCH_FOCUS, focusState, 0);
592         } else {
593             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
594                     CallAudioRouteStateMachine.SWITCH_FOCUS, focusState);
595         }
596     }
597 
setCallAudioRouteFocusStateForEndTone()598     public void setCallAudioRouteFocusStateForEndTone() {
599         if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
600             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
601                     CallAudioRouteStateMachine.SWITCH_FOCUS,
602                     CallAudioRouteStateMachine.ACTIVE_FOCUS, 1);
603         } else {
604             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
605                     CallAudioRouteStateMachine.SWITCH_FOCUS,
606                     CallAudioRouteStateMachine.ACTIVE_FOCUS);
607         }
608     }
609 
notifyAudioOperationsComplete()610     public void notifyAudioOperationsComplete() {
611         mCallAudioModeStateMachine.sendMessageWithArgs(
612                 CallAudioModeStateMachine.AUDIO_OPERATIONS_COMPLETE, makeArgsForModeStateMachine());
613     }
614 
615     @VisibleForTesting
getCallAudioRouteAdapter()616     public CallAudioRouteAdapter getCallAudioRouteAdapter() {
617         return mCallAudioRouteAdapter;
618     }
619 
620     @VisibleForTesting
getCallAudioModeStateMachine()621     public CallAudioModeStateMachine getCallAudioModeStateMachine() {
622         return mCallAudioModeStateMachine;
623     }
624 
dump(IndentingPrintWriter pw)625     void dump(IndentingPrintWriter pw) {
626         pw.println("All calls:");
627         pw.increaseIndent();
628         dumpCallsInCollection(pw, mCalls);
629         pw.decreaseIndent();
630 
631         pw.println("Active dialing, or connecting calls:");
632         pw.increaseIndent();
633         dumpCallsInCollection(pw, mActiveDialingOrConnectingCalls);
634         pw.decreaseIndent();
635 
636         pw.println("Ringing calls:");
637         pw.increaseIndent();
638         dumpCallsInCollection(pw, mRingingCalls);
639         pw.decreaseIndent();
640 
641         pw.println("Holding calls:");
642         pw.increaseIndent();
643         dumpCallsInCollection(pw, mHoldingCalls);
644         pw.decreaseIndent();
645 
646         pw.println("Foreground call:");
647         pw.println(mForegroundCall);
648 
649         pw.println("CallAudioModeStateMachine:");
650         pw.increaseIndent();
651         mCallAudioModeStateMachine.dump(pw);
652         pw.decreaseIndent();
653 
654         pw.println("mCallAudioRouteAdapter:");
655         pw.increaseIndent();
656         mCallAudioRouteAdapter.dump(pw);
657         pw.decreaseIndent();
658 
659         pw.println("BluetoothDeviceManager:");
660         pw.increaseIndent();
661         if (mBluetoothStateReceiver.getBluetoothDeviceManager() != null) {
662             mBluetoothStateReceiver.getBluetoothDeviceManager().dump(pw);
663         }
664         pw.decreaseIndent();
665     }
666 
667     @VisibleForTesting
setIsTonePlaying(Call call, boolean isTonePlaying)668     public void setIsTonePlaying(Call call, boolean isTonePlaying) {
669         Log.i(this, "setIsTonePlaying; isTonePlaying=%b", isTonePlaying);
670         mIsTonePlaying = isTonePlaying;
671         mCallAudioModeStateMachine.sendMessageWithArgs(
672                 isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING
673                         : CallAudioModeStateMachine.TONE_STOPPED_PLAYING,
674                 makeArgsForModeStateMachine());
675 
676         if (!isTonePlaying && mIsDisconnectedTonePlaying) {
677             mCallsManager.onDisconnectedTonePlaying(call, false);
678             mIsDisconnectedTonePlaying = false;
679         }
680     }
681 
onCallLeavingState(Call call, int state)682     private void onCallLeavingState(Call call, int state) {
683         switch (state) {
684             case CallState.ACTIVE:
685             case CallState.CONNECTING:
686                 onCallLeavingActiveDialingOrConnecting();
687                 break;
688             case CallState.RINGING:
689             case CallState.SIMULATED_RINGING:
690             case CallState.ANSWERED:
691                 onCallLeavingRinging();
692                 break;
693             case CallState.ON_HOLD:
694                 onCallLeavingHold();
695                 break;
696             case CallState.PULLING:
697                 onCallLeavingActiveDialingOrConnecting();
698                 break;
699             case CallState.DIALING:
700                 stopRingbackForCall(call);
701                 onCallLeavingActiveDialingOrConnecting();
702                 break;
703             case CallState.AUDIO_PROCESSING:
704                 onCallLeavingAudioProcessing();
705                 break;
706         }
707     }
708 
onCallEnteringState(Call call, int state)709     private void onCallEnteringState(Call call, int state) {
710         switch (state) {
711             case CallState.ACTIVE:
712             case CallState.CONNECTING:
713                 onCallEnteringActiveDialingOrConnecting();
714                 break;
715             case CallState.RINGING:
716             case CallState.SIMULATED_RINGING:
717                 onCallEnteringRinging();
718                 break;
719             case CallState.ON_HOLD:
720                 onCallEnteringHold();
721                 break;
722             case CallState.PULLING:
723                 onCallEnteringActiveDialingOrConnecting();
724                 break;
725             case CallState.DIALING:
726                 onCallEnteringActiveDialingOrConnecting();
727                 playRingbackForCall(call);
728                 break;
729             case CallState.ANSWERED:
730                 if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
731                     onCallEnteringActiveDialingOrConnecting();
732                 }
733                 break;
734             case CallState.AUDIO_PROCESSING:
735                 onCallEnteringAudioProcessing();
736                 break;
737         }
738     }
739 
onCallLeavingAudioProcessing()740     private void onCallLeavingAudioProcessing() {
741         if (mAudioProcessingCalls.size() == 0) {
742             mCallAudioModeStateMachine.sendMessageWithArgs(
743                     CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS,
744                     makeArgsForModeStateMachine());
745         }
746     }
747 
onCallEnteringAudioProcessing()748     private void onCallEnteringAudioProcessing() {
749         if (mAudioProcessingCalls.size() == 1) {
750             mCallAudioModeStateMachine.sendMessageWithArgs(
751                     CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL,
752                     makeArgsForModeStateMachine());
753         }
754     }
755 
onCallLeavingActiveDialingOrConnecting()756     private void onCallLeavingActiveDialingOrConnecting() {
757         if (mActiveDialingOrConnectingCalls.size() == 0) {
758             mCallAudioModeStateMachine.sendMessageWithArgs(
759                     CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS,
760                     makeArgsForModeStateMachine());
761         }
762     }
763 
onCallLeavingRinging()764     private void onCallLeavingRinging() {
765         if (mRingingCalls.size() == 0) {
766             mCallAudioModeStateMachine.sendMessageWithArgs(
767                     CallAudioModeStateMachine.NO_MORE_RINGING_CALLS,
768                     makeArgsForModeStateMachine());
769         }
770     }
771 
onCallLeavingHold()772     private void onCallLeavingHold() {
773         if (mHoldingCalls.size() == 0) {
774             mCallAudioModeStateMachine.sendMessageWithArgs(
775                     CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS,
776                     makeArgsForModeStateMachine());
777         }
778     }
779 
onCallEnteringActiveDialingOrConnecting()780     private void onCallEnteringActiveDialingOrConnecting() {
781         if (mActiveDialingOrConnectingCalls.size() == 1) {
782             mCallAudioModeStateMachine.sendMessageWithArgs(
783                     CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL,
784                     makeArgsForModeStateMachine());
785         }
786     }
787 
onCallEnteringRinging()788     private void onCallEnteringRinging() {
789         if (mRingingCalls.size() == 1) {
790             Log.i(this, "onCallEnteringRinging: mFeatureFlags.separatelyBindToBtIncallService() ? %s",
791                     mFeatureFlags.separatelyBindToBtIncallService());
792             Log.i(this, "onCallEnteringRinging: mRingingCalls.getFirst().getBtIcsFuture() = %s",
793                     mRingingCalls.getFirst().getBtIcsFuture());
794             if (mFeatureFlags.separatelyBindToBtIncallService()
795                     && mRingingCalls.getFirst().getBtIcsFuture() != null) {
796                 mCallRingingFuture  = mRingingCalls.getFirst().getBtIcsFuture()
797                         .thenComposeAsync((completed) -> {
798                             mCallAudioModeStateMachine.sendMessageWithArgs(
799                                     CallAudioModeStateMachine.NEW_RINGING_CALL,
800                                     makeArgsForModeStateMachine());
801                             return CompletableFuture.completedFuture(completed);
802                         }, new LoggedHandlerExecutor(mHandler, "CAM.oCER", mCallsManager.getLock()))
803                         .exceptionally((throwable) -> {
804                             Log.e(this, throwable, "Error while executing BT ICS future");
805                             // Fallback on performing computation on a separate thread.
806                             handleBtBindingWaitFallback();
807                             return null;
808                         });
809             } else {
810                 mCallAudioModeStateMachine.sendMessageWithArgs(
811                         CallAudioModeStateMachine.NEW_RINGING_CALL,
812                         makeArgsForModeStateMachine());
813             }
814         }
815     }
816 
handleBtBindingWaitFallback()817     private void handleBtBindingWaitFallback() {
818         // Wait until the BT ICS binding completed to request further audio route change
819         mBtIcsBindingThread = new Thread(() -> {
820             mRingingCalls.getFirst().waitForBtIcs();
821             mCallAudioModeStateMachine.sendMessageWithArgs(
822                     CallAudioModeStateMachine.NEW_RINGING_CALL,
823                     makeArgsForModeStateMachine());
824         });
825         mBtIcsBindingThread.start();
826     }
827 
onCallEnteringHold()828     private void onCallEnteringHold() {
829         if (mHoldingCalls.size() == 1) {
830             mCallAudioModeStateMachine.sendMessageWithArgs(
831                     CallAudioModeStateMachine.NEW_HOLDING_CALL,
832                     makeArgsForModeStateMachine());
833         }
834     }
835 
updateForegroundCall()836     private void updateForegroundCall() {
837         Call oldForegroundCall = mForegroundCall;
838 
839         if (mActiveDialingOrConnectingCalls.size() > 0) {
840             // Give preference for connecting calls over active/dialing for foreground-ness.
841             Call possibleConnectingCall = null;
842             for (Call call : mActiveDialingOrConnectingCalls) {
843                 if (call.getState() == CallState.CONNECTING) {
844                     possibleConnectingCall = call;
845                 }
846             }
847             if (mFeatureFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
848                 // Prefer a connecting call
849                 if (possibleConnectingCall != null) {
850                     mForegroundCall = possibleConnectingCall;
851                 } else {
852                     // Next, prefer an active or dialing call which is not in the process of being
853                     // disconnected.
854                     mForegroundCall = mActiveDialingOrConnectingCalls
855                             .stream()
856                             .filter(c -> (c.getState() == CallState.ACTIVE
857                                     || c.getState() == CallState.DIALING)
858                                     && !c.isLocallyDisconnecting())
859                             .findFirst()
860                             // If we can't find one, then just fall back to the first one.
861                             .orElse(mActiveDialingOrConnectingCalls.iterator().next());
862                 }
863             } else {
864                 // Legacy (buggy) behavior.
865                 mForegroundCall = possibleConnectingCall == null ?
866                         mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall;
867             }
868         } else if (mRingingCalls.size() > 0) {
869             mForegroundCall = mRingingCalls.iterator().next();
870         } else if (mHoldingCalls.size() > 0) {
871             mForegroundCall = mHoldingCalls.iterator().next();
872         } else {
873             mForegroundCall = null;
874         }
875         Log.i(this, "updateForegroundCall; oldFg=%s, newFg=%s, aDC=%s, ring=%s, hold=%s",
876                 (oldForegroundCall == null ? "none" : oldForegroundCall.getId()),
877                 (mForegroundCall == null ? "none" : mForegroundCall.getId()),
878                 mActiveDialingOrConnectingCalls.stream().map(c -> c.getId()).collect(
879                         Collectors.joining(",")),
880                 mRingingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")),
881                 mHoldingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(","))
882         );
883         if (mForegroundCall != oldForegroundCall) {
884             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
885                     CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
886 
887             if (mForegroundCall != null
888                     && mFeatureFlags.ensureAudioModeUpdatesOnForegroundCallChange()) {
889                 // Ensure the voip audio mode for the new foreground call is taken into account.
890                 mCallAudioModeStateMachine.sendMessageWithArgs(
891                         CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE,
892                         makeArgsForModeStateMachine());
893             }
894             mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
895             maybePlayHoldTone(oldForegroundCall);
896         }
897     }
898 
899     @NonNull
makeArgsForModeStateMachine()900     private CallAudioModeStateMachine.MessageArgs makeArgsForModeStateMachine() {
901         return new Builder()
902                 .setHasActiveOrDialingCalls(mActiveDialingOrConnectingCalls.size() > 0)
903                 .setHasRingingCalls(mRingingCalls.size() > 0)
904                 .setHasHoldingCalls(mHoldingCalls.size() > 0)
905                 .setHasAudioProcessingCalls(mAudioProcessingCalls.size() > 0)
906                 .setIsTonePlaying(mIsTonePlaying)
907                 .setIsStreaming((mStreamingCall != null) && (!mStreamingCall.isDisconnected()))
908                 .setForegroundCallIsVoip(
909                         mForegroundCall != null && isCallVoip(mForegroundCall))
910                 .setSession(Log.createSubsession()).build();
911     }
912 
913     /**
914      * Determines if a {@link Call} is a VOIP call for audio purposes.
915      * For top level calls, we get this from {@link Call#getIsVoipAudioMode()}.  A {@link Call}
916      * representing a {@link android.telecom.Conference}, however, has no means of specifying that
917      * it is a VOIP conference, so we will get that attribute from one of the children.
918      * @param call The call.
919      * @return {@code true} if the call is a VOIP call, {@code false} if is a SIM call.
920      */
921     @VisibleForTesting
isCallVoip(Call call)922     public boolean isCallVoip(Call call) {
923         if (call.isConference() && call.getChildCalls() != null
924                 && call.getChildCalls().size() > 0 ) {
925             // If this is a conference with children, we can get the VOIP audio mode attribute from
926             // one of the children.  The Conference doesn't have a VOIP audio mode property, so we
927             // need to infer from the first child.
928             Call firstChild = call.getChildCalls().get(0);
929             return firstChild.getIsVoipAudioMode();
930         }
931         return call.getIsVoipAudioMode();
932     }
933 
getBinForCall(Call call)934     private HashSet<Call> getBinForCall(Call call) {
935         if (call.getState() == CallState.ANSWERED) {
936             // If the call has the speed-up-mt-audio capability, treat answered state as active
937             // for audio purposes.
938             if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
939                 return mActiveDialingOrConnectingCalls;
940             }
941             return mRingingCalls;
942         }
943         return mCallStateToCalls.get(call.getState());
944     }
945 
removeCallFromAllBins(Call call)946     private void removeCallFromAllBins(Call call) {
947         for (int i = 0; i < mCallStateToCalls.size(); i++) {
948             mCallStateToCalls.valueAt(i).remove(call);
949         }
950     }
951 
playToneForDisconnectedCall(Call call)952     private void playToneForDisconnectedCall(Call call) {
953         // If this call is being disconnected as a result of being handed over to another call,
954         // we will not play a disconnect tone.
955         if (call.isHandoverInProgress()) {
956             Log.i(LOG_TAG, "Omitting tone because %s is being handed over.", call);
957             completeDisconnectToneFuture(call);
958             return;
959         }
960 
961         if (mForegroundCall != null && call != mForegroundCall && mCalls.size() > 1) {
962             Log.v(LOG_TAG, "Omitting tone because we are not foreground" +
963                     " and there is another call.");
964             completeDisconnectToneFuture(call);
965             return;
966         }
967 
968         if (call.getDisconnectCause() != null) {
969             int toneToPlay = InCallTonePlayer.TONE_INVALID;
970 
971             Log.v(this, "Disconnect cause: %s.", call.getDisconnectCause());
972 
973             switch(call.getDisconnectCause().getTone()) {
974                 case ToneGenerator.TONE_SUP_BUSY:
975                     toneToPlay = InCallTonePlayer.TONE_BUSY;
976                     break;
977                 case ToneGenerator.TONE_SUP_CONGESTION:
978                     toneToPlay = InCallTonePlayer.TONE_CONGESTION;
979                     break;
980                 case ToneGenerator.TONE_CDMA_REORDER:
981                     toneToPlay = InCallTonePlayer.TONE_REORDER;
982                     break;
983                 case ToneGenerator.TONE_CDMA_ABBR_INTERCEPT:
984                     toneToPlay = InCallTonePlayer.TONE_INTERCEPT;
985                     break;
986                 case ToneGenerator.TONE_CDMA_CALLDROP_LITE:
987                     toneToPlay = InCallTonePlayer.TONE_CDMA_DROP;
988                     break;
989                 case ToneGenerator.TONE_SUP_ERROR:
990                     toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER;
991                     break;
992                 case ToneGenerator.TONE_PROP_PROMPT:
993                     toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
994                     break;
995             }
996 
997             Log.d(this, "Found a disconnected call with tone to play %d.", toneToPlay);
998 
999             if (toneToPlay != InCallTonePlayer.TONE_INVALID) {
1000                 boolean didToneStart = mPlayerFactory.createPlayer(call, toneToPlay).startTone();
1001                 if (didToneStart) {
1002                     mCallsManager.onDisconnectedTonePlaying(call, true);
1003                     mIsDisconnectedTonePlaying = true;
1004                 }
1005             } else {
1006                 completeDisconnectToneFuture(call);
1007             }
1008         }
1009     }
1010 
playRingbackForCall(Call call)1011     private void playRingbackForCall(Call call) {
1012         if (call == mForegroundCall && call.isRingbackRequested()) {
1013             mRingbackPlayer.startRingbackForCall(call);
1014         }
1015     }
1016 
stopRingbackForCall(Call call)1017     private void stopRingbackForCall(Call call) {
1018         mRingbackPlayer.stopRingbackForCall(call);
1019     }
1020 
1021     /**
1022      * Determines if a hold tone should be played and then starts or stops it accordingly.
1023      */
maybePlayHoldTone(Call call)1024     private void maybePlayHoldTone(Call call) {
1025         if (shouldPlayHoldTone()) {
1026             if (mHoldTonePlayer == null) {
1027                 mHoldTonePlayer = mPlayerFactory.createPlayer(call,
1028                         InCallTonePlayer.TONE_CALL_WAITING);
1029                 mHoldTonePlayer.startTone();
1030             }
1031         } else {
1032             if (mHoldTonePlayer != null) {
1033                 mHoldTonePlayer.stopTone();
1034                 mHoldTonePlayer = null;
1035             }
1036         }
1037     }
1038 
1039     /**
1040      * Determines if a hold tone should be played.
1041      * A hold tone should be played only if foreground call is equals with call which is
1042      * remotely held.
1043      *
1044      * @return {@code true} if the the hold tone should be played, {@code false} otherwise.
1045      */
shouldPlayHoldTone()1046     private boolean shouldPlayHoldTone() {
1047         Call foregroundCall = getForegroundCall();
1048         // If there is no foreground call, no hold tone should play.
1049         if (foregroundCall == null) {
1050             return false;
1051         }
1052 
1053         // If another call is ringing, no hold tone should play.
1054         if (mCallsManager.hasRingingCall()) {
1055             return false;
1056         }
1057 
1058         // If the foreground call isn't active, no hold tone should play. This might happen, for
1059         // example, if the user puts a remotely held call on hold itself.
1060         if (!foregroundCall.isActive()) {
1061             return false;
1062         }
1063 
1064         return foregroundCall.isRemotelyHeld();
1065     }
1066 
dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls)1067     private void dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls) {
1068         for (Call call : calls) {
1069             if (call != null) pw.println(call.getId());
1070         }
1071     }
1072 
maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call)1073     private void maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call) {
1074         // Check to see if the call being answered/rejected is the only ringing call, since this
1075         // will be called before the connection service acknowledges the state change.
1076         synchronized (mCallsManager.getLock()) {
1077             if (mRingingCalls.size() == 0 ||
1078                     (mRingingCalls.size() == 1 && call == mRingingCalls.iterator().next())) {
1079                 mRinger.stopRinging();
1080                 mRinger.stopCallWaiting();
1081             }
1082         }
1083     }
1084 
shouldPlayDisconnectTone(int oldState, int newState)1085     private boolean shouldPlayDisconnectTone(int oldState, int newState) {
1086         if (newState != CallState.DISCONNECTED) {
1087             return false;
1088         }
1089         return oldState == CallState.ACTIVE ||
1090                 oldState == CallState.DIALING ||
1091                 oldState == CallState.ON_HOLD;
1092     }
1093 
completeDisconnectToneFuture(Call call)1094     private void completeDisconnectToneFuture(Call call) {
1095         CompletableFuture<Void> disconnectedToneFuture = mCallsManager.getInCallController()
1096                 .getDisconnectedToneBtFutures().get(call.getId());
1097         if (disconnectedToneFuture != null) {
1098             Log.i(this,
1099                     "completeDisconnectToneFuture: completing deferred disconnect tone future for"
1100                             + " call %s",
1101                     call.getId());
1102             disconnectedToneFuture.complete(null);
1103         }
1104         // Make sure we schedule the unbinding of the BT ICS once the disconnected tone future has
1105         // been completed.
1106         mCallsManager.getInCallController().maybeScheduleBtUnbind(call);
1107     }
1108 
1109     @VisibleForTesting
getTrackedCalls()1110     public Set<Call> getTrackedCalls() {
1111         return mCalls;
1112     }
1113 
1114     @VisibleForTesting
getCallStateToCalls()1115     public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() {
1116         return mCallStateToCalls;
1117     }
1118 
1119     @VisibleForTesting
getCallRingingFuture()1120     public CompletableFuture<Boolean> getCallRingingFuture() {
1121         return mCallRingingFuture;
1122     }
1123 }
1124