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