blob: 56f8db9207d81a8019d20207754e394752f21ce3 [file] [log] [blame]
Sailesh Nepal810735e2014-03-18 18:15:46 -07001/*
Hall Liue091ab92015-12-18 17:05:30 -08002 * Copyright (C) 2015 The Android Open Source Project
Sailesh Nepal810735e2014-03-18 18:15:46 -07003 *
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
Hall Liue091ab92015-12-18 17:05:30 -080014 * limitations under the License
Sailesh Nepal810735e2014-03-18 18:15:46 -070015 */
16
Tyler Gunn7cc70b42014-09-12 22:17:27 -070017package com.android.server.telecom;
Sailesh Nepal810735e2014-03-18 18:15:46 -070018
Hall Liue091ab92015-12-18 17:05:30 -080019import android.annotation.NonNull;
Santos Cordon7686cd12015-10-05 15:20:55 -070020import android.media.IAudioService;
Hall Liue091ab92015-12-18 17:05:30 -080021import android.media.ToneGenerator;
Yorke Lee2a66f7b2015-05-13 14:21:19 -070022import android.telecom.CallAudioState;
Brad Ebinger953e1af2016-10-05 15:45:22 -070023import android.telecom.Log;
Hall Liue091ab92015-12-18 17:05:30 -080024import android.telecom.VideoProfile;
25import android.util.SparseArray;
Sailesh Nepal810735e2014-03-18 18:15:46 -070026
Brad Ebingerd931a012015-10-21 12:54:08 -070027import com.android.internal.annotations.VisibleForTesting;
Tyler Gunn9787e0e2014-10-14 14:36:12 -070028import com.android.internal.util.IndentingPrintWriter;
Hall Liu784a4962018-03-06 11:03:17 -080029import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
Santos Cordon1ae2b852014-03-19 03:03:10 -070030
Hall Liue091ab92015-12-18 17:05:30 -080031import java.util.Collection;
32import java.util.HashSet;
33import java.util.Set;
34import java.util.LinkedHashSet;
35
Hall Liuf62630a2015-10-27 14:53:39 -070036public class CallAudioManager extends CallsManagerListenerBase {
Ihab Awadb60f0062015-05-26 16:20:32 -070037
Hall Liu8fb1fb72015-10-22 15:24:40 -070038 public interface AudioServiceFactory {
39 IAudioService getAudioService();
40 }
41
Hall Liue091ab92015-12-18 17:05:30 -080042 private final String LOG_TAG = CallAudioManager.class.getSimpleName();
43
Hall Liu59c77e62016-03-10 18:03:45 -080044 private final LinkedHashSet<Call> mActiveDialingOrConnectingCalls;
Hall Liue091ab92015-12-18 17:05:30 -080045 private final LinkedHashSet<Call> mRingingCalls;
46 private final LinkedHashSet<Call> mHoldingCalls;
47 private final Set<Call> mCalls;
48 private final SparseArray<LinkedHashSet<Call>> mCallStateToCalls;
49
Hall Liuf62630a2015-10-27 14:53:39 -070050 private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
Hall Liue091ab92015-12-18 17:05:30 -080051 private final CallAudioModeStateMachine mCallAudioModeStateMachine;
Hall Liu784a4962018-03-06 11:03:17 -080052 private final BluetoothStateReceiver mBluetoothStateReceiver;
Hall Liue091ab92015-12-18 17:05:30 -080053 private final CallsManager mCallsManager;
54 private final InCallTonePlayer.Factory mPlayerFactory;
55 private final Ringer mRinger;
56 private final RingbackPlayer mRingbackPlayer;
57 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
Santos Cordondeb8c892014-05-30 01:38:03 -070058
Hall Liue091ab92015-12-18 17:05:30 -080059 private Call mForegroundCall;
60 private boolean mIsTonePlaying = false;
Honggangb3ccbb32016-05-31 14:46:22 +080061 private boolean mIsDisconnectedTonePlaying = false;
Tyler Gunndb821912016-02-16 14:35:25 -080062 private InCallTonePlayer mHoldTonePlayer;
Santos Cordon1ae2b852014-03-19 03:03:10 -070063
Hall Liue091ab92015-12-18 17:05:30 -080064 public CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine,
Hall Liu8fb1fb72015-10-22 15:24:40 -070065 CallsManager callsManager,
Hall Liue091ab92015-12-18 17:05:30 -080066 CallAudioModeStateMachine callAudioModeStateMachine,
67 InCallTonePlayer.Factory playerFactory,
68 Ringer ringer,
69 RingbackPlayer ringbackPlayer,
Hall Liu784a4962018-03-06 11:03:17 -080070 BluetoothStateReceiver bluetoothStateReceiver,
Hall Liue091ab92015-12-18 17:05:30 -080071 DtmfLocalTonePlayer dtmfLocalTonePlayer) {
Hall Liu59c77e62016-03-10 18:03:45 -080072 mActiveDialingOrConnectingCalls = new LinkedHashSet<>();
Hall Liue091ab92015-12-18 17:05:30 -080073 mRingingCalls = new LinkedHashSet<>();
74 mHoldingCalls = new LinkedHashSet<>();
75 mCalls = new HashSet<>();
76 mCallStateToCalls = new SparseArray<LinkedHashSet<Call>>() {{
Hall Liu59c77e62016-03-10 18:03:45 -080077 put(CallState.CONNECTING, mActiveDialingOrConnectingCalls);
78 put(CallState.ACTIVE, mActiveDialingOrConnectingCalls);
79 put(CallState.DIALING, mActiveDialingOrConnectingCalls);
Tyler Gunn1e37be52016-07-11 08:54:23 -070080 put(CallState.PULLING, mActiveDialingOrConnectingCalls);
Hall Liue091ab92015-12-18 17:05:30 -080081 put(CallState.RINGING, mRingingCalls);
82 put(CallState.ON_HOLD, mHoldingCalls);
83 }};
Hall Liuf62630a2015-10-27 14:53:39 -070084
85 mCallAudioRouteStateMachine = callAudioRouteStateMachine;
Hall Liue091ab92015-12-18 17:05:30 -080086 mCallAudioModeStateMachine = callAudioModeStateMachine;
87 mCallsManager = callsManager;
88 mPlayerFactory = playerFactory;
89 mRinger = ringer;
90 mRingbackPlayer = ringbackPlayer;
Hall Liu784a4962018-03-06 11:03:17 -080091 mBluetoothStateReceiver = bluetoothStateReceiver;
Hall Liue091ab92015-12-18 17:05:30 -080092 mDtmfLocalTonePlayer = dtmfLocalTonePlayer;
93
94 mPlayerFactory.setCallAudioManager(this);
95 mCallAudioModeStateMachine.setCallAudioManager(this);
Jack Hed0c6cab2018-02-13 18:33:57 -080096 mCallAudioRouteStateMachine.setCallAudioManager(this);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -070097 }
Santos Cordon1ae2b852014-03-19 03:03:10 -070098
Hall Liue091ab92015-12-18 17:05:30 -080099 @Override
100 public void onCallStateChanged(Call call, int oldState, int newState) {
Tyler Gunn4fe861d2016-03-29 19:33:56 -0700101 if (shouldIgnoreCallForAudio(call)) {
102 // No audio management for calls in a conference, or external calls.
Hall Liue091ab92015-12-18 17:05:30 -0800103 return;
104 }
105 Log.d(LOG_TAG, "Call state changed for TC@%s: %s -> %s", call.getId(),
106 CallState.toString(oldState), CallState.toString(newState));
Brad Ebinger72930a82015-11-23 10:06:40 -0800107
Hall Liue792b332016-04-19 14:23:09 -0700108 for (int i = 0; i < mCallStateToCalls.size(); i++) {
109 mCallStateToCalls.valueAt(i).remove(call);
Hall Liue091ab92015-12-18 17:05:30 -0800110 }
111 if (mCallStateToCalls.get(newState) != null) {
112 mCallStateToCalls.get(newState).add(call);
113 }
114
115 updateForegroundCall();
Hall Liu9029d1c2016-07-28 16:07:51 -0700116 if (shouldPlayDisconnectTone(oldState, newState)) {
Hall Liue091ab92015-12-18 17:05:30 -0800117 playToneForDisconnectedCall(call);
118 }
119
120 onCallLeavingState(call, oldState);
121 onCallEnteringState(call, newState);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700122 }
Santos Cordon1ae2b852014-03-19 03:03:10 -0700123
124 @Override
125 public void onCallAdded(Call call) {
Tyler Gunn4fe861d2016-03-29 19:33:56 -0700126 if (shouldIgnoreCallForAudio(call)) {
127 return; // Don't do audio handling for calls in a conference, or external calls.
Santos Cordon1ae2b852014-03-19 03:03:10 -0700128 }
Hall Liue091ab92015-12-18 17:05:30 -0800129
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700130 addCall(call);
131 }
132
133 @Override
134 public void onCallRemoved(Call call) {
135 if (shouldIgnoreCallForAudio(call)) {
136 return; // Don't do audio handling for calls in a conference, or external calls.
137 }
138
139 removeCall(call);
140 }
141
142 private void addCall(Call call) {
Hall Liue091ab92015-12-18 17:05:30 -0800143 if (mCalls.contains(call)) {
144 Log.w(LOG_TAG, "Call TC@%s is being added twice.", call.getId());
145 return; // No guarantees that the same call won't get added twice.
146 }
147
148 Log.d(LOG_TAG, "Call added with id TC@%s in state %s", call.getId(),
149 CallState.toString(call.getState()));
150
151 if (mCallStateToCalls.get(call.getState()) != null) {
152 mCallStateToCalls.get(call.getState()).add(call);
153 }
154 updateForegroundCall();
155 mCalls.add(call);
Hall Liu784a4962018-03-06 11:03:17 -0800156 if (mCalls.size() == 1) {
157 mBluetoothStateReceiver.setIsInCall(true);
158 }
Hall Liue091ab92015-12-18 17:05:30 -0800159
160 onCallEnteringState(call, call.getState());
Santos Cordon1ae2b852014-03-19 03:03:10 -0700161 }
162
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700163 private void removeCall(Call call) {
Hall Liue091ab92015-12-18 17:05:30 -0800164 if (!mCalls.contains(call)) {
165 return; // No guarantees that the same call won't get removed twice.
166 }
167
168 Log.d(LOG_TAG, "Call removed with id TC@%s in state %s", call.getId(),
169 CallState.toString(call.getState()));
170
Hall Liue792b332016-04-19 14:23:09 -0700171 for (int i = 0; i < mCallStateToCalls.size(); i++) {
172 mCallStateToCalls.valueAt(i).remove(call);
Hall Liue091ab92015-12-18 17:05:30 -0800173 }
174
175 updateForegroundCall();
176 mCalls.remove(call);
Hall Liu784a4962018-03-06 11:03:17 -0800177 if (mCalls.size() == 0) {
178 mBluetoothStateReceiver.setIsInCall(false);
179 }
Hall Liue091ab92015-12-18 17:05:30 -0800180
181 onCallLeavingState(call, call.getState());
Santos Cordon1ae2b852014-03-19 03:03:10 -0700182 }
183
Tyler Gunn4fe861d2016-03-29 19:33:56 -0700184 /**
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700185 * Handles changes to the external state of a call. External calls which become regular calls
186 * should be tracked, and regular calls which become external should no longer be tracked.
187 *
188 * @param call The call.
189 * @param isExternalCall {@code True} if the call is now external, {@code false} if it is now
190 * a regular call.
191 */
192 @Override
193 public void onExternalCallChanged(Call call, boolean isExternalCall) {
Tyler Gunnfe6d79e2016-07-11 16:08:38 -0700194 if (isExternalCall) {
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700195 Log.d(LOG_TAG, "Removing call which became external ID %s", call.getId());
196 removeCall(call);
197 } else if (!isExternalCall) {
198 Log.d(LOG_TAG, "Adding external call which was pulled with ID %s", call.getId());
199 addCall(call);
Tyler Gunnfe6d79e2016-07-11 16:08:38 -0700200
201 if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(call.getVideoState())) {
202 // When pulling a video call, automatically enable the speakerphone.
203 Log.d(LOG_TAG, "Switching to speaker because external video call %s was pulled." +
204 call.getId());
205 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
206 CallAudioRouteStateMachine.SWITCH_SPEAKER);
207 }
Tyler Gunn1a40c4f2016-04-14 14:29:45 -0700208 }
209 }
210
211 /**
Tyler Gunn4fe861d2016-03-29 19:33:56 -0700212 * Determines if {@link CallAudioManager} should do any audio routing operations for a call.
213 * We ignore child calls of a conference and external calls for audio routing purposes.
214 *
215 * @param call The call to check.
216 * @return {@code true} if the call should be ignored for audio routing, {@code false}
217 * otherwise
218 */
219 private boolean shouldIgnoreCallForAudio(Call call) {
220 return call.getParentCall() != null || call.isExternalCall();
221 }
222
Santos Cordon1ae2b852014-03-19 03:03:10 -0700223 @Override
224 public void onIncomingCallAnswered(Call call) {
Hall Liu8c7e2562016-04-13 18:26:03 -0700225 if (!mCalls.contains(call)) {
226 return;
227 }
228
Hall Liue091ab92015-12-18 17:05:30 -0800229 // This is called after the UI answers the call, but before the connection service
230 // sets the call to active. Only thing to handle for mode here is the audio speedup thing.
Santos Cordonc7e85d42014-05-22 02:51:48 -0700231
Dong Zhou3d4bafb2015-03-12 11:41:50 -0500232 if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
Hall Liue091ab92015-12-18 17:05:30 -0800233 if (mForegroundCall == call) {
234 Log.i(LOG_TAG, "Invoking the MT_AUDIO_SPEEDUP mechanism. Transitioning into " +
235 "an active in-call audio state before connection service has " +
236 "connected the call.");
237 if (mCallStateToCalls.get(call.getState()) != null) {
238 mCallStateToCalls.get(call.getState()).remove(call);
239 }
Hall Liu59c77e62016-03-10 18:03:45 -0800240 mActiveDialingOrConnectingCalls.add(call);
Hall Liue792b332016-04-19 14:23:09 -0700241 mCallAudioModeStateMachine.sendMessageWithArgs(
Hall Liue091ab92015-12-18 17:05:30 -0800242 CallAudioModeStateMachine.MT_AUDIO_SPEEDUP_FOR_RINGING_CALL,
243 makeArgsForModeStateMachine());
244 }
245 }
246
Hall Liu25921e82017-07-24 17:58:55 -0700247 // Turn off mute when a new incoming call is answered iff it's not a handover.
248 if (!call.isHandoverInProgress()) {
249 mute(false /* shouldMute */);
250 }
Tyler Gunn8e154db2016-11-10 13:47:22 -0800251
Hall Liu600b0992016-03-14 17:56:25 -0700252 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call);
Santos Cordon1ae2b852014-03-19 03:03:10 -0700253 }
254
255 @Override
Hall Liue091ab92015-12-18 17:05:30 -0800256 public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
257 if (videoProfile == null) {
258 return;
259 }
260
261 if (call != mForegroundCall) {
262 // We only play tones for foreground calls.
263 return;
264 }
265
266 int previousVideoState = call.getVideoState();
267 int newVideoState = videoProfile.getVideoState();
268 Log.v(this, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
269 .videoStateToString(newVideoState));
270
271 boolean isUpgradeRequest = !VideoProfile.isReceptionEnabled(previousVideoState) &&
272 VideoProfile.isReceptionEnabled(newVideoState);
273
274 if (isUpgradeRequest) {
275 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_VIDEO_UPGRADE).startTone();
276 }
Santos Cordon1ae2b852014-03-19 03:03:10 -0700277 }
278
Tyler Gunndb821912016-02-16 14:35:25 -0800279 /**
280 * Play or stop a call hold tone for a call. Triggered via
281 * {@link Connection#sendConnectionEvent(String)} when the
282 * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
283 * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
284 *
285 * @param call The call which requested the hold tone.
286 */
287 @Override
288 public void onHoldToneRequested(Call call) {
289 maybePlayHoldTone();
290 }
291
Sailesh Nepal7e669572014-07-08 21:29:12 -0700292 @Override
Andrew Lee5be64bc2014-09-08 18:35:15 -0700293 public void onIsVoipAudioModeChanged(Call call) {
Hall Liue091ab92015-12-18 17:05:30 -0800294 if (call != mForegroundCall) {
295 return;
296 }
Hall Liue792b332016-04-19 14:23:09 -0700297 mCallAudioModeStateMachine.sendMessageWithArgs(
Hall Liue091ab92015-12-18 17:05:30 -0800298 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE,
299 makeArgsForModeStateMachine());
300 }
301
302 @Override
303 public void onRingbackRequested(Call call, boolean shouldRingback) {
304 if (call == mForegroundCall && shouldRingback) {
305 mRingbackPlayer.startRingbackForCall(call);
306 } else {
307 mRingbackPlayer.stopRingbackForCall(call);
308 }
309 }
310
311 @Override
312 public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String message) {
Hall Liu600b0992016-03-14 17:56:25 -0700313 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call);
Hall Liue091ab92015-12-18 17:05:30 -0800314 }
315
316 @Override
317 public void onIsConferencedChanged(Call call) {
318 // This indicates a conferencing change, which shouldn't impact any audio mode stuff.
319 Call parentCall = call.getParentCall();
320 if (parentCall == null) {
321 // Indicates that the call should be tracked for audio purposes. Treat it as if it were
322 // just added.
323 Log.i(LOG_TAG, "Call TC@" + call.getId() + " left conference and will" +
324 " now be tracked by CallAudioManager.");
325 onCallAdded(call);
326 } else {
327 // The call joined a conference, so stop tracking it.
328 if (mCallStateToCalls.get(call.getState()) != null) {
329 mCallStateToCalls.get(call.getState()).remove(call);
330 }
331
332 updateForegroundCall();
333 mCalls.remove(call);
334 }
335 }
336
Hall Liufd33fef2016-04-19 14:23:09 -0700337 @Override
338 public void onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs,
339 ConnectionServiceWrapper newCs) {
340 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
341 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
342 }
343
Hall Liu9696c212016-06-24 16:09:02 -0700344 @Override
345 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {
346 if (call != getForegroundCall()) {
347 Log.d(LOG_TAG, "Ignoring video state change from %s to %s for call %s -- not " +
348 "foreground.", VideoProfile.videoStateToString(previousVideoState),
349 VideoProfile.videoStateToString(newVideoState), call.getId());
350 return;
351 }
352
353 if (!VideoProfile.isVideo(previousVideoState) &&
354 mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) {
355 Log.d(LOG_TAG, "Switching to speaker because call %s transitioned video state from %s" +
356 " to %s", call.getId(), VideoProfile.videoStateToString(previousVideoState),
357 VideoProfile.videoStateToString(newVideoState));
358 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
359 CallAudioRouteStateMachine.SWITCH_SPEAKER);
360 }
361 }
362
Hall Liue091ab92015-12-18 17:05:30 -0800363 public CallAudioState getCallAudioState() {
364 return mCallAudioRouteStateMachine.getCurrentCallAudioState();
365 }
366
Hall Liu2a3455b2016-02-22 17:09:00 -0800367 public Call getPossiblyHeldForegroundCall() {
368 return mForegroundCall;
369 }
370
Hall Liue091ab92015-12-18 17:05:30 -0800371 public Call getForegroundCall() {
372 if (mForegroundCall != null && mForegroundCall.getState() != CallState.ON_HOLD) {
373 return mForegroundCall;
374 }
375 return null;
Sailesh Nepal7e669572014-07-08 21:29:12 -0700376 }
377
Omata Shou6bc865c2017-07-26 13:39:27 +0900378 @VisibleForTesting
379 public void toggleMute() {
380 // Don't mute if there are any emergency calls.
381 if (mCallsManager.hasEmergencyCall()) {
382 Log.v(this, "ignoring toggleMute for emergency call");
383 return;
384 }
Brad Ebinger72930a82015-11-23 10:06:40 -0800385 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
386 CallAudioRouteStateMachine.TOGGLE_MUTE);
Santos Cordondeb8c892014-05-30 01:38:03 -0700387 }
388
Jack Hed0c6cab2018-02-13 18:33:57 -0800389 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
390 public void onRingerModeChange() {
Jack He1f3efbd2018-03-24 00:19:00 -0700391 mCallAudioModeStateMachine.sendMessageWithArgs(
392 CallAudioModeStateMachine.RINGER_MODE_CHANGE, makeArgsForModeStateMachine());
Jack Hed0c6cab2018-02-13 18:33:57 -0800393 }
394
Tyler Gunn8e154db2016-11-10 13:47:22 -0800395 @VisibleForTesting
396 public void mute(boolean shouldMute) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700397 Log.v(this, "mute, shouldMute: %b", shouldMute);
Santos Cordon1ae2b852014-03-19 03:03:10 -0700398
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700399 // Don't mute if there are any emergency calls.
Ihab Awad8de76912015-02-17 12:25:52 -0800400 if (mCallsManager.hasEmergencyCall()) {
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700401 shouldMute = false;
402 Log.v(this, "ignoring mute for emergency call");
Santos Cordon1ae2b852014-03-19 03:03:10 -0700403 }
404
Brad Ebinger72930a82015-11-23 10:06:40 -0800405 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(shouldMute
Hall Liuf62630a2015-10-27 14:53:39 -0700406 ? CallAudioRouteStateMachine.MUTE_ON : CallAudioRouteStateMachine.MUTE_OFF);
Sailesh Nepal810735e2014-03-18 18:15:46 -0700407 }
408
Santos Cordon1ae2b852014-03-19 03:03:10 -0700409 /**
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700410 * Changed the audio route, for example from earpiece to speaker phone.
Santos Cordon1ae2b852014-03-19 03:03:10 -0700411 *
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700412 * @param route The new audio route to use. See {@link CallAudioState}.
Hall Liu9086fb12017-11-07 18:01:53 -0800413 * @param bluetoothAddress the address of the desired bluetooth device, if route is
414 * {@link CallAudioState#ROUTE_BLUETOOTH}.
Santos Cordon1ae2b852014-03-19 03:03:10 -0700415 */
Hall Liu9086fb12017-11-07 18:01:53 -0800416 void setAudioRoute(int route, String bluetoothAddress) {
Yorke Lee2a66f7b2015-05-13 14:21:19 -0700417 Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
Hall Liuf62630a2015-10-27 14:53:39 -0700418 switch (route) {
419 case CallAudioState.ROUTE_BLUETOOTH:
Brad Ebinger72930a82015-11-23 10:06:40 -0800420 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
Hall Liu9086fb12017-11-07 18:01:53 -0800421 CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH, 0, bluetoothAddress);
Hall Liuf62630a2015-10-27 14:53:39 -0700422 return;
423 case CallAudioState.ROUTE_SPEAKER:
Brad Ebinger72930a82015-11-23 10:06:40 -0800424 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
Hall Liu4f296ba2016-02-18 14:46:57 -0800425 CallAudioRouteStateMachine.USER_SWITCH_SPEAKER);
Hall Liuf62630a2015-10-27 14:53:39 -0700426 return;
427 case CallAudioState.ROUTE_WIRED_HEADSET:
Brad Ebinger72930a82015-11-23 10:06:40 -0800428 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
Hall Liu4f296ba2016-02-18 14:46:57 -0800429 CallAudioRouteStateMachine.USER_SWITCH_HEADSET);
Hall Liuf62630a2015-10-27 14:53:39 -0700430 return;
431 case CallAudioState.ROUTE_EARPIECE:
Brad Ebinger72930a82015-11-23 10:06:40 -0800432 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
Hall Liu4f296ba2016-02-18 14:46:57 -0800433 CallAudioRouteStateMachine.USER_SWITCH_EARPIECE);
Hall Liuf62630a2015-10-27 14:53:39 -0700434 return;
435 case CallAudioState.ROUTE_WIRED_OR_EARPIECE:
Brad Ebinger72930a82015-11-23 10:06:40 -0800436 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
Hall Liu95f2db92017-06-19 18:32:55 -0700437 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
438 CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE);
Hall Liuf62630a2015-10-27 14:53:39 -0700439 return;
440 default:
441 Log.wtf(this, "Invalid route specified: %d", route);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700442 }
443 }
444
Tyler Gunn911d4de2017-12-19 08:11:35 -0800445 /**
446 * Switch call audio routing to the baseline route, including bluetooth headsets if there are
447 * any connected.
448 */
449 void switchBaseline() {
450 Log.i(this, "switchBaseline");
451 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
452 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE,
453 CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE);
454 }
455
Hall Liue091ab92015-12-18 17:05:30 -0800456 void silenceRingers() {
457 for (Call call : mRingingCalls) {
458 call.silence();
459 }
460
Hall Liue091ab92015-12-18 17:05:30 -0800461 mRinger.stopRinging();
462 mRinger.stopCallWaiting();
Hall Liue091ab92015-12-18 17:05:30 -0800463 }
464
Hall Liufbed0162016-01-14 18:06:29 -0800465 @VisibleForTesting
Hall Liu6a5995e2016-09-08 16:00:14 -0700466 public boolean startRinging() {
Hall Liu136f4c92017-04-04 13:35:18 -0700467 return mRinger.startRinging(mForegroundCall,
468 mCallAudioRouteStateMachine.isHfpDeviceAvailable());
Hall Liue091ab92015-12-18 17:05:30 -0800469 }
470
Hall Liufbed0162016-01-14 18:06:29 -0800471 @VisibleForTesting
472 public void startCallWaiting() {
xulicheng27437d92017-07-20 09:48:03 +0800473 if (mRingingCalls.size() == 1) {
474 mRinger.startCallWaiting(mRingingCalls.iterator().next());
475 }
Hall Liue091ab92015-12-18 17:05:30 -0800476 }
477
Hall Liufbed0162016-01-14 18:06:29 -0800478 @VisibleForTesting
479 public void stopRinging() {
Hall Liue091ab92015-12-18 17:05:30 -0800480 mRinger.stopRinging();
481 }
482
Hall Liufbed0162016-01-14 18:06:29 -0800483 @VisibleForTesting
484 public void stopCallWaiting() {
Hall Liue091ab92015-12-18 17:05:30 -0800485 mRinger.stopCallWaiting();
486 }
487
Hall Liufbed0162016-01-14 18:06:29 -0800488 @VisibleForTesting
489 public void setCallAudioRouteFocusState(int focusState) {
Hall Liue091ab92015-12-18 17:05:30 -0800490 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
491 CallAudioRouteStateMachine.SWITCH_FOCUS, focusState);
492 }
493
Hall Liu9ecbb1c2016-04-14 14:35:48 -0700494 @VisibleForTesting
495 public CallAudioRouteStateMachine getCallAudioRouteStateMachine() {
496 return mCallAudioRouteStateMachine;
497 }
498
Hall Liu609992b2016-08-31 15:48:51 -0700499 @VisibleForTesting
500 public CallAudioModeStateMachine getCallAudioModeStateMachine() {
501 return mCallAudioModeStateMachine;
502 }
503
Hall Liue091ab92015-12-18 17:05:30 -0800504 void dump(IndentingPrintWriter pw) {
Hall Liu8c7e2562016-04-13 18:26:03 -0700505 pw.println("All calls:");
506 pw.increaseIndent();
507 dumpCallsInCollection(pw, mCalls);
508 pw.decreaseIndent();
509
Hall Liu59c77e62016-03-10 18:03:45 -0800510 pw.println("Active dialing, or connecting calls:");
Hall Liue091ab92015-12-18 17:05:30 -0800511 pw.increaseIndent();
Hall Liu59c77e62016-03-10 18:03:45 -0800512 dumpCallsInCollection(pw, mActiveDialingOrConnectingCalls);
Hall Liue091ab92015-12-18 17:05:30 -0800513 pw.decreaseIndent();
514
515 pw.println("Ringing calls:");
516 pw.increaseIndent();
517 dumpCallsInCollection(pw, mRingingCalls);
518 pw.decreaseIndent();
519
520 pw.println("Holding calls:");
521 pw.increaseIndent();
522 dumpCallsInCollection(pw, mHoldingCalls);
523 pw.decreaseIndent();
524
525 pw.println("Foreground call:");
526 pw.println(mForegroundCall);
Hall Liua51c99f2017-03-13 16:39:26 -0700527
528 pw.println("CallAudioModeStateMachine pending messages:");
529 pw.increaseIndent();
530 mCallAudioModeStateMachine.dumpPendingMessages(pw);
531 pw.decreaseIndent();
532
533 pw.println("CallAudioRouteStateMachine pending messages:");
534 pw.increaseIndent();
535 mCallAudioRouteStateMachine.dumpPendingMessages(pw);
536 pw.decreaseIndent();
Hall Liue091ab92015-12-18 17:05:30 -0800537 }
538
Brad Ebingerd931a012015-10-21 12:54:08 -0700539 @VisibleForTesting
Hall Liue091ab92015-12-18 17:05:30 -0800540 public void setIsTonePlaying(boolean isTonePlaying) {
541 mIsTonePlaying = isTonePlaying;
Hall Liue792b332016-04-19 14:23:09 -0700542 mCallAudioModeStateMachine.sendMessageWithArgs(
Hall Liue091ab92015-12-18 17:05:30 -0800543 isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING
544 : CallAudioModeStateMachine.TONE_STOPPED_PLAYING,
545 makeArgsForModeStateMachine());
Honggangb3ccbb32016-05-31 14:46:22 +0800546
547 if (!isTonePlaying && mIsDisconnectedTonePlaying) {
548 mCallsManager.onDisconnectedTonePlaying(false);
549 mIsDisconnectedTonePlaying = false;
550 }
Hall Liue091ab92015-12-18 17:05:30 -0800551 }
552
553 private void onCallLeavingState(Call call, int state) {
554 switch (state) {
555 case CallState.ACTIVE:
Hall Liu59c77e62016-03-10 18:03:45 -0800556 case CallState.CONNECTING:
557 onCallLeavingActiveDialingOrConnecting();
Hall Liue091ab92015-12-18 17:05:30 -0800558 break;
559 case CallState.RINGING:
560 onCallLeavingRinging();
561 break;
562 case CallState.ON_HOLD:
563 onCallLeavingHold();
564 break;
Tyler Gunn1e37be52016-07-11 08:54:23 -0700565 case CallState.PULLING:
566 onCallLeavingActiveDialingOrConnecting();
567 break;
Hall Liue091ab92015-12-18 17:05:30 -0800568 case CallState.DIALING:
569 stopRingbackForCall(call);
Hall Liu59c77e62016-03-10 18:03:45 -0800570 onCallLeavingActiveDialingOrConnecting();
Tyler Gunn1e37be52016-07-11 08:54:23 -0700571 break;
Santos Cordon1ae2b852014-03-19 03:03:10 -0700572 }
Sailesh Nepal810735e2014-03-18 18:15:46 -0700573 }
574
Hall Liue091ab92015-12-18 17:05:30 -0800575 private void onCallEnteringState(Call call, int state) {
576 switch (state) {
577 case CallState.ACTIVE:
Hall Liu59c77e62016-03-10 18:03:45 -0800578 case CallState.CONNECTING:
579 onCallEnteringActiveDialingOrConnecting();
Hall Liue091ab92015-12-18 17:05:30 -0800580 break;
581 case CallState.RINGING:
582 onCallEnteringRinging();
583 break;
584 case CallState.ON_HOLD:
585 onCallEnteringHold();
586 break;
Tyler Gunn1e37be52016-07-11 08:54:23 -0700587 case CallState.PULLING:
588 onCallEnteringActiveDialingOrConnecting();
589 break;
Hall Liue091ab92015-12-18 17:05:30 -0800590 case CallState.DIALING:
Hall Liu59c77e62016-03-10 18:03:45 -0800591 onCallEnteringActiveDialingOrConnecting();
Hall Liue091ab92015-12-18 17:05:30 -0800592 playRingbackForCall(call);
593 break;
Santos Cordona56f2762014-03-24 15:55:53 -0700594 }
595 }
596
Hall Liu59c77e62016-03-10 18:03:45 -0800597 private void onCallLeavingActiveDialingOrConnecting() {
598 if (mActiveDialingOrConnectingCalls.size() == 0) {
Hall Liue792b332016-04-19 14:23:09 -0700599 mCallAudioModeStateMachine.sendMessageWithArgs(
Hall Liue091ab92015-12-18 17:05:30 -0800600 CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS,
601 makeArgsForModeStateMachine());
Dong Zhou3d4bafb2015-03-12 11:41:50 -0500602 }
Santos Cordon14ff8382014-08-05 20:44:27 -0700603 }
604
Hall Liue091ab92015-12-18 17:05:30 -0800605 private void onCallLeavingRinging() {
606 if (mRingingCalls.size() == 0) {
Hall Liue792b332016-04-19 14:23:09 -0700607 mCallAudioModeStateMachine.sendMessageWithArgs(
608 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS,
Hall Liue091ab92015-12-18 17:05:30 -0800609 makeArgsForModeStateMachine());
610 }
Tyler Gunn96703262015-06-23 14:36:55 -0700611 }
612
Hall Liue091ab92015-12-18 17:05:30 -0800613 private void onCallLeavingHold() {
614 if (mHoldingCalls.size() == 0) {
Hall Liue792b332016-04-19 14:23:09 -0700615 mCallAudioModeStateMachine.sendMessageWithArgs(
616 CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS,
Hall Liue091ab92015-12-18 17:05:30 -0800617 makeArgsForModeStateMachine());
618 }
619 }
Tyler Gunn96703262015-06-23 14:36:55 -0700620
Hall Liu59c77e62016-03-10 18:03:45 -0800621 private void onCallEnteringActiveDialingOrConnecting() {
622 if (mActiveDialingOrConnectingCalls.size() == 1) {
Hall Liue792b332016-04-19 14:23:09 -0700623 mCallAudioModeStateMachine.sendMessageWithArgs(
Hall Liue091ab92015-12-18 17:05:30 -0800624 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL,
625 makeArgsForModeStateMachine());
626 }
627 }
628
629 private void onCallEnteringRinging() {
630 if (mRingingCalls.size() == 1) {
Hall Liue792b332016-04-19 14:23:09 -0700631 mCallAudioModeStateMachine.sendMessageWithArgs(
632 CallAudioModeStateMachine.NEW_RINGING_CALL,
Hall Liue091ab92015-12-18 17:05:30 -0800633 makeArgsForModeStateMachine());
634 }
635 }
636
637 private void onCallEnteringHold() {
638 if (mHoldingCalls.size() == 1) {
Hall Liue792b332016-04-19 14:23:09 -0700639 mCallAudioModeStateMachine.sendMessageWithArgs(
640 CallAudioModeStateMachine.NEW_HOLDING_CALL,
Hall Liue091ab92015-12-18 17:05:30 -0800641 makeArgsForModeStateMachine());
642 }
643 }
644
645 private void updateForegroundCall() {
646 Call oldForegroundCall = mForegroundCall;
Hall Liu59c77e62016-03-10 18:03:45 -0800647 if (mActiveDialingOrConnectingCalls.size() > 0) {
648 // Give preference for connecting calls over active/dialing for foreground-ness.
649 Call possibleConnectingCall = null;
650 for (Call call : mActiveDialingOrConnectingCalls) {
651 if (call.getState() == CallState.CONNECTING) {
652 possibleConnectingCall = call;
653 }
654 }
655 mForegroundCall = possibleConnectingCall == null ?
656 mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall;
Hall Liue091ab92015-12-18 17:05:30 -0800657 } else if (mRingingCalls.size() > 0) {
658 mForegroundCall = mRingingCalls.iterator().next();
659 } else if (mHoldingCalls.size() > 0) {
660 mForegroundCall = mHoldingCalls.iterator().next();
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700661 } else {
Hall Liue091ab92015-12-18 17:05:30 -0800662 mForegroundCall = null;
663 }
664
665 if (mForegroundCall != oldForegroundCall) {
Hall Liu8c7e2562016-04-13 18:26:03 -0700666 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(
667 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
Hall Liue091ab92015-12-18 17:05:30 -0800668 mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
Tyler Gunndb821912016-02-16 14:35:25 -0800669 maybePlayHoldTone();
Hall Liue091ab92015-12-18 17:05:30 -0800670 }
671 }
672
673 @NonNull
674 private CallAudioModeStateMachine.MessageArgs makeArgsForModeStateMachine() {
675 return new CallAudioModeStateMachine.MessageArgs(
Hall Liu59c77e62016-03-10 18:03:45 -0800676 mActiveDialingOrConnectingCalls.size() > 0,
Hall Liue091ab92015-12-18 17:05:30 -0800677 mRingingCalls.size() > 0,
678 mHoldingCalls.size() > 0,
679 mIsTonePlaying,
680 mForegroundCall != null && mForegroundCall.getIsVoipAudioMode(),
681 Log.createSubsession());
682 }
683
684 private void playToneForDisconnectedCall(Call call) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700685 // If this call is being disconnected as a result of being handed over to another call,
686 // we will not play a disconnect tone.
Tyler Gunn6f6f1c52017-04-17 18:22:04 -0700687 if (call.isHandoverInProgress()) {
Tyler Gunn6f9ceb22017-04-06 08:47:01 -0700688 Log.i(LOG_TAG, "Omitting tone because %s is being handed over.", call);
689 return;
690 }
691
Hall Liue091ab92015-12-18 17:05:30 -0800692 if (mForegroundCall != null && call != mForegroundCall && mCalls.size() > 1) {
693 Log.v(LOG_TAG, "Omitting tone because we are not foreground" +
694 " and there is another call.");
695 return;
696 }
697
698 if (call.getDisconnectCause() != null) {
699 int toneToPlay = InCallTonePlayer.TONE_INVALID;
700
701 Log.v(this, "Disconnect cause: %s.", call.getDisconnectCause());
702
703 switch(call.getDisconnectCause().getTone()) {
704 case ToneGenerator.TONE_SUP_BUSY:
705 toneToPlay = InCallTonePlayer.TONE_BUSY;
706 break;
707 case ToneGenerator.TONE_SUP_CONGESTION:
708 toneToPlay = InCallTonePlayer.TONE_CONGESTION;
709 break;
710 case ToneGenerator.TONE_CDMA_REORDER:
711 toneToPlay = InCallTonePlayer.TONE_REORDER;
712 break;
713 case ToneGenerator.TONE_CDMA_ABBR_INTERCEPT:
714 toneToPlay = InCallTonePlayer.TONE_INTERCEPT;
715 break;
716 case ToneGenerator.TONE_CDMA_CALLDROP_LITE:
717 toneToPlay = InCallTonePlayer.TONE_CDMA_DROP;
718 break;
719 case ToneGenerator.TONE_SUP_ERROR:
720 toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER;
721 break;
722 case ToneGenerator.TONE_PROP_PROMPT:
723 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED;
724 break;
725 }
726
727 Log.d(this, "Found a disconnected call with tone to play %d.", toneToPlay);
728
729 if (toneToPlay != InCallTonePlayer.TONE_INVALID) {
730 mPlayerFactory.createPlayer(toneToPlay).startTone();
Honggangb3ccbb32016-05-31 14:46:22 +0800731 mCallsManager.onDisconnectedTonePlaying(true);
732 mIsDisconnectedTonePlaying = true;
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700733 }
734 }
735 }
736
Hall Liue091ab92015-12-18 17:05:30 -0800737 private void playRingbackForCall(Call call) {
738 if (call == mForegroundCall && call.isRingbackRequested()) {
739 mRingbackPlayer.startRingbackForCall(call);
Sailesh Nepal6aca10a2014-03-24 16:11:02 -0700740 }
Sailesh Nepal810735e2014-03-18 18:15:46 -0700741 }
Santos Cordon5ba7f272014-05-28 13:59:49 -0700742
Hall Liue091ab92015-12-18 17:05:30 -0800743 private void stopRingbackForCall(Call call) {
744 mRingbackPlayer.stopRingbackForCall(call);
745 }
Santos Cordon5ba7f272014-05-28 13:59:49 -0700746
Tyler Gunndb821912016-02-16 14:35:25 -0800747 /**
748 * Determines if a hold tone should be played and then starts or stops it accordingly.
749 */
750 private void maybePlayHoldTone() {
751 if (shouldPlayHoldTone()) {
752 if (mHoldTonePlayer == null) {
753 mHoldTonePlayer = mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING);
Hall Liub626c022016-06-23 13:16:51 -0700754 mHoldTonePlayer.startTone();
Tyler Gunndb821912016-02-16 14:35:25 -0800755 }
756 } else {
757 if (mHoldTonePlayer != null) {
758 mHoldTonePlayer.stopTone();
759 mHoldTonePlayer = null;
760 }
761 }
762 }
763
764 /**
765 * Determines if a hold tone should be played.
766 * A hold tone should be played only if foreground call is equals with call which is
767 * remotely held.
768 *
769 * @return {@code true} if the the hold tone should be played, {@code false} otherwise.
770 */
771 private boolean shouldPlayHoldTone() {
772 Call foregroundCall = getForegroundCall();
773 // If there is no foreground call, no hold tone should play.
774 if (foregroundCall == null) {
775 return false;
776 }
777
778 // If another call is ringing, no hold tone should play.
779 if (mCallsManager.hasRingingCall()) {
780 return false;
781 }
782
783 // If the foreground call isn't active, no hold tone should play. This might happen, for
784 // example, if the user puts a remotely held call on hold itself.
785 if (!foregroundCall.isActive()) {
786 return false;
787 }
788
789 return foregroundCall.isRemotelyHeld();
790 }
791
Hall Liue091ab92015-12-18 17:05:30 -0800792 private void dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls) {
793 for (Call call : calls) {
794 if (call != null) pw.println(call.getId());
Santos Cordon5ba7f272014-05-28 13:59:49 -0700795 }
Tyler Gunn9787e0e2014-10-14 14:36:12 -0700796 }
Hall Liu600b0992016-03-14 17:56:25 -0700797
798 private void maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call) {
799 // Check to see if the call being answered/rejected is the only ringing call, since this
800 // will be called before the connection service acknowledges the state change.
801 if (mRingingCalls.size() == 0 ||
802 (mRingingCalls.size() == 1 && call == mRingingCalls.iterator().next())) {
803 mRinger.stopRinging();
804 mRinger.stopCallWaiting();
805 }
806 }
Hall Liue792b332016-04-19 14:23:09 -0700807
Hall Liu9029d1c2016-07-28 16:07:51 -0700808 private boolean shouldPlayDisconnectTone(int oldState, int newState) {
809 if (newState != CallState.DISCONNECTED) {
810 return false;
811 }
812 return oldState == CallState.ACTIVE ||
813 oldState == CallState.DIALING ||
814 oldState == CallState.ON_HOLD;
815 }
816
Hall Liue792b332016-04-19 14:23:09 -0700817 @VisibleForTesting
818 public Set<Call> getTrackedCalls() {
819 return mCalls;
820 }
821
822 @VisibleForTesting
823 public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() {
824 return mCallStateToCalls;
825 }
Honggangb3ccbb32016-05-31 14:46:22 +0800826}