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