blob: f88475a60b5e62882433bae0dc0f404a7cf2ae86 [file] [log] [blame]
Hall Liuf62630a2015-10-27 14:53:39 -07001/*
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
17package com.android.server.telecom;
18
19
20import android.app.ActivityManagerNative;
Tyler Gunneaaf0742016-09-15 15:36:38 -070021import android.app.NotificationManager;
22import android.content.BroadcastReceiver;
Hall Liuf62630a2015-10-27 14:53:39 -070023import android.content.Context;
Tyler Gunneaaf0742016-09-15 15:36:38 -070024import android.content.Intent;
25import android.content.IntentFilter;
Hall Liuf62630a2015-10-27 14:53:39 -070026import android.content.pm.UserInfo;
27import android.media.AudioManager;
28import android.media.IAudioService;
29import android.os.Binder;
30import android.os.Message;
31import android.os.RemoteException;
Hall Liua3e9dda2015-12-15 17:52:50 -080032import android.os.SystemProperties;
Hall Liuf62630a2015-10-27 14:53:39 -070033import android.os.UserHandle;
34import android.telecom.CallAudioState;
Hall Liuef15aea2016-01-05 16:25:51 -080035import android.util.SparseArray;
Hall Liuf62630a2015-10-27 14:53:39 -070036
37import com.android.internal.util.IState;
38import com.android.internal.util.State;
39import com.android.internal.util.StateMachine;
40
41import java.util.HashMap;
42
43/**
44 * This class describes the available routes of a call as a state machine.
45 * Transitions are caused solely by the commands sent as messages. Possible values for msg.what
46 * are defined as event constants in this file.
47 *
48 * The eight states are all instances of the abstract base class, {@link AudioState}. Each state
49 * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and
50 * speakerphone) and audio focus status (active or quiescent).
51 *
52 * Messages are processed first by the processMessage method in the base class, AudioState.
53 * Any messages not completely handled by AudioState are further processed by the same method in
54 * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute},
55 * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at
56 * this level are then processed by the classes corresponding to the state instances themselves.
57 *
58 * There are several variables carrying additional state. These include:
59 * mAvailableRoutes: A bitmask describing which audio routes are available
60 * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting
61 * from a wired headset
62 * mIsMuted: a boolean indicating whether the audio is muted
63 */
64public class CallAudioRouteStateMachine extends StateMachine {
Tyler Gunn49056662017-01-10 16:18:46 -080065 private static final String TELECOM_PACKAGE =
66 CallAudioRouteStateMachine.class.getPackage().getName();
67
Hall Liuf62630a2015-10-27 14:53:39 -070068 /** Direct the audio stream through the device's earpiece. */
69 public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE;
70
71 /** Direct the audio stream through Bluetooth. */
72 public static final int ROUTE_BLUETOOTH = CallAudioState.ROUTE_BLUETOOTH;
73
74 /** Direct the audio stream through a wired headset. */
75 public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET;
76
77 /** Direct the audio stream through the device's speakerphone. */
78 public static final int ROUTE_SPEAKER = CallAudioState.ROUTE_SPEAKER;
79
80 /** Valid values for msg.what */
81 public static final int CONNECT_WIRED_HEADSET = 1;
82 public static final int DISCONNECT_WIRED_HEADSET = 2;
83 public static final int CONNECT_BLUETOOTH = 3;
84 public static final int DISCONNECT_BLUETOOTH = 4;
85 public static final int CONNECT_DOCK = 5;
86 public static final int DISCONNECT_DOCK = 6;
87
88 public static final int SWITCH_EARPIECE = 1001;
89 public static final int SWITCH_BLUETOOTH = 1002;
90 public static final int SWITCH_HEADSET = 1003;
91 public static final int SWITCH_SPEAKER = 1004;
Hall Liua3e9dda2015-12-15 17:52:50 -080092 // Wired headset, earpiece, or speakerphone, in that order of precedence.
93 public static final int SWITCH_BASELINE_ROUTE = 1005;
Hall Liue74af082016-02-10 16:12:47 -080094 public static final int BT_AUDIO_DISCONNECT = 1006;
Hall Liuf62630a2015-10-27 14:53:39 -070095
Hall Liu4f296ba2016-02-18 14:46:57 -080096 public static final int USER_SWITCH_EARPIECE = 1101;
97 public static final int USER_SWITCH_BLUETOOTH = 1102;
98 public static final int USER_SWITCH_HEADSET = 1103;
99 public static final int USER_SWITCH_SPEAKER = 1104;
100 public static final int USER_SWITCH_BASELINE_ROUTE = 1105;
101
Hall Liu8c7e2562016-04-13 18:26:03 -0700102 public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201;
103
Hall Liuf62630a2015-10-27 14:53:39 -0700104 public static final int MUTE_ON = 3001;
105 public static final int MUTE_OFF = 3002;
106 public static final int TOGGLE_MUTE = 3003;
107
108 public static final int SWITCH_FOCUS = 4001;
109
Hall Liu29855702015-12-11 17:42:03 -0800110 // Used in testing to execute verifications. Not compatible with subsessions.
111 public static final int RUN_RUNNABLE = 9001;
112
Hall Liuf62630a2015-10-27 14:53:39 -0700113 /** Valid values for mAudioFocusType */
114 public static final int NO_FOCUS = 1;
Hall Liue271f222016-08-15 18:04:06 -0700115 public static final int ACTIVE_FOCUS = 2;
116 public static final int RINGING_FOCUS = 3;
Hall Liuf62630a2015-10-27 14:53:39 -0700117
Hall Liu874c0f82016-04-29 18:13:18 -0700118 private static final SparseArray<String> AUDIO_ROUTE_TO_LOG_EVENT = new SparseArray<String>() {{
119 put(CallAudioState.ROUTE_BLUETOOTH, Log.Events.AUDIO_ROUTE_BT);
120 put(CallAudioState.ROUTE_EARPIECE, Log.Events.AUDIO_ROUTE_EARPIECE);
121 put(CallAudioState.ROUTE_SPEAKER, Log.Events.AUDIO_ROUTE_SPEAKER);
122 put(CallAudioState.ROUTE_WIRED_HEADSET, Log.Events.AUDIO_ROUTE_HEADSET);
123 }};
124
Hall Liuef15aea2016-01-05 16:25:51 -0800125 private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
126 put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET");
127 put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET");
128 put(CONNECT_BLUETOOTH, "CONNECT_BLUETOOTH");
129 put(DISCONNECT_BLUETOOTH, "DISCONNECT_BLUETOOTH");
130 put(CONNECT_DOCK, "CONNECT_DOCK");
131 put(DISCONNECT_DOCK, "DISCONNECT_DOCK");
132
133 put(SWITCH_EARPIECE, "SWITCH_EARPIECE");
134 put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH");
135 put(SWITCH_HEADSET, "SWITCH_HEADSET");
136 put(SWITCH_SPEAKER, "SWITCH_SPEAKER");
Hall Liua3e9dda2015-12-15 17:52:50 -0800137 put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE");
Hall Liue74af082016-02-10 16:12:47 -0800138 put(BT_AUDIO_DISCONNECT, "BT_AUDIO_DISCONNECT");
Hall Liuef15aea2016-01-05 16:25:51 -0800139
Hall Liu4f296ba2016-02-18 14:46:57 -0800140 put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE");
141 put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH");
142 put(USER_SWITCH_HEADSET, "USER_SWITCH_HEADSET");
143 put(USER_SWITCH_SPEAKER, "USER_SWITCH_SPEAKER");
144 put(USER_SWITCH_BASELINE_ROUTE, "USER_SWITCH_BASELINE_ROUTE");
145
Hall Liue271f222016-08-15 18:04:06 -0700146 put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE");
147
Hall Liuef15aea2016-01-05 16:25:51 -0800148 put(MUTE_ON, "MUTE_ON");
149 put(MUTE_OFF, "MUTE_OFF");
150 put(TOGGLE_MUTE, "TOGGLE_MUTE");
151
152 put(SWITCH_FOCUS, "SWITCH_FOCUS");
153
154 put(RUN_RUNNABLE, "RUN_RUNNABLE");
155 }};
156
Tyler Gunneaaf0742016-09-15 15:36:38 -0700157 /**
158 * BroadcastReceiver used to track changes in the notification interruption filter. This
159 * ensures changes to the notification interruption filter made by the user during a call are
160 * respected when restoring the notification interruption filter state.
161 */
162 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
163 @Override
164 public void onReceive(Context context, Intent intent) {
165 Log.startSession("CARSM.oR");
166 try {
167 String action = intent.getAction();
168
169 if (action.equals(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)) {
Tyler Gunn49056662017-01-10 16:18:46 -0800170 // We get an this broadcast any time the notification filter is changed, even if
171 // we are the initiator of the change.
172 // So, we'll look at who the initiator of the manual zen rule is in the
173 // notification manager. If its us, then we can just exit now.
174 String initiator =
175 mInterruptionFilterProxy.getInterruptionModeInitiator();
176
177 if (TELECOM_PACKAGE.equals(initiator)) {
178 // We are the initiator of this change, so ignore it.
179 Log.i(this, "interruptionFilterChanged - ignoring own change");
180 return;
181 }
182
Tyler Gunneaaf0742016-09-15 15:36:38 -0700183 if (mAreNotificationSuppressed) {
184 // If we've already set the interruption filter, and the user changes it to
185 // something other than INTERRUPTION_FILTER_ALARMS, assume we will no longer
186 // try to change it back if the audio route changes.
187 mAreNotificationSuppressed =
188 mInterruptionFilterProxy.getCurrentInterruptionFilter()
189 == NotificationManager.INTERRUPTION_FILTER_ALARMS;
Tyler Gunn49056662017-01-10 16:18:46 -0800190 Log.i(this, "interruptionFilterChanged - changing to %b",
191 mAreNotificationSuppressed);
Tyler Gunneaaf0742016-09-15 15:36:38 -0700192 }
193 }
194 } finally {
195 Log.endSession();
196 }
197 }
198 };
199
Hall Liuf62630a2015-10-27 14:53:39 -0700200 private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute";
201 private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute";
202 private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute";
203 private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute";
Hall Liue271f222016-08-15 18:04:06 -0700204 private static final String RINGING_BLUETOOTH_ROUTE_NAME = "RingingBluetoothRoute";
Hall Liuf62630a2015-10-27 14:53:39 -0700205 private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute";
206 private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute";
207 private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute";
208 private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute";
209
210 public static final String NAME = CallAudioRouteStateMachine.class.getName();
211
Brad Ebinger11623a32015-11-25 13:52:02 -0800212 @Override
213 protected void onPreHandleMessage(Message msg) {
214 if (msg.obj != null && msg.obj instanceof Session) {
Hall Liuef15aea2016-01-05 16:25:51 -0800215 String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown");
216 Log.continueSession((Session) msg.obj, "CARSM.pM_" + messageCodeName);
Hall Liue271f222016-08-15 18:04:06 -0700217 Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1);
Brad Ebinger11623a32015-11-25 13:52:02 -0800218 }
219 }
220
221 @Override
222 protected void onPostHandleMessage(Message msg) {
223 Log.endSession();
224 }
225
Hall Liuf62630a2015-10-27 14:53:39 -0700226 abstract class AudioState extends State {
227 @Override
228 public void enter() {
229 super.enter();
230 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
231 "Entering state " + getName());
232 }
233
234 @Override
235 public void exit() {
236 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
237 "Leaving state " + getName());
238 super.exit();
239 }
240
241 @Override
242 public boolean processMessage(Message msg) {
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800243 int addedRoutes = 0;
244 int removedRoutes = 0;
245
Hall Liuf62630a2015-10-27 14:53:39 -0700246 switch (msg.what) {
247 case CONNECT_WIRED_HEADSET:
248 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
249 "Wired headset connected");
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800250 removedRoutes |= ROUTE_EARPIECE;
251 addedRoutes |= ROUTE_WIRED_HEADSET;
252 break;
Hall Liuf62630a2015-10-27 14:53:39 -0700253 case CONNECT_BLUETOOTH:
Hall Liue74af082016-02-10 16:12:47 -0800254 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
255 "Bluetooth connected");
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800256 addedRoutes |= ROUTE_BLUETOOTH;
257 break;
Hall Liuf62630a2015-10-27 14:53:39 -0700258 case DISCONNECT_WIRED_HEADSET:
259 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
260 "Wired headset disconnected");
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800261 removedRoutes |= ROUTE_WIRED_HEADSET;
Hall Liua3e9dda2015-12-15 17:52:50 -0800262 if (mDoesDeviceSupportEarpieceRoute) {
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800263 addedRoutes |= ROUTE_EARPIECE;
Hall Liua3e9dda2015-12-15 17:52:50 -0800264 }
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800265 break;
Hall Liuf62630a2015-10-27 14:53:39 -0700266 case DISCONNECT_BLUETOOTH:
Hall Liue74af082016-02-10 16:12:47 -0800267 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
268 "Bluetooth disconnected");
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800269 removedRoutes |= ROUTE_BLUETOOTH;
270 break;
Hall Liua3e9dda2015-12-15 17:52:50 -0800271 case SWITCH_BASELINE_ROUTE:
Hall Liu4f296ba2016-02-18 14:46:57 -0800272 sendInternalMessage(calculateBaselineRouteMessage(false));
273 return HANDLED;
274 case USER_SWITCH_BASELINE_ROUTE:
275 sendInternalMessage(calculateBaselineRouteMessage(true));
Hall Liuf62630a2015-10-27 14:53:39 -0700276 return HANDLED;
Hall Liue271f222016-08-15 18:04:06 -0700277 case SWITCH_FOCUS:
278 mAudioFocusType = msg.arg1;
279 return NOT_HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -0700280 default:
281 return NOT_HANDLED;
282 }
Christine Hallstrom96a0be62016-11-30 16:05:13 -0800283
284 if (addedRoutes != 0 || removedRoutes != 0) {
285 mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true);
286 mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes,
287 addedRoutes, false);
288 }
289
290 return NOT_HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -0700291 }
292
293 // Behavior will depend on whether the state is an active one or a quiescent one.
294 abstract public void updateSystemAudioState();
295 abstract public boolean isActive();
296 }
297
298 class ActiveEarpieceRoute extends EarpieceRoute {
299 @Override
300 public String getName() {
301 return ACTIVE_EARPIECE_ROUTE_NAME;
302 }
303
304 @Override
305 public boolean isActive() {
306 return true;
307 }
308
309 @Override
310 public void enter() {
311 super.enter();
312 setSpeakerphoneOn(false);
313 setBluetoothOn(false);
Tyler Gunn00667fc2016-09-20 10:51:02 -0700314 if (mAudioFocusType == ACTIVE_FOCUS) {
315 setNotificationsSuppressed(true);
316 }
Hall Liuf62630a2015-10-27 14:53:39 -0700317 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE,
318 mAvailableRoutes);
Hall Liud109afb2016-01-26 12:53:53 -0800319 setSystemAudioState(newState);
Hall Liuf62630a2015-10-27 14:53:39 -0700320 updateInternalCallAudioState();
321 }
322
323 @Override
Tyler Gunneaaf0742016-09-15 15:36:38 -0700324 public void exit() {
325 super.exit();
326 setNotificationsSuppressed(false);
327 }
328
329 @Override
Hall Liuf62630a2015-10-27 14:53:39 -0700330 public void updateSystemAudioState() {
Hall Liuf62630a2015-10-27 14:53:39 -0700331 updateInternalCallAudioState();
Hall Liud109afb2016-01-26 12:53:53 -0800332 setSystemAudioState(mCurrentCallAudioState);
Hall Liuf62630a2015-10-27 14:53:39 -0700333 }
334
335 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800336 public boolean processMessage(Message msg) {
337 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700338 return HANDLED;
339 }
340 switch (msg.what) {
341 case SWITCH_EARPIECE:
Hall Liu4f296ba2016-02-18 14:46:57 -0800342 case USER_SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -0700343 // Nothing to do here
344 return HANDLED;
345 case SWITCH_BLUETOOTH:
Hall Liu4f296ba2016-02-18 14:46:57 -0800346 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700347 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
Hall Liue271f222016-08-15 18:04:06 -0700348 transitionTo(mAudioFocusType == ACTIVE_FOCUS ?
349 mActiveBluetoothRoute : mRingingBluetoothRoute);
Hall Liuf62630a2015-10-27 14:53:39 -0700350 } else {
351 Log.w(this, "Ignoring switch to bluetooth command. Not available.");
352 }
353 return HANDLED;
354 case SWITCH_HEADSET:
Hall Liu4f296ba2016-02-18 14:46:57 -0800355 case USER_SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -0700356 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
357 transitionTo(mActiveHeadsetRoute);
358 } else {
359 Log.w(this, "Ignoring switch to headset command. Not available.");
360 }
361 return HANDLED;
362 case SWITCH_SPEAKER:
Hall Liu4f296ba2016-02-18 14:46:57 -0800363 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -0700364 transitionTo(mActiveSpeakerRoute);
365 return HANDLED;
366 case SWITCH_FOCUS:
Tyler Gunn00667fc2016-09-20 10:51:02 -0700367 if (msg.arg1 == ACTIVE_FOCUS) {
368 setNotificationsSuppressed(true);
369 }
370
Hall Liuf62630a2015-10-27 14:53:39 -0700371 if (msg.arg1 == NO_FOCUS) {
Hall Liud8ca5452016-03-10 14:53:15 -0800372 reinitialize();
Hall Liuf62630a2015-10-27 14:53:39 -0700373 }
374 return HANDLED;
375 default:
376 return NOT_HANDLED;
377 }
378 }
379 }
380
381 class QuiescentEarpieceRoute extends EarpieceRoute {
382 @Override
383 public String getName() {
384 return QUIESCENT_EARPIECE_ROUTE_NAME;
385 }
386
387 @Override
388 public boolean isActive() {
389 return false;
390 }
391
392 @Override
393 public void enter() {
394 super.enter();
Hall Liu4f296ba2016-02-18 14:46:57 -0800395 mHasUserExplicitlyLeftBluetooth = false;
Hall Liuf62630a2015-10-27 14:53:39 -0700396 updateInternalCallAudioState();
397 }
398
399 @Override
400 public void updateSystemAudioState() {
401 updateInternalCallAudioState();
402 }
403
404 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800405 public boolean processMessage(Message msg) {
406 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700407 return HANDLED;
408 }
409 switch (msg.what) {
410 case SWITCH_EARPIECE:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700411 case USER_SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -0700412 // Nothing to do here
413 return HANDLED;
414 case SWITCH_BLUETOOTH:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700415 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700416 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
417 transitionTo(mQuiescentBluetoothRoute);
418 } else {
419 Log.w(this, "Ignoring switch to bluetooth command. Not available.");
420 }
421 return HANDLED;
422 case SWITCH_HEADSET:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700423 case USER_SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -0700424 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
425 transitionTo(mQuiescentHeadsetRoute);
426 } else {
427 Log.w(this, "Ignoring switch to headset command. Not available.");
428 }
429 return HANDLED;
430 case SWITCH_SPEAKER:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700431 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -0700432 transitionTo(mQuiescentSpeakerRoute);
433 return HANDLED;
434 case SWITCH_FOCUS:
Hall Liue271f222016-08-15 18:04:06 -0700435 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
Hall Liuf62630a2015-10-27 14:53:39 -0700436 transitionTo(mActiveEarpieceRoute);
437 }
438 return HANDLED;
439 default:
440 return NOT_HANDLED;
441 }
442 }
443 }
444
445 abstract class EarpieceRoute extends AudioState {
446 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800447 public boolean processMessage(Message msg) {
448 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700449 return HANDLED;
450 }
451 switch (msg.what) {
452 case CONNECT_WIRED_HEADSET:
453 sendInternalMessage(SWITCH_HEADSET);
454 return HANDLED;
455 case CONNECT_BLUETOOTH:
Hall Liu4f296ba2016-02-18 14:46:57 -0800456 if (!mHasUserExplicitlyLeftBluetooth) {
457 sendInternalMessage(SWITCH_BLUETOOTH);
458 } else {
Hall Liue74af082016-02-10 16:12:47 -0800459 Log.i(this, "Not switching to BT route from earpiece because user has " +
460 "explicitly disconnected.");
Hall Liu4f296ba2016-02-18 14:46:57 -0800461 updateSystemAudioState();
462 }
Hall Liuf62630a2015-10-27 14:53:39 -0700463 return HANDLED;
464 case DISCONNECT_BLUETOOTH:
465 updateSystemAudioState();
466 // No change in audio route required
467 return HANDLED;
468 case DISCONNECT_WIRED_HEADSET:
469 Log.e(this, new IllegalStateException(),
470 "Wired headset should not go from connected to not when on " +
471 "earpiece");
472 updateSystemAudioState();
473 return HANDLED;
Hall Liue74af082016-02-10 16:12:47 -0800474 case BT_AUDIO_DISCONNECT:
475 // This may be sent as a confirmation by the BT stack after switch off BT.
476 return HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -0700477 case CONNECT_DOCK:
478 sendInternalMessage(SWITCH_SPEAKER);
479 return HANDLED;
480 case DISCONNECT_DOCK:
481 // Nothing to do here
482 return HANDLED;
483 default:
484 return NOT_HANDLED;
485 }
486 }
487 }
488
489 class ActiveHeadsetRoute extends HeadsetRoute {
490 @Override
491 public String getName() {
492 return ACTIVE_HEADSET_ROUTE_NAME;
493 }
494
495 @Override
496 public boolean isActive() {
497 return true;
498 }
499
500 @Override
501 public void enter() {
502 super.enter();
503 setSpeakerphoneOn(false);
504 setBluetoothOn(false);
505 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET,
506 mAvailableRoutes);
Hall Liud109afb2016-01-26 12:53:53 -0800507 setSystemAudioState(newState);
Hall Liuf62630a2015-10-27 14:53:39 -0700508 updateInternalCallAudioState();
509 }
510
511 @Override
512 public void updateSystemAudioState() {
Hall Liuf62630a2015-10-27 14:53:39 -0700513 updateInternalCallAudioState();
Hall Liud109afb2016-01-26 12:53:53 -0800514 setSystemAudioState(mCurrentCallAudioState);
Hall Liuf62630a2015-10-27 14:53:39 -0700515 }
516
517 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800518 public boolean processMessage(Message msg) {
519 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700520 return HANDLED;
521 }
522 switch (msg.what) {
523 case SWITCH_EARPIECE:
Hall Liu4f296ba2016-02-18 14:46:57 -0800524 case USER_SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -0700525 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
526 transitionTo(mActiveEarpieceRoute);
527 } else {
528 Log.w(this, "Ignoring switch to earpiece command. Not available.");
529 }
530 return HANDLED;
531 case SWITCH_BLUETOOTH:
Hall Liu4f296ba2016-02-18 14:46:57 -0800532 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700533 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
Hall Liue271f222016-08-15 18:04:06 -0700534 transitionTo(mAudioFocusType == ACTIVE_FOCUS ?
535 mActiveBluetoothRoute : mRingingBluetoothRoute);
Hall Liuf62630a2015-10-27 14:53:39 -0700536 } else {
537 Log.w(this, "Ignoring switch to bluetooth command. Not available.");
538 }
539 return HANDLED;
540 case SWITCH_HEADSET:
Hall Liu4f296ba2016-02-18 14:46:57 -0800541 case USER_SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -0700542 // Nothing to do
543 return HANDLED;
544 case SWITCH_SPEAKER:
Hall Liu4f296ba2016-02-18 14:46:57 -0800545 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -0700546 transitionTo(mActiveSpeakerRoute);
547 return HANDLED;
548 case SWITCH_FOCUS:
549 if (msg.arg1 == NO_FOCUS) {
Hall Liud8ca5452016-03-10 14:53:15 -0800550 reinitialize();
Hall Liuf62630a2015-10-27 14:53:39 -0700551 }
552 return HANDLED;
553 default:
554 return NOT_HANDLED;
555 }
556 }
557 }
558
559 class QuiescentHeadsetRoute extends HeadsetRoute {
560 @Override
561 public String getName() {
562 return QUIESCENT_HEADSET_ROUTE_NAME;
563 }
564
565 @Override
566 public boolean isActive() {
567 return false;
568 }
569
570 @Override
571 public void enter() {
572 super.enter();
Hall Liu4f296ba2016-02-18 14:46:57 -0800573 mHasUserExplicitlyLeftBluetooth = false;
Hall Liuf62630a2015-10-27 14:53:39 -0700574 updateInternalCallAudioState();
575 }
576
577 @Override
578 public void updateSystemAudioState() {
579 updateInternalCallAudioState();
580 }
581
582 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800583 public boolean processMessage(Message msg) {
584 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700585 return HANDLED;
586 }
587 switch (msg.what) {
588 case SWITCH_EARPIECE:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700589 case USER_SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -0700590 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
591 transitionTo(mQuiescentEarpieceRoute);
592 } else {
593 Log.w(this, "Ignoring switch to earpiece command. Not available.");
594 }
595 return HANDLED;
596 case SWITCH_BLUETOOTH:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700597 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700598 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
599 transitionTo(mQuiescentBluetoothRoute);
600 } else {
601 Log.w(this, "Ignoring switch to bluetooth command. Not available.");
602 }
603 return HANDLED;
604 case SWITCH_HEADSET:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700605 case USER_SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -0700606 // Nothing to do
607 return HANDLED;
608 case SWITCH_SPEAKER:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700609 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -0700610 transitionTo(mQuiescentSpeakerRoute);
611 return HANDLED;
612 case SWITCH_FOCUS:
Hall Liue271f222016-08-15 18:04:06 -0700613 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
Hall Liuf62630a2015-10-27 14:53:39 -0700614 transitionTo(mActiveHeadsetRoute);
615 }
616 return HANDLED;
617 default:
618 return NOT_HANDLED;
619 }
620 }
621 }
622
623 abstract class HeadsetRoute extends AudioState {
624 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800625 public boolean processMessage(Message msg) {
626 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700627 return HANDLED;
628 }
629 switch (msg.what) {
630 case CONNECT_WIRED_HEADSET:
631 Log.e(this, new IllegalStateException(),
632 "Wired headset should already be connected.");
633 mAvailableRoutes |= ROUTE_WIRED_HEADSET;
634 updateSystemAudioState();
635 return HANDLED;
636 case CONNECT_BLUETOOTH:
Hall Liu4f296ba2016-02-18 14:46:57 -0800637 if (!mHasUserExplicitlyLeftBluetooth) {
638 sendInternalMessage(SWITCH_BLUETOOTH);
639 } else {
Hall Liue74af082016-02-10 16:12:47 -0800640 Log.i(this, "Not switching to BT route from headset because user has " +
641 "explicitly disconnected.");
Hall Liu4f296ba2016-02-18 14:46:57 -0800642 updateSystemAudioState();
643 }
Hall Liuf62630a2015-10-27 14:53:39 -0700644 return HANDLED;
645 case DISCONNECT_BLUETOOTH:
646 updateSystemAudioState();
647 // No change in audio route required
648 return HANDLED;
649 case DISCONNECT_WIRED_HEADSET:
650 if (mWasOnSpeaker) {
651 sendInternalMessage(SWITCH_SPEAKER);
652 } else {
Hall Liua3e9dda2015-12-15 17:52:50 -0800653 sendInternalMessage(SWITCH_BASELINE_ROUTE);
Hall Liuf62630a2015-10-27 14:53:39 -0700654 }
655 return HANDLED;
Hall Liue74af082016-02-10 16:12:47 -0800656 case BT_AUDIO_DISCONNECT:
657 // This may be sent as a confirmation by the BT stack after switch off BT.
658 return HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -0700659 case CONNECT_DOCK:
660 // Nothing to do here
661 return HANDLED;
662 case DISCONNECT_DOCK:
663 // Nothing to do here
664 return HANDLED;
665 default:
666 return NOT_HANDLED;
667 }
668 }
669 }
670
671 class ActiveBluetoothRoute extends BluetoothRoute {
672 @Override
673 public String getName() {
674 return ACTIVE_BLUETOOTH_ROUTE_NAME;
675 }
676
677 @Override
678 public boolean isActive() {
679 return true;
680 }
681
682 @Override
683 public void enter() {
684 super.enter();
685 setSpeakerphoneOn(false);
686 setBluetoothOn(true);
687 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
688 mAvailableRoutes);
Hall Liud109afb2016-01-26 12:53:53 -0800689 setSystemAudioState(newState);
Hall Liuf62630a2015-10-27 14:53:39 -0700690 updateInternalCallAudioState();
691 }
692
693 @Override
694 public void updateSystemAudioState() {
Hall Liuf62630a2015-10-27 14:53:39 -0700695 updateInternalCallAudioState();
Hall Liud109afb2016-01-26 12:53:53 -0800696 setSystemAudioState(mCurrentCallAudioState);
Hall Liuf62630a2015-10-27 14:53:39 -0700697 }
698
699 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800700 public boolean processMessage(Message msg) {
701 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700702 return HANDLED;
703 }
704 switch (msg.what) {
Hall Liu4f296ba2016-02-18 14:46:57 -0800705 case USER_SWITCH_EARPIECE:
706 mHasUserExplicitlyLeftBluetooth = true;
707 // fall through
Hall Liuf62630a2015-10-27 14:53:39 -0700708 case SWITCH_EARPIECE:
709 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
710 transitionTo(mActiveEarpieceRoute);
711 } else {
712 Log.w(this, "Ignoring switch to earpiece command. Not available.");
713 }
714 return HANDLED;
715 case SWITCH_BLUETOOTH:
Hall Liu4f296ba2016-02-18 14:46:57 -0800716 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700717 // Nothing to do
718 return HANDLED;
Hall Liu4f296ba2016-02-18 14:46:57 -0800719 case USER_SWITCH_HEADSET:
720 mHasUserExplicitlyLeftBluetooth = true;
721 // fall through
Hall Liuf62630a2015-10-27 14:53:39 -0700722 case SWITCH_HEADSET:
723 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
724 transitionTo(mActiveHeadsetRoute);
725 } else {
726 Log.w(this, "Ignoring switch to headset command. Not available.");
727 }
728 return HANDLED;
Hall Liu4f296ba2016-02-18 14:46:57 -0800729 case USER_SWITCH_SPEAKER:
730 mHasUserExplicitlyLeftBluetooth = true;
731 // fall through
Hall Liuf62630a2015-10-27 14:53:39 -0700732 case SWITCH_SPEAKER:
733 transitionTo(mActiveSpeakerRoute);
734 return HANDLED;
735 case SWITCH_FOCUS:
736 if (msg.arg1 == NO_FOCUS) {
Hall Liud8ca5452016-03-10 14:53:15 -0800737 reinitialize();
Hall Liue271f222016-08-15 18:04:06 -0700738 } else if (msg.arg1 == RINGING_FOCUS) {
739 transitionTo(mRingingBluetoothRoute);
Hall Liuf62630a2015-10-27 14:53:39 -0700740 }
741 return HANDLED;
Hall Liu147cc5f2016-04-25 14:29:19 -0700742 case BT_AUDIO_DISCONNECT:
743 sendInternalMessage(SWITCH_BASELINE_ROUTE);
744 return HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -0700745 default:
746 return NOT_HANDLED;
747 }
748 }
749 }
750
Hall Liue271f222016-08-15 18:04:06 -0700751 class RingingBluetoothRoute extends BluetoothRoute {
752 @Override
753 public String getName() {
754 return RINGING_BLUETOOTH_ROUTE_NAME;
755 }
756
757 @Override
758 public boolean isActive() {
759 return false;
760 }
761
762 @Override
763 public void enter() {
764 super.enter();
765 setSpeakerphoneOn(false);
766 // Do not enable SCO audio here, since RING is being sent to the headset.
767 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH,
768 mAvailableRoutes);
769 setSystemAudioState(newState);
770 updateInternalCallAudioState();
771 }
772
773 @Override
774 public void updateSystemAudioState() {
775 updateInternalCallAudioState();
776 setSystemAudioState(mCurrentCallAudioState);
777 }
778
779 @Override
780 public boolean processMessage(Message msg) {
781 if (super.processMessage(msg) == HANDLED) {
782 return HANDLED;
783 }
784 switch (msg.what) {
785 case USER_SWITCH_EARPIECE:
786 mHasUserExplicitlyLeftBluetooth = true;
787 // fall through
788 case SWITCH_EARPIECE:
789 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
790 transitionTo(mActiveEarpieceRoute);
791 } else {
792 Log.w(this, "Ignoring switch to earpiece command. Not available.");
793 }
794 return HANDLED;
795 case SWITCH_BLUETOOTH:
796 case USER_SWITCH_BLUETOOTH:
797 // Nothing to do
798 return HANDLED;
799 case USER_SWITCH_HEADSET:
800 mHasUserExplicitlyLeftBluetooth = true;
801 // fall through
802 case SWITCH_HEADSET:
803 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
804 transitionTo(mActiveHeadsetRoute);
805 } else {
806 Log.w(this, "Ignoring switch to headset command. Not available.");
807 }
808 return HANDLED;
809 case USER_SWITCH_SPEAKER:
810 mHasUserExplicitlyLeftBluetooth = true;
811 // fall through
812 case SWITCH_SPEAKER:
813 transitionTo(mActiveSpeakerRoute);
814 return HANDLED;
815 case SWITCH_FOCUS:
816 if (msg.arg1 == NO_FOCUS) {
817 reinitialize();
818 } else if (msg.arg1 == ACTIVE_FOCUS) {
819 transitionTo(mActiveBluetoothRoute);
820 }
821 return HANDLED;
822 case BT_AUDIO_DISCONNECT:
823 // Ignore BT_AUDIO_DISCONNECT when ringing, since SCO audio should not be
824 // connected.
825 return HANDLED;
826 default:
827 return NOT_HANDLED;
828 }
829 }
830 }
831
Hall Liuf62630a2015-10-27 14:53:39 -0700832 class QuiescentBluetoothRoute extends BluetoothRoute {
833 @Override
834 public String getName() {
835 return QUIESCENT_BLUETOOTH_ROUTE_NAME;
836 }
837
838 @Override
839 public boolean isActive() {
840 return false;
841 }
842
843 @Override
844 public void enter() {
845 super.enter();
Hall Liu4f296ba2016-02-18 14:46:57 -0800846 mHasUserExplicitlyLeftBluetooth = false;
Hall Liuf62630a2015-10-27 14:53:39 -0700847 updateInternalCallAudioState();
848 }
849
850 @Override
851 public void updateSystemAudioState() {
852 updateInternalCallAudioState();
853 }
854
855 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800856 public boolean processMessage(Message msg) {
857 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700858 return HANDLED;
859 }
860 switch (msg.what) {
861 case SWITCH_EARPIECE:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700862 case USER_SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -0700863 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
864 transitionTo(mQuiescentEarpieceRoute);
865 } else {
866 Log.w(this, "Ignoring switch to earpiece command. Not available.");
867 }
868 return HANDLED;
869 case SWITCH_BLUETOOTH:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700870 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700871 // Nothing to do
872 return HANDLED;
873 case SWITCH_HEADSET:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700874 case USER_SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -0700875 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
876 transitionTo(mQuiescentHeadsetRoute);
877 } else {
878 Log.w(this, "Ignoring switch to headset command. Not available.");
879 }
880 return HANDLED;
881 case SWITCH_SPEAKER:
Garik Badalyan967bdfe2016-05-05 16:01:21 -0700882 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -0700883 transitionTo(mQuiescentSpeakerRoute);
884 return HANDLED;
885 case SWITCH_FOCUS:
Hall Liue271f222016-08-15 18:04:06 -0700886 if (msg.arg1 == ACTIVE_FOCUS) {
Hall Liuf62630a2015-10-27 14:53:39 -0700887 transitionTo(mActiveBluetoothRoute);
Hall Liue271f222016-08-15 18:04:06 -0700888 } else if (msg.arg1 == RINGING_FOCUS) {
889 transitionTo(mRingingBluetoothRoute);
Hall Liuf62630a2015-10-27 14:53:39 -0700890 }
891 return HANDLED;
Hall Liu147cc5f2016-04-25 14:29:19 -0700892 case BT_AUDIO_DISCONNECT:
893 // Ignore this -- audio disconnecting while quiescent should not cause a
894 // route switch, since the device is still connected.
895 return HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -0700896 default:
897 return NOT_HANDLED;
898 }
899 }
900 }
901
902 abstract class BluetoothRoute extends AudioState {
903 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800904 public boolean processMessage(Message msg) {
905 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700906 return HANDLED;
907 }
908 switch (msg.what) {
909 case CONNECT_WIRED_HEADSET:
910 sendInternalMessage(SWITCH_HEADSET);
911 return HANDLED;
912 case CONNECT_BLUETOOTH:
913 // We can't tell when a change in bluetooth state corresponds to an
914 // actual connection or disconnection, so we'll just ignore it if we're already
915 // in the bluetooth route.
916 return HANDLED;
917 case DISCONNECT_BLUETOOTH:
Hall Liua3e9dda2015-12-15 17:52:50 -0800918 sendInternalMessage(SWITCH_BASELINE_ROUTE);
Hall Liuf62630a2015-10-27 14:53:39 -0700919 mWasOnSpeaker = false;
920 return HANDLED;
921 case DISCONNECT_WIRED_HEADSET:
922 updateSystemAudioState();
923 // No change in audio route required
924 return HANDLED;
925 case CONNECT_DOCK:
926 // Nothing to do here
927 return HANDLED;
928 case DISCONNECT_DOCK:
929 // Nothing to do here
930 return HANDLED;
931 default:
932 return NOT_HANDLED;
933 }
934 }
935 }
936
937 class ActiveSpeakerRoute extends SpeakerRoute {
938 @Override
939 public String getName() {
940 return ACTIVE_SPEAKER_ROUTE_NAME;
941 }
942
943 @Override
944 public boolean isActive() {
945 return true;
946 }
947
948 @Override
949 public void enter() {
950 super.enter();
951 mWasOnSpeaker = true;
952 setSpeakerphoneOn(true);
953 setBluetoothOn(false);
954 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER,
955 mAvailableRoutes);
Hall Liud109afb2016-01-26 12:53:53 -0800956 setSystemAudioState(newState);
Hall Liuf62630a2015-10-27 14:53:39 -0700957 updateInternalCallAudioState();
958 }
959
960 @Override
961 public void updateSystemAudioState() {
Hall Liuf62630a2015-10-27 14:53:39 -0700962 updateInternalCallAudioState();
Hall Liud109afb2016-01-26 12:53:53 -0800963 setSystemAudioState(mCurrentCallAudioState);
Hall Liuf62630a2015-10-27 14:53:39 -0700964 }
965
966 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -0800967 public boolean processMessage(Message msg) {
968 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -0700969 return HANDLED;
970 }
971 switch(msg.what) {
Hall Liu4f296ba2016-02-18 14:46:57 -0800972 case USER_SWITCH_EARPIECE:
Hall Liu04209a32016-05-02 14:47:06 -0700973 mWasOnSpeaker = false;
974 // fall through
975 case SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -0700976 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
977 transitionTo(mActiveEarpieceRoute);
978 } else {
979 Log.w(this, "Ignoring switch to earpiece command. Not available.");
980 }
981 return HANDLED;
Hall Liu4f296ba2016-02-18 14:46:57 -0800982 case USER_SWITCH_BLUETOOTH:
Hall Liu04209a32016-05-02 14:47:06 -0700983 mWasOnSpeaker = false;
984 // fall through
985 case SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -0700986 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
Hall Liue271f222016-08-15 18:04:06 -0700987 transitionTo(mAudioFocusType == ACTIVE_FOCUS ?
988 mActiveBluetoothRoute : mRingingBluetoothRoute);
Hall Liuf62630a2015-10-27 14:53:39 -0700989 } else {
990 Log.w(this, "Ignoring switch to bluetooth command. Not available.");
991 }
992 return HANDLED;
Hall Liu4f296ba2016-02-18 14:46:57 -0800993 case USER_SWITCH_HEADSET:
Hall Liu04209a32016-05-02 14:47:06 -0700994 mWasOnSpeaker = false;
995 // fall through
996 case SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -0700997 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
998 transitionTo(mActiveHeadsetRoute);
999 } else {
1000 Log.w(this, "Ignoring switch to headset command. Not available.");
1001 }
1002 return HANDLED;
1003 case SWITCH_SPEAKER:
Hall Liu4f296ba2016-02-18 14:46:57 -08001004 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -07001005 // Nothing to do
1006 return HANDLED;
1007 case SWITCH_FOCUS:
1008 if (msg.arg1 == NO_FOCUS) {
Hall Liud8ca5452016-03-10 14:53:15 -08001009 reinitialize();
Hall Liuf62630a2015-10-27 14:53:39 -07001010 }
1011 return HANDLED;
1012 default:
1013 return NOT_HANDLED;
1014 }
1015 }
1016 }
1017
1018 class QuiescentSpeakerRoute extends SpeakerRoute {
1019 @Override
1020 public String getName() {
1021 return QUIESCENT_SPEAKER_ROUTE_NAME;
1022 }
1023
1024 @Override
1025 public boolean isActive() {
1026 return false;
1027 }
1028
1029 @Override
1030 public void enter() {
1031 super.enter();
Hall Liu4f296ba2016-02-18 14:46:57 -08001032 mHasUserExplicitlyLeftBluetooth = false;
Hall Liuf62630a2015-10-27 14:53:39 -07001033 // Omit setting mWasOnSpeaker to true here, since this does not reflect a call
1034 // actually being on speakerphone.
1035 updateInternalCallAudioState();
1036 }
1037
1038 @Override
1039 public void updateSystemAudioState() {
1040 updateInternalCallAudioState();
1041 }
1042
1043 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -08001044 public boolean processMessage(Message msg) {
1045 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -07001046 return HANDLED;
1047 }
1048 switch(msg.what) {
1049 case SWITCH_EARPIECE:
Garik Badalyan967bdfe2016-05-05 16:01:21 -07001050 case USER_SWITCH_EARPIECE:
Hall Liuf62630a2015-10-27 14:53:39 -07001051 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
1052 transitionTo(mQuiescentEarpieceRoute);
1053 } else {
1054 Log.w(this, "Ignoring switch to earpiece command. Not available.");
1055 }
1056 return HANDLED;
1057 case SWITCH_BLUETOOTH:
Garik Badalyan967bdfe2016-05-05 16:01:21 -07001058 case USER_SWITCH_BLUETOOTH:
Hall Liuf62630a2015-10-27 14:53:39 -07001059 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) {
1060 transitionTo(mQuiescentBluetoothRoute);
1061 } else {
1062 Log.w(this, "Ignoring switch to bluetooth command. Not available.");
1063 }
1064 return HANDLED;
1065 case SWITCH_HEADSET:
Garik Badalyan967bdfe2016-05-05 16:01:21 -07001066 case USER_SWITCH_HEADSET:
Hall Liuf62630a2015-10-27 14:53:39 -07001067 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1068 transitionTo(mQuiescentHeadsetRoute);
1069 } else {
1070 Log.w(this, "Ignoring switch to headset command. Not available.");
1071 }
1072 return HANDLED;
1073 case SWITCH_SPEAKER:
Garik Badalyan967bdfe2016-05-05 16:01:21 -07001074 case USER_SWITCH_SPEAKER:
Hall Liuf62630a2015-10-27 14:53:39 -07001075 // Nothing to do
1076 return HANDLED;
1077 case SWITCH_FOCUS:
Hall Liue271f222016-08-15 18:04:06 -07001078 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) {
Hall Liuf62630a2015-10-27 14:53:39 -07001079 transitionTo(mActiveSpeakerRoute);
1080 }
1081 return HANDLED;
1082 default:
1083 return NOT_HANDLED;
1084 }
1085 }
1086 }
1087
1088 abstract class SpeakerRoute extends AudioState {
1089 @Override
Brad Ebinger11623a32015-11-25 13:52:02 -08001090 public boolean processMessage(Message msg) {
1091 if (super.processMessage(msg) == HANDLED) {
Hall Liuf62630a2015-10-27 14:53:39 -07001092 return HANDLED;
1093 }
1094 switch (msg.what) {
1095 case CONNECT_WIRED_HEADSET:
1096 sendInternalMessage(SWITCH_HEADSET);
1097 return HANDLED;
1098 case CONNECT_BLUETOOTH:
Hall Liu4f296ba2016-02-18 14:46:57 -08001099 if (!mHasUserExplicitlyLeftBluetooth) {
1100 sendInternalMessage(SWITCH_BLUETOOTH);
1101 } else {
Hall Liue74af082016-02-10 16:12:47 -08001102 Log.i(this, "Not switching to BT route from speaker because user has " +
1103 "explicitly disconnected.");
Hall Liu4f296ba2016-02-18 14:46:57 -08001104 updateSystemAudioState();
1105 }
Hall Liuf62630a2015-10-27 14:53:39 -07001106 return HANDLED;
1107 case DISCONNECT_BLUETOOTH:
1108 updateSystemAudioState();
1109 // No change in audio route required
1110 return HANDLED;
1111 case DISCONNECT_WIRED_HEADSET:
1112 updateSystemAudioState();
1113 // No change in audio route required
1114 return HANDLED;
Hall Liue74af082016-02-10 16:12:47 -08001115 case BT_AUDIO_DISCONNECT:
1116 // This may be sent as a confirmation by the BT stack after switch off BT.
1117 return HANDLED;
Hall Liuf62630a2015-10-27 14:53:39 -07001118 case CONNECT_DOCK:
1119 // Nothing to do here
1120 return HANDLED;
1121 case DISCONNECT_DOCK:
Hall Liua3e9dda2015-12-15 17:52:50 -08001122 sendInternalMessage(SWITCH_BASELINE_ROUTE);
Hall Liuf62630a2015-10-27 14:53:39 -07001123 return HANDLED;
1124 default:
1125 return NOT_HANDLED;
1126 }
1127 }
1128 }
1129
1130 private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute();
1131 private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute();
1132 private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute();
1133 private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute();
Hall Liue271f222016-08-15 18:04:06 -07001134 private final RingingBluetoothRoute mRingingBluetoothRoute = new RingingBluetoothRoute();
Hall Liuf62630a2015-10-27 14:53:39 -07001135 private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute();
1136 private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute();
1137 private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute();
1138 private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute();
1139
1140 /**
1141 * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit
1142 * states
1143 */
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001144 private int mDeviceSupportedRoutes;
Hall Liuf62630a2015-10-27 14:53:39 -07001145 private int mAvailableRoutes;
Hall Liue271f222016-08-15 18:04:06 -07001146 private int mAudioFocusType;
Hall Liuf62630a2015-10-27 14:53:39 -07001147 private boolean mWasOnSpeaker;
1148 private boolean mIsMuted;
Tyler Gunneaaf0742016-09-15 15:36:38 -07001149 private boolean mAreNotificationSuppressed = false;
Hall Liuf62630a2015-10-27 14:53:39 -07001150
1151 private final Context mContext;
1152 private final CallsManager mCallsManager;
1153 private final AudioManager mAudioManager;
1154 private final BluetoothManager mBluetoothManager;
1155 private final WiredHeadsetManager mWiredHeadsetManager;
1156 private final StatusBarNotifier mStatusBarNotifier;
1157 private final CallAudioManager.AudioServiceFactory mAudioServiceFactory;
Tyler Gunneaaf0742016-09-15 15:36:38 -07001158 private final InterruptionFilterProxy mInterruptionFilterProxy;
Hall Liua3e9dda2015-12-15 17:52:50 -08001159 private final boolean mDoesDeviceSupportEarpieceRoute;
Hall Liu609992b2016-08-31 15:48:51 -07001160 private final TelecomSystem.SyncRoot mLock;
Hall Liu4f296ba2016-02-18 14:46:57 -08001161 private boolean mHasUserExplicitlyLeftBluetooth = false;
Hall Liuf62630a2015-10-27 14:53:39 -07001162
1163 private HashMap<String, Integer> mStateNameToRouteCode;
1164 private HashMap<Integer, AudioState> mRouteCodeToQuiescentState;
1165
1166 // CallAudioState is used as an interface to communicate with many other system components.
1167 // No internal state transitions should depend on this variable.
1168 private CallAudioState mCurrentCallAudioState;
Hall Liud109afb2016-01-26 12:53:53 -08001169 private CallAudioState mLastKnownCallAudioState;
Hall Liuf62630a2015-10-27 14:53:39 -07001170
1171 public CallAudioRouteStateMachine(
1172 Context context,
1173 CallsManager callsManager,
1174 BluetoothManager bluetoothManager,
1175 WiredHeadsetManager wiredHeadsetManager,
1176 StatusBarNotifier statusBarNotifier,
Hall Liua3e9dda2015-12-15 17:52:50 -08001177 CallAudioManager.AudioServiceFactory audioServiceFactory,
Tyler Gunneaaf0742016-09-15 15:36:38 -07001178 InterruptionFilterProxy interruptionFilterProxy,
Hall Liua3e9dda2015-12-15 17:52:50 -08001179 boolean doesDeviceSupportEarpieceRoute) {
Hall Liuf62630a2015-10-27 14:53:39 -07001180 super(NAME);
1181 addState(mActiveEarpieceRoute);
1182 addState(mActiveHeadsetRoute);
1183 addState(mActiveBluetoothRoute);
1184 addState(mActiveSpeakerRoute);
Hall Liue271f222016-08-15 18:04:06 -07001185 addState(mRingingBluetoothRoute);
Hall Liuf62630a2015-10-27 14:53:39 -07001186 addState(mQuiescentEarpieceRoute);
1187 addState(mQuiescentHeadsetRoute);
1188 addState(mQuiescentBluetoothRoute);
1189 addState(mQuiescentSpeakerRoute);
1190
1191 mContext = context;
1192 mCallsManager = callsManager;
1193 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
1194 mBluetoothManager = bluetoothManager;
1195 mWiredHeadsetManager = wiredHeadsetManager;
1196 mStatusBarNotifier = statusBarNotifier;
1197 mAudioServiceFactory = audioServiceFactory;
Tyler Gunneaaf0742016-09-15 15:36:38 -07001198 mInterruptionFilterProxy = interruptionFilterProxy;
1199 // Register for misc other intent broadcasts.
1200 IntentFilter intentFilter =
1201 new IntentFilter(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED);
1202 context.registerReceiver(mReceiver, intentFilter);
Hall Liua3e9dda2015-12-15 17:52:50 -08001203 mDoesDeviceSupportEarpieceRoute = doesDeviceSupportEarpieceRoute;
Hall Liu609992b2016-08-31 15:48:51 -07001204 mLock = callsManager.getLock();
Hall Liuf62630a2015-10-27 14:53:39 -07001205
1206 mStateNameToRouteCode = new HashMap<>(8);
1207 mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE);
1208 mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1209 mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
1210 mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER);
Hall Liue271f222016-08-15 18:04:06 -07001211 mStateNameToRouteCode.put(mRingingBluetoothRoute.getName(), ROUTE_BLUETOOTH);
Hall Liuf62630a2015-10-27 14:53:39 -07001212 mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE);
1213 mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH);
1214 mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET);
1215 mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER);
1216
1217 mRouteCodeToQuiescentState = new HashMap<>(4);
1218 mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute);
1219 mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute);
1220 mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute);
1221 mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
Hall Liuf62630a2015-10-27 14:53:39 -07001222 }
1223
1224 /**
1225 * Initializes the state machine with info on initial audio route, supported audio routes,
1226 * and mute status.
1227 */
1228 public void initialize() {
1229 CallAudioState initState = getInitialAudioState();
1230 initialize(initState);
1231 }
1232
1233 public void initialize(CallAudioState initState) {
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001234 if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) {
Hall Liud5d98842016-12-07 14:04:57 -08001235 Log.e(this, new IllegalArgumentException(), "Route %d specified when supported call" +
1236 " routes are: %d", initState.getRoute(), getCurrentCallSupportedRoutes());
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001237 }
1238
Hall Liuf62630a2015-10-27 14:53:39 -07001239 mCurrentCallAudioState = initState;
Hall Liud109afb2016-01-26 12:53:53 -08001240 mLastKnownCallAudioState = initState;
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001241 mDeviceSupportedRoutes = initState.getSupportedRouteMask();
Hall Liud5d98842016-12-07 14:04:57 -08001242 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
Hall Liuf62630a2015-10-27 14:53:39 -07001243 mIsMuted = initState.isMuted();
Hall Liud8ca5452016-03-10 14:53:15 -08001244 mWasOnSpeaker = false;
Hall Liuf62630a2015-10-27 14:53:39 -07001245
1246 mStatusBarNotifier.notifyMute(initState.isMuted());
1247 mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER);
1248 setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute()));
1249 start();
1250 }
1251
1252 /**
1253 * Getter for the current CallAudioState object that the state machine is keeping track of.
1254 * Used for compatibility purposes.
1255 */
1256 public CallAudioState getCurrentCallAudioState() {
1257 return mCurrentCallAudioState;
1258 }
1259
Brad Ebinger72930a82015-11-23 10:06:40 -08001260 public void sendMessageWithSessionInfo(int message, int arg) {
1261 sendMessage(message, arg, 0, Log.createSubsession());
1262 }
1263
1264 public void sendMessageWithSessionInfo(int message) {
1265 sendMessage(message, 0, 0, Log.createSubsession());
1266 }
1267
Hall Liuf62630a2015-10-27 14:53:39 -07001268 /**
Hall Liu29855702015-12-11 17:42:03 -08001269 * This is for state-independent changes in audio route (i.e. muting or runnables)
Hall Liuf62630a2015-10-27 14:53:39 -07001270 * @param msg that couldn't be handled.
1271 */
1272 @Override
1273 protected void unhandledMessage(Message msg) {
1274 CallAudioState newCallAudioState;
1275 switch (msg.what) {
1276 case MUTE_ON:
1277 setMuteOn(true);
1278 newCallAudioState = new CallAudioState(mIsMuted,
1279 mCurrentCallAudioState.getRoute(),
1280 mAvailableRoutes);
Hall Liud109afb2016-01-26 12:53:53 -08001281 setSystemAudioState(newCallAudioState);
Hall Liuf62630a2015-10-27 14:53:39 -07001282 updateInternalCallAudioState();
1283 return;
1284 case MUTE_OFF:
1285 setMuteOn(false);
1286 newCallAudioState = new CallAudioState(mIsMuted,
1287 mCurrentCallAudioState.getRoute(),
1288 mAvailableRoutes);
Hall Liud109afb2016-01-26 12:53:53 -08001289 setSystemAudioState(newCallAudioState);
Hall Liuf62630a2015-10-27 14:53:39 -07001290 updateInternalCallAudioState();
1291 return;
1292 case TOGGLE_MUTE:
1293 if (mIsMuted) {
1294 sendInternalMessage(MUTE_OFF);
1295 } else {
1296 sendInternalMessage(MUTE_ON);
1297 }
1298 return;
Hall Liu8c7e2562016-04-13 18:26:03 -07001299 case UPDATE_SYSTEM_AUDIO_ROUTE:
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001300 updateRouteForForegroundCall();
Hall Liu8c7e2562016-04-13 18:26:03 -07001301 resendSystemAudioState();
1302 return;
Hall Liu29855702015-12-11 17:42:03 -08001303 case RUN_RUNNABLE:
Brad Ebingere62e9e82016-02-01 18:26:40 -08001304 java.lang.Runnable r = (java.lang.Runnable) msg.obj;
Hall Liu29855702015-12-11 17:42:03 -08001305 r.run();
1306 return;
Hall Liuf62630a2015-10-27 14:53:39 -07001307 default:
1308 Log.e(this, new IllegalStateException(),
1309 "Unexpected message code");
1310 }
1311 }
1312
Hall Liu29855702015-12-11 17:42:03 -08001313 public void quitStateMachine() {
1314 quitNow();
1315 }
1316
Tyler Gunneaaf0742016-09-15 15:36:38 -07001317 /**
1318 * Sets whether notifications should be suppressed or not. Used when in a call to ensure the
1319 * device will not vibrate due to notifications.
1320 * Alarm-only filtering is activated when
1321 *
1322 * @param on {@code true} when notification suppression should be activated, {@code false} when
1323 * it should be deactivated.
1324 */
1325 private void setNotificationsSuppressed(boolean on) {
1326 if (mInterruptionFilterProxy == null) {
1327 return;
1328 }
1329
Tyler Gunn00667fc2016-09-20 10:51:02 -07001330 Log.i(this, "setNotificationsSuppressed: on=%s; suppressed=%s", (on ? "yes" : "no"),
1331 (mAreNotificationSuppressed ? "yes" : "no"));
Tyler Gunneaaf0742016-09-15 15:36:38 -07001332 if (on) {
Tyler Gunn00667fc2016-09-20 10:51:02 -07001333 if (!mAreNotificationSuppressed) {
1334 // Enabling suppression of notifications.
1335 int interruptionFilter = mInterruptionFilterProxy.getCurrentInterruptionFilter();
1336 if (interruptionFilter == NotificationManager.INTERRUPTION_FILTER_ALL) {
1337 // No interruption filter is specified, so suppress notifications by setting the
1338 // current filter to alarms-only.
1339 mAreNotificationSuppressed = true;
1340 mInterruptionFilterProxy.setInterruptionFilter(
1341 NotificationManager.INTERRUPTION_FILTER_ALARMS);
1342 } else {
1343 // Interruption filter is already chosen by the user, so do not attempt to change
1344 // it.
1345 mAreNotificationSuppressed = false;
1346 }
Tyler Gunneaaf0742016-09-15 15:36:38 -07001347 }
1348 } else {
1349 // Disabling suppression of notifications.
1350 if (mAreNotificationSuppressed) {
1351 // We have implemented the alarms-only policy and the user has not changed it since
1352 // we originally set it, so reset the notification filter.
1353 mInterruptionFilterProxy.setInterruptionFilter(
1354 NotificationManager.INTERRUPTION_FILTER_ALL);
1355 }
1356 mAreNotificationSuppressed = false;
1357 }
1358 }
1359
Hall Liuf62630a2015-10-27 14:53:39 -07001360 private void setSpeakerphoneOn(boolean on) {
1361 if (mAudioManager.isSpeakerphoneOn() != on) {
1362 Log.i(this, "turning speaker phone %s", on);
1363 mAudioManager.setSpeakerphoneOn(on);
Hall Liu1d025d02016-06-24 14:01:19 -07001364 mStatusBarNotifier.notifySpeakerphone(on);
Hall Liuf62630a2015-10-27 14:53:39 -07001365 }
1366 }
1367
1368 private void setBluetoothOn(boolean on) {
1369 if (mBluetoothManager.isBluetoothAvailable()) {
1370 boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
1371 if (on != isAlreadyOn) {
1372 Log.i(this, "connecting bluetooth %s", on);
1373 if (on) {
1374 mBluetoothManager.connectBluetoothAudio();
1375 } else {
1376 mBluetoothManager.disconnectBluetoothAudio();
1377 }
1378 }
1379 }
1380 }
1381
1382 private void setMuteOn(boolean mute) {
1383 mIsMuted = mute;
Hall Liu874c0f82016-04-29 18:13:18 -07001384 Log.event(mCallsManager.getForegroundCall(), mute ? Log.Events.MUTE : Log.Events.UNMUTE);
1385
Hall Liuf62630a2015-10-27 14:53:39 -07001386 if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) {
1387 IAudioService audio = mAudioServiceFactory.getAudioService();
1388 Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
1389 mute, audio == null);
1390 if (audio != null) {
1391 try {
1392 // We use the audio service directly here so that we can specify
1393 // the current user. Telecom runs in the system_server process which
1394 // may run as a separate user from the foreground user. If we
1395 // used AudioManager directly, we would change mute for the system's
1396 // user and not the current foreground, which we want to avoid.
1397 audio.setMicrophoneMute(
1398 mute, mContext.getOpPackageName(), getCurrentUserId());
Hall Liu1d025d02016-06-24 14:01:19 -07001399 mStatusBarNotifier.notifyMute(mute);
Hall Liuf62630a2015-10-27 14:53:39 -07001400
1401 } catch (RemoteException e) {
1402 Log.e(this, e, "Remote exception while toggling mute.");
1403 }
1404 // TODO: Check microphone state after attempting to set to ensure that
1405 // our state corroborates AudioManager's state.
1406 }
1407 }
1408 }
1409
1410 /**
1411 * Updates the CallAudioState object from current internal state. The result is used for
1412 * external communication only.
1413 */
1414 private void updateInternalCallAudioState() {
1415 IState currentState = getCurrentState();
1416 if (currentState == null) {
1417 Log.e(this, new IllegalStateException(), "Current state should never be null" +
1418 " when updateInternalCallAudioState is called.");
1419 mCurrentCallAudioState = new CallAudioState(
1420 mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes);
1421 return;
1422 }
1423 int currentRoute = mStateNameToRouteCode.get(currentState.getName());
1424 mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes);
1425 }
1426
Hall Liud109afb2016-01-26 12:53:53 -08001427 private void setSystemAudioState(CallAudioState newCallAudioState) {
Hall Liu8c7e2562016-04-13 18:26:03 -07001428 setSystemAudioState(newCallAudioState, false);
1429 }
1430
1431 private void resendSystemAudioState() {
1432 setSystemAudioState(mLastKnownCallAudioState, true);
1433 }
1434
1435 private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) {
Hall Liu609992b2016-08-31 15:48:51 -07001436 synchronized (mLock) {
1437 Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState,
1438 newCallAudioState);
1439 if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) {
1440 if (newCallAudioState.getRoute() != mLastKnownCallAudioState.getRoute()) {
1441 Log.event(mCallsManager.getForegroundCall(),
1442 AUDIO_ROUTE_TO_LOG_EVENT.get(newCallAudioState.getRoute(),
1443 Log.Events.AUDIO_ROUTE));
1444 }
Hall Liu874c0f82016-04-29 18:13:18 -07001445
Hall Liu609992b2016-08-31 15:48:51 -07001446 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState);
1447 updateAudioForForegroundCall(newCallAudioState);
1448 mLastKnownCallAudioState = newCallAudioState;
1449 }
Hall Liuf62630a2015-10-27 14:53:39 -07001450 }
1451 }
1452
1453 private void updateAudioForForegroundCall(CallAudioState newCallAudioState) {
1454 Call call = mCallsManager.getForegroundCall();
1455 if (call != null && call.getConnectionService() != null) {
1456 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState);
1457 }
1458 }
1459
1460 private int calculateSupportedRoutes() {
1461 int routeMask = CallAudioState.ROUTE_SPEAKER;
1462
1463 if (mWiredHeadsetManager.isPluggedIn()) {
1464 routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
Hall Liua3e9dda2015-12-15 17:52:50 -08001465 } else if (mDoesDeviceSupportEarpieceRoute){
Hall Liuf62630a2015-10-27 14:53:39 -07001466 routeMask |= CallAudioState.ROUTE_EARPIECE;
1467 }
1468
1469 if (mBluetoothManager.isBluetoothAvailable()) {
1470 routeMask |= CallAudioState.ROUTE_BLUETOOTH;
1471 }
1472
1473 return routeMask;
1474 }
1475
1476 private void sendInternalMessage(int messageCode) {
1477 // Internal messages are messages which the state machine sends to itself in the
1478 // course of processing externally-sourced messages. We want to send these messages at
1479 // the front of the queue in order to make actions appear atomic to the user and to
1480 // prevent scenarios such as these:
1481 // 1. State machine handler thread is suspended for some reason.
1482 // 2. Headset gets connected (sends CONNECT_HEADSET).
1483 // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER).
1484 // 4. State machine handler is un-suspended.
1485 // 5. State machine handler processes the CONNECT_HEADSET message and sends
1486 // SWITCH_HEADSET at end of queue.
1487 // 6. State machine handler processes SWITCH_SPEAKER.
1488 // 7. State machine handler processes SWITCH_HEADSET.
Brad Ebinger72930a82015-11-23 10:06:40 -08001489 Session subsession = Log.createSubsession();
1490 if(subsession != null) {
1491 sendMessageAtFrontOfQueue(messageCode, subsession);
1492 } else {
1493 sendMessageAtFrontOfQueue(messageCode);
1494 }
Hall Liuf62630a2015-10-27 14:53:39 -07001495 }
1496
1497 private CallAudioState getInitialAudioState() {
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001498 int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes();
Hall Liua3e9dda2015-12-15 17:52:50 -08001499 final int route;
1500
Hall Liuf62630a2015-10-27 14:53:39 -07001501 if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0) {
1502 route = ROUTE_BLUETOOTH;
Hall Liua3e9dda2015-12-15 17:52:50 -08001503 } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) {
1504 route = ROUTE_WIRED_HEADSET;
1505 } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) {
1506 route = ROUTE_EARPIECE;
1507 } else {
1508 route = ROUTE_SPEAKER;
Hall Liuf62630a2015-10-27 14:53:39 -07001509 }
1510
1511 return new CallAudioState(false, route, supportedRouteMask);
1512 }
1513
1514 private int getCurrentUserId() {
1515 final long ident = Binder.clearCallingIdentity();
1516 try {
1517 UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
1518 return currentUser.id;
1519 } catch (RemoteException e) {
1520 // Activity manager not running, nothing we can do assume user 0.
1521 } finally {
1522 Binder.restoreCallingIdentity(ident);
1523 }
1524 return UserHandle.USER_OWNER;
1525 }
1526
1527 private boolean isInActiveState() {
1528 AudioState currentState = (AudioState) getCurrentState();
1529 if (currentState == null) {
1530 Log.w(this, "Current state is null, assuming inactive state");
1531 return false;
1532 }
1533 return currentState.isActive();
1534 }
Hall Liua3e9dda2015-12-15 17:52:50 -08001535
1536 public static boolean doesDeviceSupportEarpieceRoute() {
1537 String[] characteristics = SystemProperties.get("ro.build.characteristics").split(",");
1538 for (String characteristic : characteristics) {
1539 if ("watch".equals(characteristic)) {
1540 return false;
1541 }
1542 }
1543 return true;
1544 }
Hall Liu4f296ba2016-02-18 14:46:57 -08001545
1546 private int calculateBaselineRouteMessage(boolean isExplicitUserRequest) {
1547 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) {
1548 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE;
1549 } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) {
1550 return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET;
Hall Liu4f296ba2016-02-18 14:46:57 -08001551 } else {
Hall Liud5d98842016-12-07 14:04:57 -08001552 return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER;
Hall Liu4f296ba2016-02-18 14:46:57 -08001553 }
1554 }
Hall Liud8ca5452016-03-10 14:53:15 -08001555
1556 private void reinitialize() {
1557 CallAudioState initState = getInitialAudioState();
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001558 mDeviceSupportedRoutes = initState.getSupportedRouteMask();
1559 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
Hall Liud8ca5452016-03-10 14:53:15 -08001560 mIsMuted = initState.isMuted();
1561 setMuteOn(mIsMuted);
1562 mWasOnSpeaker = false;
1563 mHasUserExplicitlyLeftBluetooth = false;
Garik Badalyanb3cbeb02016-05-06 17:04:24 -07001564 mLastKnownCallAudioState = initState;
Hall Liud8ca5452016-03-10 14:53:15 -08001565 transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute()));
1566 }
Christine Hallstrom96a0be62016-11-30 16:05:13 -08001567
1568 private void updateRouteForForegroundCall() {
1569 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes();
1570
1571 CallAudioState currentState = getCurrentCallAudioState();
1572
1573 // Move to baseline route in the case the current route is no longer available.
1574 if ((mAvailableRoutes & currentState.getRoute()) == 0) {
1575 sendInternalMessage(calculateBaselineRouteMessage(false));
1576 }
1577 }
Hall Liud5d98842016-12-07 14:04:57 -08001578
1579 private int getCurrentCallSupportedRoutes() {
1580 int supportedRoutes = CallAudioState.ROUTE_ALL;
1581
1582 if (mCallsManager.getForegroundCall() != null) {
1583 supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes();
1584 }
1585
1586 return supportedRoutes;
1587 }
1588
1589 private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) {
1590 base &= ~remove;
1591
1592 if (considerCurrentCall) {
1593 add &= getCurrentCallSupportedRoutes();
1594 }
1595
1596 base |= add;
1597
1598 return base;
1599 }
Garik Badalyanb3cbeb02016-05-06 17:04:24 -07001600}