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 { create(SystemStateHelper systemStateHelper, AudioManager am)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 MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls, boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip, Session session)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 toString()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 setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls)83 public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) { 84 mHasActiveOrDialingCalls = hasActiveOrDialingCalls; 85 return this; 86 } 87 setHasRingingCalls(boolean hasRingingCalls)88 public Builder setHasRingingCalls(boolean hasRingingCalls) { 89 mHasRingingCalls = hasRingingCalls; 90 return this; 91 } 92 setHasHoldingCalls(boolean hasHoldingCalls)93 public Builder setHasHoldingCalls(boolean hasHoldingCalls) { 94 mHasHoldingCalls = hasHoldingCalls; 95 return this; 96 } 97 setHasAudioProcessingCalls(boolean hasAudioProcessingCalls)98 public Builder setHasAudioProcessingCalls(boolean hasAudioProcessingCalls) { 99 mHasAudioProcessingCalls = hasAudioProcessingCalls; 100 return this; 101 } 102 setIsTonePlaying(boolean isTonePlaying)103 public Builder setIsTonePlaying(boolean isTonePlaying) { 104 mIsTonePlaying = isTonePlaying; 105 return this; 106 } 107 setForegroundCallIsVoip(boolean foregroundCallIsVoip)108 public Builder setForegroundCallIsVoip(boolean foregroundCallIsVoip) { 109 mForegroundCallIsVoip = foregroundCallIsVoip; 110 return this; 111 } 112 setSession(Session session)113 public Builder setSession(Session session) { 114 mSession = session; 115 return this; 116 } 117 build()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 processMessage(Message msg)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 enter()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 processMessage(Message msg)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 enter()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 processMessage(Message msg)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 tryStartRinging()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 enter()384 public void enter() { 385 Log.i(LOG_TAG, "Audio focus entering RINGING state"); 386 tryStartRinging(); 387 mCallAudioManager.stopCallWaiting(); 388 } 389 390 @Override exit()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 processMessage(Message msg)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 enter()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 processMessage(Message msg)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 enter()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 processMessage(Message msg)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 enter()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 processMessage(Message msg)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 CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager)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 */ CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager, Looper looper)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 createStates()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 setCallAudioManager(CallAudioManager callAudioManager)734 public void setCallAudioManager(CallAudioManager callAudioManager) { 735 mCallAudioManager = callAudioManager; 736 } 737 getCurrentStateName()738 public String getCurrentStateName() { 739 IState currentState = getCurrentState(); 740 return currentState == null ? "no state" : currentState.getName(); 741 } 742 sendMessageWithArgs(int messageCode, MessageArgs args)743 public void sendMessageWithArgs(int messageCode, MessageArgs args) { 744 sendMessage(messageCode, args); 745 } 746 747 @Override onPreHandleMessage(Message msg)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 dumpPendingMessages(IndentingPrintWriter pw)762 public void dumpPendingMessages(IndentingPrintWriter pw) { 763 getHandler().getLooper().dump(pw::println, ""); 764 } 765 766 @Override onPostHandleMessage(Message msg)767 protected void onPostHandleMessage(Message msg) { 768 Log.endSession(); 769 } 770 calculateProperStateFromArgs(MessageArgs args)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