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