Home | History | Annotate | Download | only in telecom
      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.Message;
     21 import android.telecom.Log;
     22 import android.telecom.Logging.Runnable;
     23 import android.telecom.Logging.Session;
     24 import android.util.SparseArray;
     25 
     26 import com.android.internal.util.IState;
     27 import com.android.internal.util.IndentingPrintWriter;
     28 import com.android.internal.util.State;
     29 import com.android.internal.util.StateMachine;
     30 
     31 public class CallAudioModeStateMachine extends StateMachine {
     32     public static class MessageArgs {
     33         public boolean hasActiveOrDialingCalls;
     34         public boolean hasRingingCalls;
     35         public boolean hasHoldingCalls;
     36         public boolean isTonePlaying;
     37         public boolean foregroundCallIsVoip;
     38         public Session session;
     39 
     40         public MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
     41                 boolean hasHoldingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip,
     42                 Session session) {
     43             this.hasActiveOrDialingCalls = hasActiveOrDialingCalls;
     44             this.hasRingingCalls = hasRingingCalls;
     45             this.hasHoldingCalls = hasHoldingCalls;
     46             this.isTonePlaying = isTonePlaying;
     47             this.foregroundCallIsVoip = foregroundCallIsVoip;
     48             this.session = session;
     49         }
     50 
     51         public MessageArgs() {
     52             this.session = Log.createSubsession();
     53         }
     54 
     55         @Override
     56         public String toString() {
     57             return "MessageArgs{" +
     58                     "hasActiveCalls=" + hasActiveOrDialingCalls +
     59                     ", hasRingingCalls=" + hasRingingCalls +
     60                     ", hasHoldingCalls=" + hasHoldingCalls +
     61                     ", isTonePlaying=" + isTonePlaying +
     62                     ", foregroundCallIsVoip=" + foregroundCallIsVoip +
     63                     ", session=" + session +
     64                     '}';
     65         }
     66     }
     67 
     68     public static final int INITIALIZE = 1;
     69     // These ENTER_*_FOCUS commands are for testing.
     70     public static final int ENTER_CALL_FOCUS_FOR_TESTING = 2;
     71     public static final int ENTER_COMMS_FOCUS_FOR_TESTING = 3;
     72     public static final int ENTER_RING_FOCUS_FOR_TESTING = 4;
     73     public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5;
     74     public static final int ABANDON_FOCUS_FOR_TESTING = 6;
     75 
     76     public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001;
     77     public static final int NO_MORE_RINGING_CALLS = 1002;
     78     public static final int NO_MORE_HOLDING_CALLS = 1003;
     79 
     80     public static final int NEW_ACTIVE_OR_DIALING_CALL = 2001;
     81     public static final int NEW_RINGING_CALL = 2002;
     82     public static final int NEW_HOLDING_CALL = 2003;
     83     public static final int MT_AUDIO_SPEEDUP_FOR_RINGING_CALL = 2004;
     84 
     85     public static final int TONE_STARTED_PLAYING = 3001;
     86     public static final int TONE_STOPPED_PLAYING = 3002;
     87 
     88     public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001;
     89 
     90     public static final int RINGER_MODE_CHANGE = 5001;
     91 
     92     public static final int RUN_RUNNABLE = 9001;
     93 
     94     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
     95         put(ENTER_CALL_FOCUS_FOR_TESTING, "ENTER_CALL_FOCUS_FOR_TESTING");
     96         put(ENTER_COMMS_FOCUS_FOR_TESTING, "ENTER_COMMS_FOCUS_FOR_TESTING");
     97         put(ENTER_RING_FOCUS_FOR_TESTING, "ENTER_RING_FOCUS_FOR_TESTING");
     98         put(ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, "ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING");
     99         put(ABANDON_FOCUS_FOR_TESTING, "ABANDON_FOCUS_FOR_TESTING");
    100         put(NO_MORE_ACTIVE_OR_DIALING_CALLS, "NO_MORE_ACTIVE_OR_DIALING_CALLS");
    101         put(NO_MORE_RINGING_CALLS, "NO_MORE_RINGING_CALLS");
    102         put(NO_MORE_HOLDING_CALLS, "NO_MORE_HOLDING_CALLS");
    103         put(NEW_ACTIVE_OR_DIALING_CALL, "NEW_ACTIVE_OR_DIALING_CALL");
    104         put(NEW_RINGING_CALL, "NEW_RINGING_CALL");
    105         put(NEW_HOLDING_CALL, "NEW_HOLDING_CALL");
    106         put(MT_AUDIO_SPEEDUP_FOR_RINGING_CALL, "MT_AUDIO_SPEEDUP_FOR_RINGING_CALL");
    107         put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING");
    108         put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING");
    109         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
    110         put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE");
    111 
    112         put(RUN_RUNNABLE, "RUN_RUNNABLE");
    113     }};
    114 
    115     public static final String TONE_HOLD_STATE_NAME = OtherFocusState.class.getSimpleName();
    116     public static final String UNFOCUSED_STATE_NAME = UnfocusedState.class.getSimpleName();
    117     public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName();
    118     public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName();
    119     public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName();
    120 
    121     private class BaseState extends State {
    122         @Override
    123         public boolean processMessage(Message msg) {
    124             switch (msg.what) {
    125                 case ENTER_CALL_FOCUS_FOR_TESTING:
    126                     transitionTo(mSimCallFocusState);
    127                     return HANDLED;
    128                 case ENTER_COMMS_FOCUS_FOR_TESTING:
    129                     transitionTo(mVoipCallFocusState);
    130                     return HANDLED;
    131                 case ENTER_RING_FOCUS_FOR_TESTING:
    132                     transitionTo(mRingingFocusState);
    133                     return HANDLED;
    134                 case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING:
    135                     transitionTo(mOtherFocusState);
    136                     return HANDLED;
    137                 case ABANDON_FOCUS_FOR_TESTING:
    138                     transitionTo(mUnfocusedState);
    139                     return HANDLED;
    140                 case INITIALIZE:
    141                     mIsInitialized = true;
    142                     return HANDLED;
    143                 case RUN_RUNNABLE:
    144                     java.lang.Runnable r = (java.lang.Runnable) msg.obj;
    145                     r.run();
    146                     return HANDLED;
    147                 default:
    148                     return NOT_HANDLED;
    149             }
    150         }
    151     }
    152 
    153     private class UnfocusedState extends BaseState {
    154         @Override
    155         public void enter() {
    156             if (mIsInitialized) {
    157                 Log.i(LOG_TAG, "Abandoning audio focus: now UNFOCUSED");
    158                 mAudioManager.abandonAudioFocusForCall();
    159                 mAudioManager.setMode(AudioManager.MODE_NORMAL);
    160 
    161                 mMostRecentMode = AudioManager.MODE_NORMAL;
    162                 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
    163             }
    164         }
    165 
    166         @Override
    167         public boolean processMessage(Message msg) {
    168             if (super.processMessage(msg) == HANDLED) {
    169                 return HANDLED;
    170             }
    171             MessageArgs args = (MessageArgs) msg.obj;
    172             switch (msg.what) {
    173                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
    174                     // Do nothing.
    175                     return HANDLED;
    176                 case NO_MORE_RINGING_CALLS:
    177                     // Do nothing.
    178                     return HANDLED;
    179                 case NO_MORE_HOLDING_CALLS:
    180                     // Do nothing.
    181                     return HANDLED;
    182                 case NEW_ACTIVE_OR_DIALING_CALL:
    183                     transitionTo(args.foregroundCallIsVoip
    184                             ? mVoipCallFocusState : mSimCallFocusState);
    185                     return HANDLED;
    186                 case NEW_RINGING_CALL:
    187                     transitionTo(mRingingFocusState);
    188                     return HANDLED;
    189                 case NEW_HOLDING_CALL:
    190                     // This really shouldn't happen, but transition to the focused state anyway.
    191                     Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
    192                             " Args are: \n" + args.toString());
    193                     transitionTo(mOtherFocusState);
    194                     return HANDLED;
    195                 case TONE_STARTED_PLAYING:
    196                     // This shouldn't happen either, but perform the action anyway.
    197                     Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
    198                             + args.toString());
    199                     return HANDLED;
    200                 default:
    201                     // The forced focus switch commands are handled by BaseState.
    202                     return NOT_HANDLED;
    203             }
    204         }
    205     }
    206 
    207     private class RingingFocusState extends BaseState {
    208         private void tryStartRinging() {
    209             if (mCallAudioManager.startRinging()) {
    210                 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
    211                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    212                 mAudioManager.setMode(AudioManager.MODE_RINGTONE);
    213                 mCallAudioManager.setCallAudioRouteFocusState(
    214                         CallAudioRouteStateMachine.RINGING_FOCUS);
    215             } else {
    216                 Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
    217             }
    218         }
    219 
    220         @Override
    221         public void enter() {
    222             Log.i(LOG_TAG, "Audio focus entering RINGING state");
    223             tryStartRinging();
    224             mCallAudioManager.stopCallWaiting();
    225         }
    226 
    227         @Override
    228         public void exit() {
    229             // Audio mode and audio stream will be set by the next state.
    230             mCallAudioManager.stopRinging();
    231         }
    232 
    233         @Override
    234         public boolean processMessage(Message msg) {
    235             if (super.processMessage(msg) == HANDLED) {
    236                 return HANDLED;
    237             }
    238             MessageArgs args = (MessageArgs) msg.obj;
    239             switch (msg.what) {
    240                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
    241                     // Do nothing. Loss of an active call should not impact ringer.
    242                     return HANDLED;
    243                 case NO_MORE_HOLDING_CALLS:
    244                     // Do nothing and keep ringing.
    245                     return HANDLED;
    246                 case NO_MORE_RINGING_CALLS:
    247                     // If there are active or holding calls, switch to the appropriate focus.
    248                     // Otherwise abandon focus.
    249                     if (args.hasActiveOrDialingCalls) {
    250                         if (args.foregroundCallIsVoip) {
    251                             transitionTo(mVoipCallFocusState);
    252                         } else {
    253                             transitionTo(mSimCallFocusState);
    254                         }
    255                     } else if (args.hasHoldingCalls || args.isTonePlaying) {
    256                         transitionTo(mOtherFocusState);
    257                     } else {
    258                         transitionTo(mUnfocusedState);
    259                     }
    260                     return HANDLED;
    261                 case NEW_ACTIVE_OR_DIALING_CALL:
    262                     // If a call becomes active suddenly, give it priority over ringing.
    263                     transitionTo(args.foregroundCallIsVoip
    264                             ? mVoipCallFocusState : mSimCallFocusState);
    265                     return HANDLED;
    266                 case NEW_RINGING_CALL:
    267                     Log.w(LOG_TAG, "Unexpected behavior! New ringing call appeared while in " +
    268                             "ringing state.");
    269                     return HANDLED;
    270                 case NEW_HOLDING_CALL:
    271                     // This really shouldn't happen, but transition to the focused state anyway.
    272                     Log.w(LOG_TAG, "Call was surprisingly put into hold while ringing." +
    273                             " Args are: " + args.toString());
    274                     transitionTo(mOtherFocusState);
    275                     return HANDLED;
    276                 case MT_AUDIO_SPEEDUP_FOR_RINGING_CALL:
    277                     // This happens when an IMS call is answered by the in-call UI. Special case
    278                     // that we have to deal with for some reason.
    279 
    280                     // The IMS audio routing may be via modem or via RTP stream. In case via RTP
    281                     // stream, the state machine should transit to mVoipCallFocusState.
    282                     transitionTo(args.foregroundCallIsVoip
    283                             ? mVoipCallFocusState : mSimCallFocusState);
    284                     return HANDLED;
    285                 case RINGER_MODE_CHANGE: {
    286                     Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE");
    287                     tryStartRinging();
    288                     return HANDLED;
    289                 }
    290                 default:
    291                     // The forced focus switch commands are handled by BaseState.
    292                     return NOT_HANDLED;
    293             }
    294         }
    295     }
    296 
    297     private class SimCallFocusState extends BaseState {
    298         @Override
    299         public void enter() {
    300             Log.i(LOG_TAG, "Audio focus entering SIM CALL state");
    301             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
    302                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    303             mAudioManager.setMode(AudioManager.MODE_IN_CALL);
    304             mMostRecentMode = AudioManager.MODE_IN_CALL;
    305             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
    306         }
    307 
    308         @Override
    309         public boolean processMessage(Message msg) {
    310             if (super.processMessage(msg) == HANDLED) {
    311                 return HANDLED;
    312             }
    313             MessageArgs args = (MessageArgs) msg.obj;
    314             switch (msg.what) {
    315                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
    316                     // Switch to either ringing, holding, or inactive
    317                     transitionTo(destinationStateAfterNoMoreActiveCalls(args));
    318                     return HANDLED;
    319                 case NO_MORE_RINGING_CALLS:
    320                     // Don't transition state, but stop any call-waiting tones that may have been
    321                     // playing.
    322                     if (args.isTonePlaying) {
    323                         mCallAudioManager.stopCallWaiting();
    324                     }
    325                     // If a MT-audio-speedup call gets disconnected by the connection service
    326                     // concurrently with the user answering it, we may get this message
    327                     // indicating that a ringing call has disconnected while this state machine
    328                     // is in the SimCallFocusState.
    329                     if (!args.hasActiveOrDialingCalls) {
    330                         transitionTo(destinationStateAfterNoMoreActiveCalls(args));
    331                     }
    332                     return HANDLED;
    333                 case NO_MORE_HOLDING_CALLS:
    334                     // Do nothing.
    335                     return HANDLED;
    336                 case NEW_ACTIVE_OR_DIALING_CALL:
    337                     // Do nothing. Already active.
    338                     return HANDLED;
    339                 case NEW_RINGING_CALL:
    340                     // Don't make a call ring over an active call, but do play a call waiting tone.
    341                     mCallAudioManager.startCallWaiting();
    342                     return HANDLED;
    343                 case NEW_HOLDING_CALL:
    344                     // Don't do anything now. Putting an active call on hold will be handled when
    345                     // NO_MORE_ACTIVE_CALLS is processed.
    346                     return HANDLED;
    347                 case FOREGROUND_VOIP_MODE_CHANGE:
    348                     if (args.foregroundCallIsVoip) {
    349                         transitionTo(mVoipCallFocusState);
    350                     }
    351                     return HANDLED;
    352                 default:
    353                     // The forced focus switch commands are handled by BaseState.
    354                     return NOT_HANDLED;
    355             }
    356         }
    357     }
    358 
    359     private class VoipCallFocusState extends BaseState {
    360         @Override
    361         public void enter() {
    362             Log.i(LOG_TAG, "Audio focus entering VOIP CALL state");
    363             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
    364                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    365             mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
    366             mMostRecentMode = AudioManager.MODE_IN_COMMUNICATION;
    367             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
    368         }
    369 
    370         @Override
    371         public boolean processMessage(Message msg) {
    372             if (super.processMessage(msg) == HANDLED) {
    373                 return HANDLED;
    374             }
    375             MessageArgs args = (MessageArgs) msg.obj;
    376             switch (msg.what) {
    377                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
    378                     // Switch to either ringing, holding, or inactive
    379                     transitionTo(destinationStateAfterNoMoreActiveCalls(args));
    380                     return HANDLED;
    381                 case NO_MORE_RINGING_CALLS:
    382                     // Don't transition state, but stop any call-waiting tones that may have been
    383                     // playing.
    384                     if (args.isTonePlaying) {
    385                         mCallAudioManager.stopCallWaiting();
    386                     }
    387                     return HANDLED;
    388                 case NO_MORE_HOLDING_CALLS:
    389                     // Do nothing.
    390                     return HANDLED;
    391                 case NEW_ACTIVE_OR_DIALING_CALL:
    392                     // Do nothing. Already active.
    393                     return HANDLED;
    394                 case NEW_RINGING_CALL:
    395                     // Don't make a call ring over an active call, but do play a call waiting tone.
    396                     mCallAudioManager.startCallWaiting();
    397                     return HANDLED;
    398                 case NEW_HOLDING_CALL:
    399                     // Don't do anything now. Putting an active call on hold will be handled when
    400                     // NO_MORE_ACTIVE_CALLS is processed.
    401                     return HANDLED;
    402                 case FOREGROUND_VOIP_MODE_CHANGE:
    403                     if (!args.foregroundCallIsVoip) {
    404                         transitionTo(mSimCallFocusState);
    405                     }
    406                     return HANDLED;
    407                 default:
    408                     // The forced focus switch commands are handled by BaseState.
    409                     return NOT_HANDLED;
    410             }
    411         }
    412     }
    413 
    414     /**
    415      * This class is used for calls on hold and end-of-call tones.
    416      */
    417     private class OtherFocusState extends BaseState {
    418         @Override
    419         public void enter() {
    420             Log.i(LOG_TAG, "Audio focus entering TONE/HOLDING state");
    421             mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
    422                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    423             mAudioManager.setMode(mMostRecentMode);
    424             mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.ACTIVE_FOCUS);
    425         }
    426 
    427         @Override
    428         public boolean processMessage(Message msg) {
    429             if (super.processMessage(msg) == HANDLED) {
    430                 return HANDLED;
    431             }
    432             MessageArgs args = (MessageArgs) msg.obj;
    433             switch (msg.what) {
    434                 case NO_MORE_HOLDING_CALLS:
    435                     if (args.hasActiveOrDialingCalls) {
    436                         transitionTo(args.foregroundCallIsVoip
    437                                 ? mVoipCallFocusState : mSimCallFocusState);
    438                     } else if (args.hasRingingCalls) {
    439                         transitionTo(mRingingFocusState);
    440                     } else if (!args.isTonePlaying) {
    441                         transitionTo(mUnfocusedState);
    442                     }
    443                     // Do nothing if a tone is playing.
    444                     return HANDLED;
    445                 case NEW_ACTIVE_OR_DIALING_CALL:
    446                     transitionTo(args.foregroundCallIsVoip
    447                             ? mVoipCallFocusState : mSimCallFocusState);
    448                     return HANDLED;
    449                 case NEW_RINGING_CALL:
    450                     // Apparently this is current behavior. Should this be the case?
    451                     transitionTo(mRingingFocusState);
    452                     return HANDLED;
    453                 case NEW_HOLDING_CALL:
    454                     // Do nothing.
    455                     return HANDLED;
    456                 case NO_MORE_RINGING_CALLS:
    457                     // If there are no more ringing calls in this state, then stop any call-waiting
    458                     // tones that may be playing.
    459                     mCallAudioManager.stopCallWaiting();
    460                     return HANDLED;
    461                 case TONE_STOPPED_PLAYING:
    462                     transitionTo(destinationStateAfterNoMoreActiveCalls(args));
    463                 default:
    464                     return NOT_HANDLED;
    465             }
    466         }
    467     }
    468 
    469     private static final String LOG_TAG = CallAudioModeStateMachine.class.getSimpleName();
    470 
    471     private final BaseState mUnfocusedState = new UnfocusedState();
    472     private final BaseState mRingingFocusState = new RingingFocusState();
    473     private final BaseState mSimCallFocusState = new SimCallFocusState();
    474     private final BaseState mVoipCallFocusState = new VoipCallFocusState();
    475     private final BaseState mOtherFocusState = new OtherFocusState();
    476 
    477     private final AudioManager mAudioManager;
    478     private CallAudioManager mCallAudioManager;
    479 
    480     private int mMostRecentMode;
    481     private boolean mIsInitialized = false;
    482 
    483     public CallAudioModeStateMachine(AudioManager audioManager) {
    484         super(CallAudioModeStateMachine.class.getSimpleName());
    485         mAudioManager = audioManager;
    486         mMostRecentMode = AudioManager.MODE_NORMAL;
    487 
    488         addState(mUnfocusedState);
    489         addState(mRingingFocusState);
    490         addState(mSimCallFocusState);
    491         addState(mVoipCallFocusState);
    492         addState(mOtherFocusState);
    493         setInitialState(mUnfocusedState);
    494         start();
    495         sendMessage(INITIALIZE, new MessageArgs());
    496     }
    497 
    498     public void setCallAudioManager(CallAudioManager callAudioManager) {
    499         mCallAudioManager = callAudioManager;
    500     }
    501 
    502     public String getCurrentStateName() {
    503         IState currentState = getCurrentState();
    504         return currentState == null ? "no state" : currentState.getName();
    505     }
    506 
    507     public void sendMessageWithArgs(int messageCode, MessageArgs args) {
    508         sendMessage(messageCode, args);
    509     }
    510 
    511     @Override
    512     protected void onPreHandleMessage(Message msg) {
    513         if (msg.obj != null && msg.obj instanceof MessageArgs) {
    514             Log.continueSession(((MessageArgs) msg.obj).session, "CAMSM.pM_" + msg.what);
    515             Log.i(LOG_TAG, "Message received: %s.", MESSAGE_CODE_TO_NAME.get(msg.what));
    516         } else if (msg.what == RUN_RUNNABLE && msg.obj instanceof Runnable) {
    517             Log.i(LOG_TAG, "Running runnable for testing");
    518         } else {
    519                 Log.w(LOG_TAG, "Message sent must be of type nonnull MessageArgs, but got " +
    520                         (msg.obj == null ? "null" : msg.obj.getClass().getSimpleName()));
    521                 Log.w(LOG_TAG, "The message was of code %d = %s",
    522                         msg.what, MESSAGE_CODE_TO_NAME.get(msg.what));
    523         }
    524     }
    525 
    526     public void dumpPendingMessages(IndentingPrintWriter pw) {
    527         getHandler().getLooper().dump(pw::println, "");
    528     }
    529 
    530     @Override
    531     protected void onPostHandleMessage(Message msg) {
    532         Log.endSession();
    533     }
    534 
    535     private BaseState destinationStateAfterNoMoreActiveCalls(MessageArgs args) {
    536         if (args.hasHoldingCalls) {
    537             return mOtherFocusState;
    538         } else if (args.hasRingingCalls) {
    539             return mRingingFocusState;
    540         } else if (args.isTonePlaying) {
    541             return mOtherFocusState;
    542         } else {
    543             return mUnfocusedState;
    544         }
    545     }
    546 }
    547