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