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 { tryStartRinging()356 private void tryStartRinging() { 357 if (mCallAudioManager.startRinging()) { 358 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING, 359 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 360 // Do not set MODE_RINGTONE if we were previously in the CALL_SCREENING mode -- this 361 // trips up the audio system. 362 if (mAudioManager.getMode() != NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING) { 363 mAudioManager.setMode(AudioManager.MODE_RINGTONE); 364 } 365 mCallAudioManager.setCallAudioRouteFocusState( 366 CallAudioRouteStateMachine.RINGING_FOCUS); 367 } else { 368 Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus"); 369 } 370 } 371 372 @Override enter()373 public void enter() { 374 Log.i(LOG_TAG, "Audio focus entering RINGING state"); 375 tryStartRinging(); 376 mCallAudioManager.stopCallWaiting(); 377 } 378 379 @Override exit()380 public void exit() { 381 // Audio mode and audio stream will be set by the next state. 382 mCallAudioManager.stopRinging(); 383 } 384 385 @Override processMessage(Message msg)386 public boolean processMessage(Message msg) { 387 if (super.processMessage(msg) == HANDLED) { 388 return HANDLED; 389 } 390 MessageArgs args = (MessageArgs) msg.obj; 391 switch (msg.what) { 392 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 393 // Do nothing. Loss of an active call should not impact ringer. 394 return HANDLED; 395 case NO_MORE_HOLDING_CALLS: 396 // Do nothing and keep ringing. 397 return HANDLED; 398 case NO_MORE_RINGING_CALLS: 399 BaseState destState = calculateProperStateFromArgs(args); 400 if (destState == this) { 401 Log.w(LOG_TAG, "Got spurious NO_MORE_RINGING_CALLS"); 402 } 403 transitionTo(destState); 404 return HANDLED; 405 case NEW_ACTIVE_OR_DIALING_CALL: 406 // If a call becomes active suddenly, give it priority over ringing. 407 transitionTo(args.foregroundCallIsVoip 408 ? mVoipCallFocusState : mSimCallFocusState); 409 return HANDLED; 410 case NEW_AUDIO_PROCESSING_CALL: 411 // If we don't have any more ringing calls, transition to audio processing. 412 if (!args.hasRingingCalls) { 413 transitionTo(mAudioProcessingFocusState); 414 } else { 415 Log.w(LOG_TAG, "Got a audio processing call while there's still a call " 416 + "ringing"); 417 } 418 case NEW_RINGING_CALL: 419 // Can happen as a duplicate message 420 return HANDLED; 421 case NEW_HOLDING_CALL: 422 // This really shouldn't happen, but transition to the focused state anyway. 423 Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." + 424 " Args are: " + args.toString()); 425 transitionTo(mOtherFocusState); 426 return HANDLED; 427 case RINGER_MODE_CHANGE: { 428 Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE"); 429 tryStartRinging(); 430 return HANDLED; 431 } 432 case AUDIO_OPERATIONS_COMPLETE: 433 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 434 + " state"); 435 return HANDLED; 436 default: 437 // The forced focus switch commands are handled by BaseState. 438 return NOT_HANDLED; 439 } 440 } 441 } 442 443 private class SimCallFocusState extends BaseState { 444 @Override enter()445 public void enter() { 446 Log.i(LOG_TAG, "Audio focus entering SIM CALL state"); 447 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, 448 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 449 mAudioManager.setMode(AudioManager.MODE_IN_CALL); 450 mMostRecentMode = AudioManager.MODE_IN_CALL; 451 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS); 452 } 453 454 @Override processMessage(Message msg)455 public boolean processMessage(Message msg) { 456 if (super.processMessage(msg) == HANDLED) { 457 return HANDLED; 458 } 459 MessageArgs args = (MessageArgs) msg.obj; 460 switch (msg.what) { 461 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 462 // Switch to either ringing, holding, or inactive 463 transitionTo(calculateProperStateFromArgs(args)); 464 return HANDLED; 465 case NO_MORE_RINGING_CALLS: 466 // Don't transition state, but stop any call-waiting tones that may have been 467 // playing. 468 if (args.isTonePlaying) { 469 mCallAudioManager.stopCallWaiting(); 470 } 471 // If a MT-audio-speedup call gets disconnected by the connection service 472 // concurrently with the user answering it, we may get this message 473 // indicating that a ringing call has disconnected while this state machine 474 // is in the SimCallFocusState. 475 if (!args.hasActiveOrDialingCalls) { 476 transitionTo(calculateProperStateFromArgs(args)); 477 } 478 return HANDLED; 479 case NO_MORE_HOLDING_CALLS: 480 if (args.foregroundCallIsVoip) { 481 transitionTo(mVoipCallFocusState); 482 } 483 return HANDLED; 484 case NEW_ACTIVE_OR_DIALING_CALL: 485 if (args.foregroundCallIsVoip) { 486 transitionTo(mVoipCallFocusState); 487 } 488 return HANDLED; 489 case NEW_RINGING_CALL: 490 // Don't make a call ring over an active call, but do play a call waiting tone. 491 mCallAudioManager.startCallWaiting("call already active"); 492 return HANDLED; 493 case NEW_HOLDING_CALL: 494 // Just check the voip mode. Putting an active call on hold will be handled when 495 // NO_MORE_ACTIVE_CALLS is processed. 496 if (args.foregroundCallIsVoip) { 497 transitionTo(mVoipCallFocusState); 498 } 499 return HANDLED; 500 case NEW_AUDIO_PROCESSING_CALL: 501 // If we don't have any more active calls, transition to audio processing. 502 if (!args.hasActiveOrDialingCalls) { 503 transitionTo(mAudioProcessingFocusState); 504 } else { 505 Log.w(LOG_TAG, "Got a audio processing call while there's still a call " 506 + "active"); 507 } 508 case FOREGROUND_VOIP_MODE_CHANGE: 509 if (args.foregroundCallIsVoip) { 510 transitionTo(mVoipCallFocusState); 511 } 512 return HANDLED; 513 case AUDIO_OPERATIONS_COMPLETE: 514 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 515 + " state"); 516 return HANDLED; 517 default: 518 // The forced focus switch commands are handled by BaseState. 519 return NOT_HANDLED; 520 } 521 } 522 } 523 524 private class VoipCallFocusState extends BaseState { 525 @Override enter()526 public void enter() { 527 Log.i(LOG_TAG, "Audio focus entering VOIP CALL state"); 528 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, 529 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 530 mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); 531 mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION; 532 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS); 533 } 534 535 @Override processMessage(Message msg)536 public boolean processMessage(Message msg) { 537 if (super.processMessage(msg) == HANDLED) { 538 return HANDLED; 539 } 540 MessageArgs args = (MessageArgs) msg.obj; 541 switch (msg.what) { 542 case NO_MORE_ACTIVE_OR_DIALING_CALLS: 543 // Switch to either ringing, holding, or inactive 544 transitionTo(calculateProperStateFromArgs(args)); 545 return HANDLED; 546 case NO_MORE_RINGING_CALLS: 547 // Don't transition state, but stop any call-waiting tones that may have been 548 // playing. 549 if (args.isTonePlaying) { 550 mCallAudioManager.stopCallWaiting(); 551 } 552 return HANDLED; 553 case NO_MORE_HOLDING_CALLS: 554 if (!args.foregroundCallIsVoip) { 555 transitionTo(mSimCallFocusState); 556 } 557 return HANDLED; 558 case NEW_ACTIVE_OR_DIALING_CALL: 559 if (!args.foregroundCallIsVoip) { 560 transitionTo(mSimCallFocusState); 561 } 562 return HANDLED; 563 case NEW_RINGING_CALL: 564 // Don't make a call ring over an active call, but do play a call waiting tone. 565 mCallAudioManager.startCallWaiting("call already active"); 566 return HANDLED; 567 case NEW_HOLDING_CALL: 568 // Just check the voip mode. Putting an active call on hold will be handled when 569 // NO_MORE_ACTIVE_CALLS is processed. 570 if (!args.foregroundCallIsVoip) { 571 transitionTo(mSimCallFocusState); 572 } 573 return HANDLED; 574 case NEW_AUDIO_PROCESSING_CALL: 575 // If we don't have any more active calls, transition to audio processing. 576 if (!args.hasActiveOrDialingCalls) { 577 transitionTo(mAudioProcessingFocusState); 578 } else { 579 Log.w(LOG_TAG, "Got a audio processing call while there's still a call " 580 + "active"); 581 } 582 case FOREGROUND_VOIP_MODE_CHANGE: 583 if (!args.foregroundCallIsVoip) { 584 transitionTo(mSimCallFocusState); 585 } 586 return HANDLED; 587 case AUDIO_OPERATIONS_COMPLETE: 588 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 589 + " state"); 590 return HANDLED; 591 default: 592 // The forced focus switch commands are handled by BaseState. 593 return NOT_HANDLED; 594 } 595 } 596 } 597 598 /** 599 * This class is used for calls on hold and end-of-call tones. 600 */ 601 private class OtherFocusState extends BaseState { 602 @Override enter()603 public void enter() { 604 Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state"); 605 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL, 606 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 607 mAudioManager.setMode(mMostRecentMode); 608 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS); 609 } 610 611 @Override processMessage(Message msg)612 public boolean processMessage(Message msg) { 613 if (super.processMessage(msg) == HANDLED) { 614 return HANDLED; 615 } 616 MessageArgs args = (MessageArgs) msg.obj; 617 switch (msg.what) { 618 case NO_MORE_HOLDING_CALLS: 619 if (args.hasActiveOrDialingCalls) { 620 transitionTo(args.foregroundCallIsVoip 621 ? mVoipCallFocusState : mSimCallFocusState); 622 } else if (args.hasRingingCalls) { 623 transitionTo(mRingingFocusState); 624 } else if (!args.isTonePlaying) { 625 transitionTo(mUnfocusedState); 626 } 627 // Do nothing if a tone is playing. 628 return HANDLED; 629 case NEW_ACTIVE_OR_DIALING_CALL: 630 transitionTo(args.foregroundCallIsVoip 631 ? mVoipCallFocusState : mSimCallFocusState); 632 return HANDLED; 633 case NEW_RINGING_CALL: 634 // TODO: consider whether to move this into MessageArgs if more things start 635 // to use it. 636 if (args.hasHoldingCalls && mSystemStateHelper.isDeviceAtEar()) { 637 mCallAudioManager.startCallWaiting( 638 "Device is at ear with held call"); 639 } else { 640 transitionTo(mRingingFocusState); 641 } 642 return HANDLED; 643 case NEW_HOLDING_CALL: 644 // Do nothing. 645 return HANDLED; 646 case NO_MORE_RINGING_CALLS: 647 // If there are no more ringing calls in this state, then stop any call-waiting 648 // tones that may be playing. 649 mCallAudioManager.stopCallWaiting(); 650 return HANDLED; 651 case TONE_STOPPED_PLAYING: 652 transitionTo(calculateProperStateFromArgs(args)); 653 return HANDLED; 654 case AUDIO_OPERATIONS_COMPLETE: 655 Log.w(LOG_TAG, "Should not be seeing AUDIO_OPERATIONS_COMPLETE in a focused" 656 + " state"); 657 return HANDLED; 658 default: 659 return NOT_HANDLED; 660 } 661 } 662 } 663 664 private static final String LOG_TAG = CallAudioModeStateMachine.class.getSimpleName(); 665 666 private final BaseState mUnfocusedState = new UnfocusedState(); 667 private final BaseState mRingingFocusState = new RingingFocusState(); 668 private final BaseState mSimCallFocusState = new SimCallFocusState(); 669 private final BaseState mVoipCallFocusState = new VoipCallFocusState(); 670 private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState(); 671 private final BaseState mOtherFocusState = new OtherFocusState(); 672 673 private final AudioManager mAudioManager; 674 private final SystemStateHelper mSystemStateHelper; 675 private CallAudioManager mCallAudioManager; 676 677 private int mMostRecentMode; 678 private boolean mIsInitialized = false; 679 CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager)680 public CallAudioModeStateMachine(SystemStateHelper systemStateHelper, 681 AudioManager audioManager) { 682 super(CallAudioModeStateMachine.class.getSimpleName()); 683 mAudioManager = audioManager; 684 mSystemStateHelper = systemStateHelper; 685 mMostRecentMode = AudioManager.MODE_NORMAL; 686 687 createStates(); 688 } 689 690 /** 691 * Used for testing 692 */ CallAudioModeStateMachine(SystemStateHelper systemStateHelper, AudioManager audioManager, Looper looper)693 public CallAudioModeStateMachine(SystemStateHelper systemStateHelper, 694 AudioManager audioManager, Looper looper) { 695 super(CallAudioModeStateMachine.class.getSimpleName(), looper); 696 mAudioManager = audioManager; 697 mSystemStateHelper = systemStateHelper; 698 mMostRecentMode = AudioManager.MODE_NORMAL; 699 700 createStates(); 701 } 702 createStates()703 private void createStates() { 704 addState(mUnfocusedState); 705 addState(mRingingFocusState); 706 addState(mSimCallFocusState); 707 addState(mVoipCallFocusState); 708 addState(mAudioProcessingFocusState); 709 addState(mOtherFocusState); 710 setInitialState(mUnfocusedState); 711 start(); 712 sendMessage(INITIALIZE, new MessageArgs.Builder() 713 .setHasActiveOrDialingCalls(false) 714 .setHasRingingCalls(false) 715 .setHasHoldingCalls(false) 716 .setIsTonePlaying(false) 717 .setForegroundCallIsVoip(false) 718 .setSession(Log.createSubsession()) 719 .build()); 720 } 721 setCallAudioManager(CallAudioManager callAudioManager)722 public void setCallAudioManager(CallAudioManager callAudioManager) { 723 mCallAudioManager = callAudioManager; 724 } 725 getCurrentStateName()726 public String getCurrentStateName() { 727 IState currentState = getCurrentState(); 728 return currentState == null ? "no state" : currentState.getName(); 729 } 730 sendMessageWithArgs(int messageCode, MessageArgs args)731 public void sendMessageWithArgs(int messageCode, MessageArgs args) { 732 sendMessage(messageCode, args); 733 } 734 735 @Override onPreHandleMessage(Message msg)736 protected void onPreHandleMessage(Message msg) { 737 if (msg.obj != null && msg.obj instanceof MessageArgs) { 738 Log.continueSession(((MessageArgs) msg.obj).session, "CAMSM.pM_" + msg.what); 739 Log.i(LOG_TAG, "Message received: %s.", MESSAGE_CODE_TO_NAME.get(msg.what)); 740 } else if (msg.what == RUN_RUNNABLE && msg.obj instanceof Runnable) { 741 Log.i(LOG_TAG, "Running runnable for testing"); 742 } else { 743 Log.w(LOG_TAG, "Message sent must be of type nonnull MessageArgs, but got " + 744 (msg.obj == null ? "null" : msg.obj.getClass().getSimpleName())); 745 Log.w(LOG_TAG, "The message was of code %d = %s", 746 msg.what, MESSAGE_CODE_TO_NAME.get(msg.what)); 747 } 748 } 749 dumpPendingMessages(IndentingPrintWriter pw)750 public void dumpPendingMessages(IndentingPrintWriter pw) { 751 getHandler().getLooper().dump(pw::println, ""); 752 } 753 754 @Override onPostHandleMessage(Message msg)755 protected void onPostHandleMessage(Message msg) { 756 Log.endSession(); 757 } 758 calculateProperStateFromArgs(MessageArgs args)759 private BaseState calculateProperStateFromArgs(MessageArgs args) { 760 // If there are active, audio-processing, holding, or ringing calls, 761 // switch to the appropriate focus. 762 // Otherwise abandon focus. 763 764 // The order matters here. If there are active calls, holding focus for them takes priority. 765 // After that, we want to prioritize holding calls over ringing calls so that when a 766 // call-waiting call gets answered, there's no transition in and out of the ringing focus 767 // state. After that, we want tones since we actually hold focus during them, then the 768 // audio processing state because that will release focus. 769 if (args.hasActiveOrDialingCalls) { 770 if (args.foregroundCallIsVoip) { 771 return mVoipCallFocusState; 772 } else { 773 return mSimCallFocusState; 774 } 775 } else if (args.hasHoldingCalls) { 776 return mOtherFocusState; 777 } else if (args.hasRingingCalls) { 778 return mRingingFocusState; 779 } else if (args.isTonePlaying) { 780 return mOtherFocusState; 781 } else if (args.hasAudioProcessingCalls) { 782 return mAudioProcessingFocusState; 783 } 784 return mUnfocusedState; 785 } 786 787 } 788