1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.telecom; 18 19 import android.media.AudioAttributes; 20 import android.media.AudioManager; 21 import android.os.Looper; 22 import android.os.Message; 23 import android.telecom.Log; 24 import android.telecom.Logging.Runnable; 25 import android.telecom.Logging.Session; 26 import android.util.LocalLog; 27 import android.util.SparseArray; 28 import com.android.internal.util.IState; 29 import com.android.internal.util.IndentingPrintWriter; 30 import com.android.internal.util.State; 31 import com.android.internal.util.StateMachine; 32 import com.android.server.telecom.flags.FeatureFlags; 33 34 public class CallAudioModeStateMachine extends StateMachine { 35 /** 36 * Captures the most recent CallAudioModeStateMachine state transitions and the corresponding 37 * changes to the {@link AudioManager#setMode}. 38 */ 39 private LocalLog mLocalLog = new LocalLog(20); 40 public static class Factory { create(SystemStateHelper systemStateHelper, AudioManager am, FeatureFlags featureFlags, CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker)41 public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper, 42 AudioManager am, FeatureFlags featureFlags, 43 CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker) { 44 return new CallAudioModeStateMachine(systemStateHelper, am, 45 featureFlags, callAudioCommunicationDeviceTracker); 46 } 47 } 48 49 public static class MessageArgs { 50 public boolean hasActiveOrDialingCalls; 51 public boolean hasRingingCalls; 52 public boolean hasHoldingCalls; 53 public boolean hasAudioProcessingCalls; 54 public boolean isTonePlaying; 55 public boolean foregroundCallIsVoip; 56 public boolean isStreaming; 57 public Session session; 58 MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls, boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip, boolean isStreaming, Session session)59 private MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls, 60 boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying, 61 boolean foregroundCallIsVoip, boolean isStreaming, Session session) { 62 this.hasActiveOrDialingCalls = hasActiveOrDialingCalls; 63 this.hasRingingCalls = hasRingingCalls; 64 this.hasHoldingCalls = hasHoldingCalls; 65 this.hasAudioProcessingCalls = hasAudioProcessingCalls; 66 this.isTonePlaying = isTonePlaying; 67 this.foregroundCallIsVoip = foregroundCallIsVoip; 68 this.isStreaming = isStreaming; 69 this.session = session; 70 } 71 72 @Override toString()73 public String toString() { 74 return "MessageArgs{" + 75 "hasActiveCalls=" + hasActiveOrDialingCalls + 76 ", hasRingingCalls=" + hasRingingCalls + 77 ", hasHoldingCalls=" + hasHoldingCalls + 78 ", hasAudioProcessingCalls=" + hasAudioProcessingCalls + 79 ", isTonePlaying=" + isTonePlaying + 80 ", foregroundCallIsVoip=" + foregroundCallIsVoip + 81 ", isStreaming=" + isStreaming + 82 ", session=" + session + 83 '}'; 84 } 85 86 public static class Builder { 87 private boolean mHasActiveOrDialingCalls; 88 private boolean mHasRingingCalls; 89 private boolean mHasHoldingCalls; 90 private boolean mHasAudioProcessingCalls; 91 private boolean mIsTonePlaying; 92 private boolean mForegroundCallIsVoip; 93 private boolean mIsStreaming; 94 private Session mSession; 95 setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls)96 public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) { 97 mHasActiveOrDialingCalls = hasActiveOrDialingCalls; 98 return this; 99 } 100 setHasRingingCalls(boolean hasRingingCalls)101 public Builder setHasRingingCalls(boolean hasRingingCalls) { 102 mHasRingingCalls = hasRingingCalls; 103 return this; 104 } 105 setHasHoldingCalls(boolean hasHoldingCalls)106 public Builder setHasHoldingCalls(boolean hasHoldingCalls) { 107 mHasHoldingCalls = hasHoldingCalls; 108 return this; 109 } 110 setHasAudioProcessingCalls(boolean hasAudioProcessingCalls)111 public Builder setHasAudioProcessingCalls(boolean hasAudioProcessingCalls) { 112 mHasAudioProcessingCalls = hasAudioProcessingCalls; 113 return this; 114 } 115 setIsTonePlaying(boolean isTonePlaying)116 public Builder setIsTonePlaying(boolean isTonePlaying) { 117 mIsTonePlaying = isTonePlaying; 118 return this; 119 } 120 setForegroundCallIsVoip(boolean foregroundCallIsVoip)121 public Builder setForegroundCallIsVoip(boolean foregroundCallIsVoip) { 122 mForegroundCallIsVoip = foregroundCallIsVoip; 123 return this; 124 } 125 setSession(Session session)126 public Builder setSession(Session session) { 127 mSession = session; 128 return this; 129 } 130 setIsStreaming(boolean isStraeming)131 public Builder setIsStreaming(boolean isStraeming) { 132 mIsStreaming = isStraeming; 133 return this; 134 } 135 build()136 public MessageArgs build() { 137 return new MessageArgs(mHasActiveOrDialingCalls, mHasRingingCalls, mHasHoldingCalls, 138 mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip, 139 mIsStreaming, mSession); 140 } 141 } 142 } 143 144 // TODO: remove this and replace when the new audio mode gets pushed to AOSP. 145 public static final int NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING = 4; 146 147 public static final int INITIALIZE = 1; 148 // These ENTER_*_FOCUS commands are for testing. 149 public static final int ENTER_CALL_FOCUS_FOR_TESTING = 2; 150 public static final int ENTER_COMMS_FOCUS_FOR_TESTING = 3; 151 public static final int ENTER_RING_FOCUS_FOR_TESTING = 4; 152 public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5; 153 public static final int ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING = 6; 154 public static final int ENTER_STREAMING_FOCUS_FOR_TESTING = 7; 155 public static final int ABANDON_FOCUS_FOR_TESTING = 8; 156 157 public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001; 158 public static final int NO_MORE_RINGING_CALLS = 1002; 159 public static final int NO_MORE_HOLDING_CALLS = 1003; 160 public static final int NO_MORE_AUDIO_PROCESSING_CALLS = 1004; 161 162 public static final int NEW_ACTIVE_OR_DIALING_CALL = 2001; 163 public static final int NEW_RINGING_CALL = 2002; 164 public static final int NEW_HOLDING_CALL = 2003; 165 public static final int NEW_AUDIO_PROCESSING_CALL = 2004; 166 167 public static final int TONE_STARTED_PLAYING = 3001; 168 public static final int TONE_STOPPED_PLAYING = 3002; 169 170 public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001; 171 172 public static final int RINGER_MODE_CHANGE = 5001; 173 174 // Used to indicate that Telecom is done doing things to the AudioManager and that it's safe 175 // to release focus for other apps to take over. 176 public static final int AUDIO_OPERATIONS_COMPLETE = 6001; 177 178 public static final int START_CALL_STREAMING = 7001; 179 public static final int STOP_CALL_STREAMING = 7002; 180 181 public static final int RUN_RUNNABLE = 9001; 182 183 private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{ 184 put(ENTER_CALL_FOCUS_FOR_TESTING, "ENTER_CALL_FOCUS_FOR_TESTING"); 185 put(ENTER_COMMS_FOCUS_FOR_TESTING, "ENTER_COMMS_FOCUS_FOR_TESTING"); 186 put(ENTER_RING_FOCUS_FOR_TESTING, "ENTER_RING_FOCUS_FOR_TESTING"); 187 put(ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, "ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING"); 188 put(ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, "ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING"); 189 put(ABANDON_FOCUS_FOR_TESTING, "ABANDON_FOCUS_FOR_TESTING"); 190 put(NO_MORE_ACTIVE_OR_DIALING_CALLS, "NO_MORE_ACTIVE_OR_DIALING_CALLS"); 191 put(NO_MORE_RINGING_CALLS, "NO_MORE_RINGING_CALLS"); 192 put(NO_MORE_HOLDING_CALLS, "NO_MORE_HOLDING_CALLS"); 193 put(NO_MORE_AUDIO_PROCESSING_CALLS, "NO_MORE_AUDIO_PROCESSING_CALLS"); 194 put(NEW_ACTIVE_OR_DIALING_CALL, "NEW_ACTIVE_OR_DIALING_CALL"); 195 put(NEW_RINGING_CALL, "NEW_RINGING_CALL"); 196 put(NEW_HOLDING_CALL, "NEW_HOLDING_CALL"); 197 put(NEW_AUDIO_PROCESSING_CALL, "NEW_AUDIO_PROCESSING_CALL"); 198 put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING"); 199 put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING"); 200 put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE"); 201 put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE"); 202 put(AUDIO_OPERATIONS_COMPLETE, "AUDIO_OPERATIONS_COMPLETE"); 203 put(START_CALL_STREAMING, "START_CALL_STREAMING"); 204 put(STOP_CALL_STREAMING, "STOP_CALL_STREAMING"); 205 206 put(RUN_RUNNABLE, "RUN_RUNNABLE"); 207 }}; 208 209 public static final String TONE_HOLD_STATE_NAME = OtherFocusState.class.getSimpleName(); 210 public static final String UNFOCUSED_STATE_NAME = UnfocusedState.class.getSimpleName(); 211 public static final String AUDIO_PROCESSING_STATE_NAME = 212 AudioProcessingFocusState.class.getSimpleName(); 213 public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName(); 214 public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName(); 215 public static final String STREAMING_STATE_NAME = StreamingFocusState.class.getSimpleName(); 216 public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName(); 217 218 private class BaseState extends State { 219 @Override processMessage(Message msg)220 public boolean processMessage(Message msg) { 221 switch (msg.what) { 222 case ENTER_CALL_FOCUS_FOR_TESTING: 223 transitionTo(mSimCallFocusState); 224 return HANDLED; 225 case ENTER_COMMS_FOCUS_FOR_TESTING: 226 transitionTo(mVoipCallFocusState); 227 return HANDLED; 228 case ENTER_RING_FOCUS_FOR_TESTING: 229 transitionTo(mRingingFocusState); 230 return HANDLED; 231 case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING: 232 transitionTo(mOtherFocusState); 233 return HANDLED; 234 case ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING: 235 transitionTo(mAudioProcessingFocusState); 236 return HANDLED; 237 case ENTER_STREAMING_FOCUS_FOR_TESTING: 238 transitionTo(mStreamingFocusState); 239 return HANDLED; 240 case ABANDON_FOCUS_FOR_TESTING: 241 transitionTo(mUnfocusedState); 242 return HANDLED; 243 case INITIALIZE: 244 mIsInitialized = true; 245 return HANDLED; 246 case RUN_RUNNABLE: 247 java.lang.Runnable r = (java.lang.Runnable) msg.obj; 248 r.run(); 249 return HANDLED; 250 default: 251 return NOT_HANDLED; 252 } 253 } 254 } 255 256 private class UnfocusedState extends BaseState { 257 @Override enter()258 public void enter() { 259 Log.i(LOG_TAG, "Audio focus entering UNFOCUSED state"); 260 mLocalLog.log("Enter UNFOCUSED"); 261 if (mIsInitialized) { 262 // Clear any communication device that was requested previously. 263 // Todo: Remove once clearCommunicationDeviceAfterAudioOpsComplete is 264 // completely rolled out. 265 if (mFeatureFlags.callAudioCommunicationDeviceRefactor() 266 && !mFeatureFlags.clearCommunicationDeviceAfterAudioOpsComplete()) { 267 mCommunicationDeviceTracker.clearCommunicationDevice(mCommunicationDeviceTracker 268 .getCurrentLocallyRequestedCommunicationDevice()); 269 } 270 if (mFeatureFlags.setAudioModeBeforeAbandonFocus()) { 271 Log.i(this, "enter: AudioManager#setMode(MODE_NORMAL)"); 272 mAudioManager.setMode(AudioManager.MODE_NORMAL); 273 mCallAudioManager.setCallAudioRouteFocusState( 274 CallAudioRouteStateMachine.NO_FOCUS); 275 } else { 276 mCallAudioManager.setCallAudioRouteFocusState( 277 CallAudioRouteStateMachine.NO_FOCUS); 278 Log.i(this, "enter: AudioManager#setMode(MODE_NORMAL)"); 279 mAudioManager.setMode(AudioManager.MODE_NORMAL); 280 } 281 mLocalLog.log("Mode MODE_NORMAL"); 282 mMostRecentMode = AudioManager.MODE_NORMAL; 283 // Don't release focus here -- wait until we get a signal that any other audio 284 // operations triggered by this are done before releasing focus. 285 } 286 } 287 288 @Override processMessage(Message msg)289 public boolean processMessage(Message msg) { 290 if (super.processMessage(msg) == HANDLED) { 291 return HANDLED; 292 } 293 MessageArgs args = (MessageArgs) msg.obj; 294 switch (msg.what) { 295 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 296 // Do nothing. 297 return HANDLED; 298 case NO_MORE_RINGING_CALLS: 299 // Do nothing. 300 return HANDLED; 301 case NO_MORE_HOLDING_CALLS: 302 // Do nothing. 303 return HANDLED; 304 case NO_MORE_AUDIO_PROCESSING_CALLS: 305 // Do nothing. 306 return HANDLED; 307 case NEW_ACTIVE_OR_DIALING_CALL: 308 transitionTo(args.foregroundCallIsVoip 309 ? mVoipCallFocusState : mSimCallFocusState); 310 return HANDLED; 311 case NEW_RINGING_CALL: 312 transitionTo(mRingingFocusState); 313 return HANDLED; 314 case NEW_AUDIO_PROCESSING_CALL: 315 transitionTo(mAudioProcessingFocusState); 316 return HANDLED; 317 case NEW_HOLDING_CALL: 318 // This really shouldn't happen, but transition to the focused state anyway. 319 Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." + 320 " Args are: \n" + args.toString()); 321 transitionTo(mOtherFocusState); 322 return HANDLED; 323 case START_CALL_STREAMING: 324 transitionTo(mStreamingFocusState); 325 return HANDLED; 326 case TONE_STARTED_PLAYING: 327 // This shouldn't happen either, but perform the action anyway. 328 Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n" 329 + args.toString()); 330 return HANDLED; 331 case AUDIO_OPERATIONS_COMPLETE: 332 Log.i(this, "AudioOperationsComplete: " 333 + "AudioManager#abandonAudioFocusRequest(); now unfocused"); 334 mAudioManager.abandonAudioFocusForCall(); 335 // Clear requested communication device after the call ends. 336 if (mFeatureFlags.clearCommunicationDeviceAfterAudioOpsComplete()) { 337 // Oh flags! If we're using the refactored audio route switching, we should 338 // not be using the communication device tracker; that is exclusively for 339 // the old code path. 340 if (!mFeatureFlags.dontUseCommunicationDeviceTracker() 341 || !mFeatureFlags.useRefactoredAudioRouteSwitching()) { 342 mCommunicationDeviceTracker.clearCommunicationDevice( 343 mCommunicationDeviceTracker 344 .getCurrentLocallyRequestedCommunicationDevice()); 345 } 346 } 347 return HANDLED; 348 default: 349 // The forced focus switch commands are handled by BaseState. 350 return NOT_HANDLED; 351 } 352 } 353 } 354 355 private class AudioProcessingFocusState extends BaseState { 356 @Override enter()357 public void enter() { 358 Log.i(LOG_TAG, "Audio focus entering AUDIO_PROCESSING state"); 359 mLocalLog.log("Enter AUDIO_PROCESSING"); 360 if (mIsInitialized) { 361 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS); 362 Log.i(this, "enter: AudioManager#setMode(MODE_AUDIO_PROCESSING)"); 363 mAudioManager.setMode(NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING); 364 mLocalLog.log("Mode MODE_CALL_SCREENING"); 365 mMostRecentMode = NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING; 366 } 367 } 368 369 @Override processMessage(Message msg)370 public boolean processMessage(Message msg) { 371 if (super.processMessage(msg) == HANDLED) { 372 return HANDLED; 373 } 374 MessageArgs args = (MessageArgs) msg.obj; 375 switch (msg.what) { 376 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 377 // Do nothing. 378 return HANDLED; 379 case NO_MORE_RINGING_CALLS: 380 // Do nothing. 381 return HANDLED; 382 case NO_MORE_HOLDING_CALLS: 383 // Do nothing. 384 return HANDLED; 385 case NO_MORE_AUDIO_PROCESSING_CALLS: 386 BaseState destState = calculateProperStateFromArgs(args); 387 if (destState == this) { 388 Log.w(LOG_TAG, "Got spurious NO_MORE_AUDIO_PROCESSING_CALLS"); 389 } 390 transitionTo(destState); 391 return HANDLED; 392 case NEW_ACTIVE_OR_DIALING_CALL: 393 transitionTo(args.foregroundCallIsVoip 394 ? mVoipCallFocusState : mSimCallFocusState); 395 return HANDLED; 396 case NEW_RINGING_CALL: 397 transitionTo(mRingingFocusState); 398 return HANDLED; 399 case NEW_HOLDING_CALL: 400 // This really shouldn't happen, but recalculate from args and do the transition 401 Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." + 402 " Args are: \n" + args.toString()); 403 transitionTo(mOtherFocusState); 404 return HANDLED; 405 case NEW_AUDIO_PROCESSING_CALL: 406 // Can happen as a duplicate message 407 return HANDLED; 408 case TONE_STARTED_PLAYING: 409 // This shouldn't happen either, but perform the action anyway. 410 Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n" 411 + args.toString()); 412 return HANDLED; 413 case START_CALL_STREAMING: 414 transitionTo(mStreamingFocusState); 415 return HANDLED; 416 case AUDIO_OPERATIONS_COMPLETE: 417 Log.i(LOG_TAG, "AudioManager#abandonAudioFocusRequest: now " 418 + "AUDIO_PROCESSING"); 419 mAudioManager.abandonAudioFocusForCall(); 420 return HANDLED; 421 default: 422 // The forced focus switch commands are handled by BaseState. 423 return NOT_HANDLED; 424 } 425 } 426 } 427 428 private class RingingFocusState extends BaseState { 429 // Keeps track of whether we're ringing with audio focus or if we've just entered the state 430 // without acquiring focus because of a silent ringtone or something. 431 private boolean mHasFocus = false; 432 tryStartRinging()433 private void tryStartRinging() { 434 if (mHasFocus && mCallAudioManager.isRingtonePlaying()) { 435 Log.i(LOG_TAG, 436 "RingingFocusState#tryStartRinging -- audio focus previously" 437 + " acquired and ringtone already playing -- skipping."); 438 return; 439 } 440 441 if (mCallAudioManager.startRinging()) { 442 Log.i(this, "tryStartRinging: AudioManager#requestAudioFocus(RING)"); 443 mAudioManager.requestAudioFocusForCall( 444 AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 445 446 // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode -- 447 // this trips up the audio system. 448 if (mAudioManager.getMode() != AudioManager.MODE_CALL_SCREENING) { 449 Log.i(this, "enter: AudioManager#setMode(MODE_RINGTONE)"); 450 mAudioManager.setMode(AudioManager.MODE_RINGTONE); 451 mLocalLog.log("Mode MODE_RINGTONE"); 452 } 453 mCallAudioManager.setCallAudioRouteFocusState( 454 CallAudioRouteStateMachine.RINGING_FOCUS); 455 mHasFocus = true; 456 } else { 457 Log.i( 458 LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus"); 459 } 460 } 461 462 @Override enter()463 public void enter() { 464 Log.i(LOG_TAG, "Audio focus entering RINGING state"); 465 mLocalLog.log("Enter RINGING"); 466 tryStartRinging(); 467 mCallAudioManager.stopCallWaiting(); 468 } 469 470 @Override exit()471 public void exit() { 472 // Audio mode and audio stream will be set by the next state. 473 mCallAudioManager.stopRinging(); 474 mHasFocus = false; 475 } 476 477 @Override processMessage(Message msg)478 public boolean processMessage(Message msg) { 479 if (super.processMessage(msg) == HANDLED) { 480 return HANDLED; 481 } 482 MessageArgs args = (MessageArgs) msg.obj; 483 switch (msg.what) { 484 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 485 // Do nothing. Loss of an active call should not impact ringer. 486 return HANDLED; 487 case NO_MORE_HOLDING_CALLS: 488 // Do nothing and keep ringing. 489 return HANDLED; 490 case NO_MORE_RINGING_CALLS: 491 BaseState destState = calculateProperStateFromArgs(args); 492 if (destState == this) { 493 Log.w(LOG_TAG, "Got spurious NO_MORE_RINGING_CALLS"); 494 } 495 transitionTo(destState); 496 return HANDLED; 497 case NEW_ACTIVE_OR_DIALING_CALL: 498 // If a call becomes active suddenly, give it priority over ringing. 499 transitionTo(args.foregroundCallIsVoip 500 ? mVoipCallFocusState : mSimCallFocusState); 501 return HANDLED; 502 case NEW_AUDIO_PROCESSING_CALL: 503 // If we don't have any more ringing calls, transition to audio processing. 504 if (!args.hasRingingCalls) { 505 transitionTo(mAudioProcessingFocusState); 506 } else { 507 Log.w(LOG_TAG, "Got a audio processing call while there's still a call " 508 + "ringing"); 509 } 510 case NEW_RINGING_CALL: 511 // Can happen as a duplicate message 512 return HANDLED; 513 case NEW_HOLDING_CALL: 514 // This really shouldn't happen, but transition to the focused state anyway. 515 Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." + 516 " Args are: " + args.toString()); 517 transitionTo(mOtherFocusState); 518 return HANDLED; 519 case RINGER_MODE_CHANGE: { 520 Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE"); 521 tryStartRinging(); 522 return HANDLED; 523 } 524 case AUDIO_OPERATIONS_COMPLETE: 525 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 526 + " state"); 527 return HANDLED; 528 default: 529 // The forced focus switch commands are handled by BaseState. 530 return NOT_HANDLED; 531 } 532 } 533 } 534 535 private class SimCallFocusState extends BaseState { 536 @Override enter()537 public void enter() { 538 Log.i(LOG_TAG, "Audio focus entering SIM CALL state"); 539 mLocalLog.log("Enter SIM_CALL"); 540 Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)"); 541 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, 542 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 543 Log.i(this, "enter: AudioManager#setMode(MODE_IN_CALL)"); 544 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 545 mLocalLog.log("Mode MODE_IN_CALL"); 546 mMostRecentMode = AudioManager.MODE_IN_CALL; 547 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS); 548 } 549 550 @Override processMessage(Message msg)551 public boolean processMessage(Message msg) { 552 if (super.processMessage(msg) == HANDLED) { 553 return HANDLED; 554 } 555 MessageArgs args = (MessageArgs) msg.obj; 556 switch (msg.what) { 557 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 558 // Switch to either ringing, holding, or inactive 559 transitionTo(calculateProperStateFromArgs(args)); 560 return HANDLED; 561 case NO_MORE_RINGING_CALLS: 562 // Don't transition state, but stop any call-waiting tones that may have been 563 // playing. 564 if (args.isTonePlaying) { 565 mCallAudioManager.stopCallWaiting(); 566 } 567 // If a MT-audio-speedup call gets disconnected by the connection service 568 // concurrently with the user answering it, we may get this message 569 // indicating that a ringing call has disconnected while this state machine 570 // is in the SimCallFocusState. 571 if (!args.hasActiveOrDialingCalls) { 572 transitionTo(calculateProperStateFromArgs(args)); 573 } 574 return HANDLED; 575 case NO_MORE_HOLDING_CALLS: 576 if (args.foregroundCallIsVoip) { 577 transitionTo(mVoipCallFocusState); 578 } 579 return HANDLED; 580 case NEW_ACTIVE_OR_DIALING_CALL: 581 if (args.foregroundCallIsVoip) { 582 transitionTo(mVoipCallFocusState); 583 } 584 return HANDLED; 585 case NEW_RINGING_CALL: 586 // Don't make a call ring over an active call, but do play a call waiting tone. 587 mCallAudioManager.startCallWaiting("call already active"); 588 return HANDLED; 589 case NEW_HOLDING_CALL: 590 // Just check the voip mode. Putting an active call on hold will be handled when 591 // NO_MORE_ACTIVE_CALLS is processed. 592 if (args.foregroundCallIsVoip) { 593 transitionTo(mVoipCallFocusState); 594 } 595 return HANDLED; 596 case NEW_AUDIO_PROCESSING_CALL: 597 // If we don't have any more active calls, transition to audio processing. 598 if (!args.hasActiveOrDialingCalls) { 599 transitionTo(mAudioProcessingFocusState); 600 } else { 601 Log.w(LOG_TAG, "Got a audio processing call while there's still a call " 602 + "active"); 603 } 604 case FOREGROUND_VOIP_MODE_CHANGE: 605 if (args.foregroundCallIsVoip) { 606 transitionTo(mVoipCallFocusState); 607 } 608 return HANDLED; 609 case AUDIO_OPERATIONS_COMPLETE: 610 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 611 + " state"); 612 return HANDLED; 613 default: 614 // The forced focus switch commands are handled by BaseState. 615 return NOT_HANDLED; 616 } 617 } 618 } 619 620 private class VoipCallFocusState extends BaseState { 621 @Override enter()622 public void enter() { 623 Log.i(LOG_TAG, "Audio focus entering VOIP CALL state"); 624 mLocalLog.log("Enter VOIP_CALL"); 625 Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)"); 626 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, 627 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 628 Log.i(this, "enter: AudioManager#setMode(MODE_IN_COMMUNICATION)"); 629 mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); 630 mLocalLog.log("Mode MODE_IN_COMMUNICATION"); 631 mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION; 632 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS); 633 } 634 635 @Override processMessage(Message msg)636 public boolean processMessage(Message msg) { 637 if (super.processMessage(msg) == HANDLED) { 638 return HANDLED; 639 } 640 MessageArgs args = (MessageArgs) msg.obj; 641 switch (msg.what) { 642 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 643 // Switch to either ringing, holding, or inactive 644 transitionTo(calculateProperStateFromArgs(args)); 645 return HANDLED; 646 case NO_MORE_RINGING_CALLS: 647 // Don't transition state, but stop any call-waiting tones that may have been 648 // playing. 649 if (args.isTonePlaying) { 650 mCallAudioManager.stopCallWaiting(); 651 } 652 return HANDLED; 653 case NO_MORE_HOLDING_CALLS: 654 if (!args.foregroundCallIsVoip) { 655 transitionTo(mSimCallFocusState); 656 } 657 return HANDLED; 658 case NEW_ACTIVE_OR_DIALING_CALL: 659 if (!args.foregroundCallIsVoip) { 660 transitionTo(mSimCallFocusState); 661 } 662 return HANDLED; 663 case NEW_RINGING_CALL: 664 // Don't make a call ring over an active call, but do play a call waiting tone. 665 mCallAudioManager.startCallWaiting("call already active"); 666 return HANDLED; 667 case NEW_HOLDING_CALL: 668 // Just check the voip mode. Putting an active call on hold will be handled when 669 // NO_MORE_ACTIVE_CALLS is processed. 670 if (!args.foregroundCallIsVoip) { 671 transitionTo(mSimCallFocusState); 672 } 673 return HANDLED; 674 case NEW_AUDIO_PROCESSING_CALL: 675 // If we don't have any more active calls, transition to audio processing. 676 if (!args.hasActiveOrDialingCalls) { 677 transitionTo(mAudioProcessingFocusState); 678 } else { 679 Log.w(LOG_TAG, "Got a audio processing call while there's still a call " 680 + "active"); 681 } 682 case FOREGROUND_VOIP_MODE_CHANGE: 683 if (!args.foregroundCallIsVoip) { 684 transitionTo(mSimCallFocusState); 685 } 686 return HANDLED; 687 case AUDIO_OPERATIONS_COMPLETE: 688 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 689 + " state"); 690 return HANDLED; 691 case START_CALL_STREAMING: 692 transitionTo(mStreamingFocusState); 693 return HANDLED; 694 default: 695 // The forced focus switch commands are handled by BaseState. 696 return NOT_HANDLED; 697 } 698 } 699 } 700 701 private class StreamingFocusState extends BaseState { 702 @Override enter()703 public void enter() { 704 Log.i(LOG_TAG, "Audio focus entering streaming state"); 705 mLocalLog.log("Enter Streaming"); 706 mLocalLog.log("Mode MODE_COMMUNICATION_REDIRECT"); 707 Log.i(this, "enter: AudioManager#setMode(MODE_COMMUNICATION_REDIRECT"); 708 mAudioManager.setMode(AudioManager.MODE_COMMUNICATION_REDIRECT); 709 mMostRecentMode = AudioManager.MODE_NORMAL; 710 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS); 711 mCallAudioManager.getCallAudioRouteAdapter().sendMessageWithSessionInfo( 712 CallAudioRouteStateMachine.STREAMING_FORCE_ENABLED); 713 } 714 preExit()715 private void preExit() { 716 mCallAudioManager.getCallAudioRouteAdapter().sendMessageWithSessionInfo( 717 CallAudioRouteStateMachine.STREAMING_FORCE_DISABLED); 718 } 719 720 @Override processMessage(Message msg)721 public boolean processMessage(Message msg) { 722 if (super.processMessage(msg) == HANDLED) { 723 return HANDLED; 724 } 725 MessageArgs args = (MessageArgs) msg.obj; 726 switch (msg.what) { 727 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 728 // Switch to either ringing, holding, or inactive 729 transitionTo(calculateProperStateFromArgs(args)); 730 return HANDLED; 731 case NO_MORE_RINGING_CALLS: 732 // Do nothing. 733 return HANDLED; 734 case NO_MORE_HOLDING_CALLS: 735 // Do nothing. 736 return HANDLED; 737 case NO_MORE_AUDIO_PROCESSING_CALLS: 738 // Do nothing. 739 return HANDLED; 740 case NEW_ACTIVE_OR_DIALING_CALL: 741 // Only possible for emergency call 742 BaseState destState = calculateProperStateFromArgs(args); 743 if (destState != this) { 744 preExit(); 745 transitionTo(destState); 746 } 747 return HANDLED; 748 case NEW_RINGING_CALL: 749 // Only possible for emergency call 750 preExit(); 751 transitionTo(mRingingFocusState); 752 return HANDLED; 753 case NEW_HOLDING_CALL: 754 // Do nothing. 755 return HANDLED; 756 case NEW_AUDIO_PROCESSING_CALL: 757 // Do nothing. 758 return HANDLED; 759 case START_CALL_STREAMING: 760 // Can happen as a duplicate message 761 return HANDLED; 762 case TONE_STARTED_PLAYING: 763 // Do nothing. 764 return HANDLED; 765 case STOP_CALL_STREAMING: 766 transitionTo(calculateProperStateFromArgs(args)); 767 return HANDLED; 768 default: 769 // The forced focus switch commands are handled by BaseState. 770 return NOT_HANDLED; 771 } 772 } 773 } 774 775 /** 776 * This class is used for calls on hold and end-of-call tones. 777 */ 778 private class OtherFocusState extends BaseState { 779 @Override enter()780 public void enter() { 781 Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state"); 782 mLocalLog.log("Enter TONE/HOLDING"); 783 Log.i(this, "enter: AudioManager#requestAudioFocus(CALL)"); 784 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, 785 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 786 Log.i(this, "enter: AudioManager#setMode(%d)", mMostRecentMode); 787 mAudioManager.setMode(mMostRecentMode); 788 mLocalLog.log("Mode " + mMostRecentMode); 789 mCallAudioManager.setCallAudioRouteFocusStateForEndTone(); 790 } 791 792 @Override processMessage(Message msg)793 public boolean processMessage(Message msg) { 794 if (super.processMessage(msg) == HANDLED) { 795 return HANDLED; 796 } 797 MessageArgs args = (MessageArgs) msg.obj; 798 switch (msg.what) { 799 case NO_MORE_HOLDING_CALLS: 800 if (args.hasActiveOrDialingCalls) { 801 transitionTo(args.foregroundCallIsVoip 802 ? mVoipCallFocusState : mSimCallFocusState); 803 } else if (args.hasRingingCalls) { 804 transitionTo(mRingingFocusState); 805 } else if (!args.isTonePlaying) { 806 transitionTo(mUnfocusedState); 807 } 808 // Do nothing if a tone is playing. 809 return HANDLED; 810 case NEW_ACTIVE_OR_DIALING_CALL: 811 transitionTo(args.foregroundCallIsVoip 812 ? mVoipCallFocusState : mSimCallFocusState); 813 return HANDLED; 814 case NEW_RINGING_CALL: 815 // TODO: consider whether to move this into MessageArgs if more things start 816 // to use it. 817 if (args.hasHoldingCalls && mSystemStateHelper.isDeviceAtEar()) { 818 mCallAudioManager.startCallWaiting( 819 "Device is at ear with held call"); 820 } else { 821 transitionTo(mRingingFocusState); 822 } 823 return HANDLED; 824 case NEW_HOLDING_CALL: 825 // Do nothing. 826 return HANDLED; 827 case NO_MORE_RINGING_CALLS: 828 // If there are no more ringing calls in this state, then stop any call-waiting 829 // tones that may be playing. 830 mCallAudioManager.stopCallWaiting(); 831 return HANDLED; 832 case TONE_STOPPED_PLAYING: 833 transitionTo(calculateProperStateFromArgs(args)); 834 return HANDLED; 835 case AUDIO_OPERATIONS_COMPLETE: 836 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 837 + " state"); 838 return HANDLED; 839 default: 840 return NOT_HANDLED; 841 } 842 } 843 } 844 845 private static final String LOG_TAG = CallAudioModeStateMachine.class.getSimpleName(); 846 847 private final BaseState mUnfocusedState = new UnfocusedState(); 848 private final BaseState mRingingFocusState = new RingingFocusState(); 849 private final BaseState mSimCallFocusState = new SimCallFocusState(); 850 private final BaseState mVoipCallFocusState = new VoipCallFocusState(); 851 private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState(); 852 private final BaseState mStreamingFocusState = new StreamingFocusState(); 853 private final BaseState mOtherFocusState = new OtherFocusState(); 854 855 private final AudioManager mAudioManager; 856 private final SystemStateHelper mSystemStateHelper; 857 private CallAudioManager mCallAudioManager; 858 private FeatureFlags mFeatureFlags; 859 private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker; 860 861 private int mMostRecentMode; 862 private boolean mIsInitialized = false; 863 CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager, FeatureFlags featureFlags, CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker)864 public CallAudioModeStateMachine(SystemStateHelper systemStateHelper, 865 AudioManager audioManager, FeatureFlags featureFlags, 866 CallAudioCommunicationDeviceTracker callAudioCommunicationDeviceTracker) { 867 super(CallAudioModeStateMachine.class.getSimpleName()); 868 mAudioManager = audioManager; 869 mSystemStateHelper = systemStateHelper; 870 mMostRecentMode = AudioManager.MODE_NORMAL; 871 mFeatureFlags = featureFlags; 872 mCommunicationDeviceTracker = callAudioCommunicationDeviceTracker; 873 874 createStates(); 875 } 876 877 /** 878 * Used for testing 879 */ CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager, Looper looper, FeatureFlags featureFlags, CallAudioCommunicationDeviceTracker communicationDeviceTracker)880 public CallAudioModeStateMachine(SystemStateHelper systemStateHelper, 881 AudioManager audioManager, Looper looper, FeatureFlags featureFlags, 882 CallAudioCommunicationDeviceTracker communicationDeviceTracker) { 883 super(CallAudioModeStateMachine.class.getSimpleName(), looper); 884 mAudioManager = audioManager; 885 mSystemStateHelper = systemStateHelper; 886 mMostRecentMode = AudioManager.MODE_NORMAL; 887 mFeatureFlags = featureFlags; 888 mCommunicationDeviceTracker = communicationDeviceTracker; 889 890 createStates(); 891 } 892 createStates()893 private void createStates() { 894 addState(mUnfocusedState); 895 addState(mRingingFocusState); 896 addState(mSimCallFocusState); 897 addState(mVoipCallFocusState); 898 addState(mAudioProcessingFocusState); 899 addState(mStreamingFocusState); 900 addState(mOtherFocusState); 901 setInitialState(mUnfocusedState); 902 start(); 903 sendMessage(INITIALIZE, new MessageArgs.Builder() 904 .setHasActiveOrDialingCalls(false) 905 .setHasRingingCalls(false) 906 .setHasHoldingCalls(false) 907 .setIsTonePlaying(false) 908 .setForegroundCallIsVoip(false) 909 .setIsStreaming(false) 910 .setSession(Log.createSubsession()) 911 .build()); 912 } 913 setCallAudioManager(CallAudioManager callAudioManager)914 public void setCallAudioManager(CallAudioManager callAudioManager) { 915 mCallAudioManager = callAudioManager; 916 } 917 getCurrentStateName()918 public String getCurrentStateName() { 919 IState currentState = getCurrentState(); 920 return currentState == null ? "no state" : currentState.getName(); 921 } 922 sendMessageWithArgs(int messageCode, MessageArgs args)923 public void sendMessageWithArgs(int messageCode, MessageArgs args) { 924 sendMessage(messageCode, args); 925 } 926 927 @Override onPreHandleMessage(Message msg)928 protected void onPreHandleMessage(Message msg) { 929 if (msg.obj != null && msg.obj instanceof MessageArgs) { 930 Log.continueSession(((MessageArgs) msg.obj).session, "CAMSM.pM_" + msg.what); 931 Log.i(LOG_TAG, "Message received: %s.", MESSAGE_CODE_TO_NAME.get(msg.what)); 932 } else if (msg.what == RUN_RUNNABLE && msg.obj instanceof Runnable) { 933 Log.i(LOG_TAG, "Running runnable for testing"); 934 } else { 935 Log.w(LOG_TAG, "Message sent must be of type nonnull MessageArgs, but got " + 936 (msg.obj == null ? "null" : msg.obj.getClass().getSimpleName())); 937 Log.w(LOG_TAG, "The message was of code %d = %s", 938 msg.what, MESSAGE_CODE_TO_NAME.get(msg.what)); 939 } 940 } 941 dumpPendingMessages(IndentingPrintWriter pw)942 public void dumpPendingMessages(IndentingPrintWriter pw) { 943 getHandler().getLooper().dump(pw::println, ""); 944 } 945 dump(IndentingPrintWriter pw)946 public void dump(IndentingPrintWriter pw) { 947 pw.println("History:"); 948 mLocalLog.dump(pw); 949 pw.println("Pending Msg:"); 950 dumpPendingMessages(pw); 951 } 952 953 @Override onPostHandleMessage(Message msg)954 protected void onPostHandleMessage(Message msg) { 955 Log.endSession(); 956 } 957 calculateProperStateFromArgs(MessageArgs args)958 private BaseState calculateProperStateFromArgs(MessageArgs args) { 959 // If there are active, audio-processing, holding, or ringing calls, 960 // switch to the appropriate focus. 961 // Otherwise abandon focus. 962 963 // The order matters here. If there is streaming call, holding streaming route for them 964 // takes priority. After that, holding focus for active calls takes priority. After that, we 965 // want to prioritize holding calls over ringing calls so that when a call-waiting call gets 966 // answered, there's no transition in and out of the ringing focus state. After that, we 967 // want tones since we actually hold focus during them, then the audio processing state 968 // because that will release focus. 969 if (args.isStreaming) { 970 return mSimCallFocusState; 971 } else if (args.hasActiveOrDialingCalls) { 972 if (args.foregroundCallIsVoip) { 973 return mVoipCallFocusState; 974 } else { 975 return mSimCallFocusState; 976 } 977 } else if (args.hasHoldingCalls) { 978 return mOtherFocusState; 979 } else if (args.hasRingingCalls) { 980 return mRingingFocusState; 981 } else if (args.isTonePlaying) { 982 return mOtherFocusState; 983 } else if (args.hasAudioProcessingCalls) { 984 return mAudioProcessingFocusState; 985 } 986 return mUnfocusedState; 987 } 988 989 } 990