Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright (C) 2014 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.content.Context;
     20 import android.media.AudioManager;
     21 import android.telecom.AudioState;
     22 import android.telecom.CallState;
     23 
     24 import com.android.internal.util.IndentingPrintWriter;
     25 import com.android.internal.util.Preconditions;
     26 
     27 import java.util.Objects;
     28 
     29 /**
     30  * This class manages audio modes, streams and other properties.
     31  */
     32 final class CallAudioManager extends CallsManagerListenerBase
     33         implements WiredHeadsetManager.Listener {
     34     private static final int STREAM_NONE = -1;
     35 
     36     private final StatusBarNotifier mStatusBarNotifier;
     37     private final AudioManager mAudioManager;
     38     private final BluetoothManager mBluetoothManager;
     39     private final WiredHeadsetManager mWiredHeadsetManager;
     40 
     41     private AudioState mAudioState;
     42     private int mAudioFocusStreamType;
     43     private boolean mIsRinging;
     44     private boolean mIsTonePlaying;
     45     private boolean mWasSpeakerOn;
     46     private int mMostRecentlyUsedMode = AudioManager.MODE_IN_CALL;
     47     private Call mCallToSpeedUpMTAudio = null;
     48 
     49     CallAudioManager(Context context, StatusBarNotifier statusBarNotifier,
     50             WiredHeadsetManager wiredHeadsetManager) {
     51         mStatusBarNotifier = statusBarNotifier;
     52         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
     53         mBluetoothManager = new BluetoothManager(context, this);
     54         mWiredHeadsetManager = wiredHeadsetManager;
     55         mWiredHeadsetManager.addListener(this);
     56 
     57         saveAudioState(getInitialAudioState(null));
     58         mAudioFocusStreamType = STREAM_NONE;
     59     }
     60 
     61     AudioState getAudioState() {
     62         return mAudioState;
     63     }
     64 
     65     @Override
     66     public void onCallAdded(Call call) {
     67         onCallUpdated(call);
     68 
     69         if (hasFocus() && getForegroundCall() == call) {
     70             if (!call.isIncoming()) {
     71                 // Unmute new outgoing call.
     72                 setSystemAudioState(false, mAudioState.getRoute(),
     73                         mAudioState.getSupportedRouteMask());
     74             }
     75         }
     76     }
     77 
     78     @Override
     79     public void onCallRemoved(Call call) {
     80         // If we didn't already have focus, there's nothing to do.
     81         if (hasFocus()) {
     82             if (CallsManager.getInstance().getCalls().isEmpty()) {
     83                 Log.v(this, "all calls removed, reseting system audio to default state");
     84                 setInitialAudioState(null, false /* force */);
     85                 mWasSpeakerOn = false;
     86             }
     87             updateAudioStreamAndMode();
     88         }
     89     }
     90 
     91     @Override
     92     public void onCallStateChanged(Call call, int oldState, int newState) {
     93         onCallUpdated(call);
     94     }
     95 
     96     @Override
     97     public void onIncomingCallAnswered(Call call) {
     98         int route = mAudioState.getRoute();
     99 
    100         // We do two things:
    101         // (1) If this is the first call, then we can to turn on bluetooth if available.
    102         // (2) Unmute the audio for the new incoming call.
    103         boolean isOnlyCall = CallsManager.getInstance().getCalls().size() == 1;
    104         if (isOnlyCall && mBluetoothManager.isBluetoothAvailable()) {
    105             mBluetoothManager.connectBluetoothAudio();
    106             route = AudioState.ROUTE_BLUETOOTH;
    107         }
    108 
    109         setSystemAudioState(false /* isMute */, route, mAudioState.getSupportedRouteMask());
    110 
    111         if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
    112             Log.v(this, "Speed up audio setup for IMS MT call.");
    113             mCallToSpeedUpMTAudio = call;
    114             updateAudioStreamAndMode();
    115         }
    116     }
    117 
    118     @Override
    119     public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
    120         onCallUpdated(newForegroundCall);
    121         // Ensure that the foreground call knows about the latest audio state.
    122         updateAudioForForegroundCall();
    123     }
    124 
    125     @Override
    126     public void onIsVoipAudioModeChanged(Call call) {
    127         updateAudioStreamAndMode();
    128     }
    129 
    130     /**
    131       * Updates the audio route when the headset plugged in state changes. For example, if audio is
    132       * being routed over speakerphone and a headset is plugged in then switch to wired headset.
    133       */
    134     @Override
    135     public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
    136         // This can happen even when there are no calls and we don't have focus.
    137         if (!hasFocus()) {
    138             return;
    139         }
    140 
    141         boolean isCurrentlyWiredHeadset = mAudioState.getRoute() == AudioState.ROUTE_WIRED_HEADSET;
    142 
    143         int newRoute = mAudioState.getRoute();  // start out with existing route
    144         if (newIsPluggedIn) {
    145             newRoute = AudioState.ROUTE_WIRED_HEADSET;
    146         } else if (isCurrentlyWiredHeadset) {
    147             Call call = getForegroundCall();
    148             boolean hasLiveCall = call != null && call.isAlive();
    149 
    150             if (hasLiveCall) {
    151                 // In order of preference when a wireless headset is unplugged.
    152                 if (mWasSpeakerOn) {
    153                     newRoute = AudioState.ROUTE_SPEAKER;
    154                 } else {
    155                     newRoute = AudioState.ROUTE_EARPIECE;
    156                 }
    157 
    158                 // We don't automatically connect to bluetooth when user unplugs their wired headset
    159                 // and they were previously using the wired. Wired and earpiece are effectively the
    160                 // same choice in that they replace each other as an option when wired headsets
    161                 // are plugged in and out. This means that keeping it earpiece is a bit more
    162                 // consistent with the status quo.  Bluetooth also has more danger associated with
    163                 // choosing it in the wrong curcumstance because bluetooth devices can be
    164                 // semi-public (like in a very-occupied car) where earpiece doesn't carry that risk.
    165             }
    166         }
    167 
    168         // We need to call this every time even if we do not change the route because the supported
    169         // routes changed either to include or not include WIRED_HEADSET.
    170         setSystemAudioState(mAudioState.isMuted(), newRoute, calculateSupportedRoutes());
    171     }
    172 
    173     void toggleMute() {
    174         mute(!mAudioState.isMuted());
    175     }
    176 
    177     void mute(boolean shouldMute) {
    178         if (!hasFocus()) {
    179             return;
    180         }
    181 
    182         Log.v(this, "mute, shouldMute: %b", shouldMute);
    183 
    184         // Don't mute if there are any emergency calls.
    185         if (CallsManager.getInstance().hasEmergencyCall()) {
    186             shouldMute = false;
    187             Log.v(this, "ignoring mute for emergency call");
    188         }
    189 
    190         if (mAudioState.isMuted() != shouldMute) {
    191             setSystemAudioState(shouldMute, mAudioState.getRoute(),
    192                     mAudioState.getSupportedRouteMask());
    193         }
    194     }
    195 
    196     /**
    197      * Changed the audio route, for example from earpiece to speaker phone.
    198      *
    199      * @param route The new audio route to use. See {@link AudioState}.
    200      */
    201     void setAudioRoute(int route) {
    202         // This can happen even when there are no calls and we don't have focus.
    203         if (!hasFocus()) {
    204             return;
    205         }
    206 
    207         Log.v(this, "setAudioRoute, route: %s", AudioState.audioRouteToString(route));
    208 
    209         // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
    210         int newRoute = selectWiredOrEarpiece(route, mAudioState.getSupportedRouteMask());
    211 
    212         // If route is unsupported, do nothing.
    213         if ((mAudioState.getSupportedRouteMask() | newRoute) == 0) {
    214             Log.wtf(this, "Asking to set to a route that is unsupported: %d", newRoute);
    215             return;
    216         }
    217 
    218         if (mAudioState.getRoute() != newRoute) {
    219             // Remember the new speaker state so it can be restored when the user plugs and unplugs
    220             // a headset.
    221             mWasSpeakerOn = newRoute == AudioState.ROUTE_SPEAKER;
    222             setSystemAudioState(mAudioState.isMuted(), newRoute,
    223                     mAudioState.getSupportedRouteMask());
    224         }
    225     }
    226 
    227     void setIsRinging(boolean isRinging) {
    228         if (mIsRinging != isRinging) {
    229             Log.v(this, "setIsRinging %b -> %b", mIsRinging, isRinging);
    230             mIsRinging = isRinging;
    231             updateAudioStreamAndMode();
    232         }
    233     }
    234 
    235     /**
    236      * Sets the tone playing status. Some tones can play even when there are no live calls and this
    237      * status indicates that we should keep audio focus even for tones that play beyond the life of
    238      * calls.
    239      *
    240      * @param isPlayingNew The status to set.
    241      */
    242     void setIsTonePlaying(boolean isPlayingNew) {
    243         ThreadUtil.checkOnMainThread();
    244 
    245         if (mIsTonePlaying != isPlayingNew) {
    246             Log.v(this, "mIsTonePlaying %b -> %b.", mIsTonePlaying, isPlayingNew);
    247             mIsTonePlaying = isPlayingNew;
    248             updateAudioStreamAndMode();
    249         }
    250     }
    251 
    252     /**
    253      * Updates the audio routing according to the bluetooth state.
    254      */
    255     void onBluetoothStateChange(BluetoothManager bluetoothManager) {
    256         // This can happen even when there are no calls and we don't have focus.
    257         if (!hasFocus()) {
    258             return;
    259         }
    260 
    261         int supportedRoutes = calculateSupportedRoutes();
    262         int newRoute = mAudioState.getRoute();
    263         if (bluetoothManager.isBluetoothAudioConnectedOrPending()) {
    264             newRoute = AudioState.ROUTE_BLUETOOTH;
    265         } else if (mAudioState.getRoute() == AudioState.ROUTE_BLUETOOTH) {
    266             newRoute = selectWiredOrEarpiece(AudioState.ROUTE_WIRED_OR_EARPIECE, supportedRoutes);
    267             // Do not switch to speaker when bluetooth disconnects.
    268             mWasSpeakerOn = false;
    269         }
    270 
    271         setSystemAudioState(mAudioState.isMuted(), newRoute, supportedRoutes);
    272     }
    273 
    274     boolean isBluetoothAudioOn() {
    275         return mBluetoothManager.isBluetoothAudioConnected();
    276     }
    277 
    278     boolean isBluetoothDeviceAvailable() {
    279         return mBluetoothManager.isBluetoothAvailable();
    280     }
    281 
    282     private void saveAudioState(AudioState audioState) {
    283         mAudioState = audioState;
    284         mStatusBarNotifier.notifyMute(mAudioState.isMuted());
    285         mStatusBarNotifier.notifySpeakerphone(mAudioState.getRoute() == AudioState.ROUTE_SPEAKER);
    286     }
    287 
    288     private void onCallUpdated(Call call) {
    289         boolean wasNotVoiceCall = mAudioFocusStreamType != AudioManager.STREAM_VOICE_CALL;
    290         updateAudioStreamAndMode();
    291 
    292         if (call != null && call.getState() == CallState.ACTIVE &&
    293                             call == mCallToSpeedUpMTAudio) {
    294             mCallToSpeedUpMTAudio = null;
    295         }
    296         // If we transition from not voice call to voice call, we need to set an initial state.
    297         if (wasNotVoiceCall && mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL) {
    298             setInitialAudioState(call, true /* force */);
    299         }
    300     }
    301 
    302     private void setSystemAudioState(boolean isMuted, int route, int supportedRouteMask) {
    303         setSystemAudioState(false /* force */, isMuted, route, supportedRouteMask);
    304     }
    305 
    306     private void setSystemAudioState(
    307             boolean force, boolean isMuted, int route, int supportedRouteMask) {
    308         if (!hasFocus()) {
    309             return;
    310         }
    311 
    312         AudioState oldAudioState = mAudioState;
    313         saveAudioState(new AudioState(isMuted, route, supportedRouteMask));
    314         if (!force && Objects.equals(oldAudioState, mAudioState)) {
    315             return;
    316         }
    317         Log.i(this, "changing audio state from %s to %s", oldAudioState, mAudioState);
    318 
    319         // Mute.
    320         if (mAudioState.isMuted() != mAudioManager.isMicrophoneMute()) {
    321             Log.i(this, "changing microphone mute state to: %b", mAudioState.isMuted());
    322             mAudioManager.setMicrophoneMute(mAudioState.isMuted());
    323         }
    324 
    325         // Audio route.
    326         if (mAudioState.getRoute() == AudioState.ROUTE_BLUETOOTH) {
    327             turnOnSpeaker(false);
    328             turnOnBluetooth(true);
    329         } else if (mAudioState.getRoute() == AudioState.ROUTE_SPEAKER) {
    330             turnOnBluetooth(false);
    331             turnOnSpeaker(true);
    332         } else if (mAudioState.getRoute() == AudioState.ROUTE_EARPIECE ||
    333                 mAudioState.getRoute() == AudioState.ROUTE_WIRED_HEADSET) {
    334             turnOnBluetooth(false);
    335             turnOnSpeaker(false);
    336         }
    337 
    338         if (!oldAudioState.equals(mAudioState)) {
    339             CallsManager.getInstance().onAudioStateChanged(oldAudioState, mAudioState);
    340             updateAudioForForegroundCall();
    341         }
    342     }
    343 
    344     private void turnOnSpeaker(boolean on) {
    345         // Wired headset and earpiece work the same way
    346         if (mAudioManager.isSpeakerphoneOn() != on) {
    347             Log.i(this, "turning speaker phone %s", on);
    348             mAudioManager.setSpeakerphoneOn(on);
    349         }
    350     }
    351 
    352     private void turnOnBluetooth(boolean on) {
    353         if (mBluetoothManager.isBluetoothAvailable()) {
    354             boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
    355             if (on != isAlreadyOn) {
    356                 Log.i(this, "connecting bluetooth %s", on);
    357                 if (on) {
    358                     mBluetoothManager.connectBluetoothAudio();
    359                 } else {
    360                     mBluetoothManager.disconnectBluetoothAudio();
    361                 }
    362             }
    363         }
    364     }
    365 
    366     private void updateAudioStreamAndMode() {
    367         Log.i(this, "updateAudioStreamAndMode, mIsRinging: %b, mIsTonePlaying: %b", mIsRinging,
    368                 mIsTonePlaying);
    369         if (mIsRinging) {
    370             requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
    371         } else {
    372             Call foregroundCall = getForegroundCall();
    373             Call waitingForAccountSelectionCall =
    374                     CallsManager.getInstance().getFirstCallWithState(CallState.PRE_DIAL_WAIT);
    375             Call call = CallsManager.getInstance().getForegroundCall();
    376             if (foregroundCall == null && call != null && call == mCallToSpeedUpMTAudio) {
    377                 requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL,
    378                                                          AudioManager.MODE_IN_CALL);
    379             } else if (foregroundCall != null && waitingForAccountSelectionCall == null) {
    380                 // In the case where there is a call that is waiting for account selection,
    381                 // this will fall back to abandonAudioFocus() below, which temporarily exits
    382                 // the in-call audio mode. This is to allow TalkBack to speak the "Call with"
    383                 // dialog information at media volume as opposed to through the earpiece.
    384                 // Once exiting the "Call with" dialog, the audio focus will return to an in-call
    385                 // audio mode when this method (updateAudioStreamAndMode) is called again.
    386                 int mode = foregroundCall.getIsVoipAudioMode() ?
    387                         AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_IN_CALL;
    388                 requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL, mode);
    389             } else if (mIsTonePlaying) {
    390                 // There is no call, however, we are still playing a tone, so keep focus.
    391                 // Since there is no call from which to determine the mode, use the most
    392                 // recently used mode instead.
    393                 requestAudioFocusAndSetMode(
    394                         AudioManager.STREAM_VOICE_CALL, mMostRecentlyUsedMode);
    395             } else if (!hasRingingForegroundCall()) {
    396                 abandonAudioFocus();
    397             } else {
    398                 // mIsRinging is false, but there is a foreground ringing call present. Don't
    399                 // abandon audio focus immediately to prevent audio focus from getting lost between
    400                 // the time it takes for the foreground call to transition from RINGING to ACTIVE/
    401                 // DISCONNECTED. When the call eventually transitions to the next state, audio
    402                 // focus will be correctly abandoned by the if clause above.
    403             }
    404         }
    405     }
    406 
    407     private void requestAudioFocusAndSetMode(int stream, int mode) {
    408         Log.i(this, "requestAudioFocusAndSetMode, stream: %d -> %d, mode: %d",
    409                 mAudioFocusStreamType, stream, mode);
    410         Preconditions.checkState(stream != STREAM_NONE);
    411 
    412         // Even if we already have focus, if the stream is different we update audio manager to give
    413         // it a hint about the purpose of our focus.
    414         if (mAudioFocusStreamType != stream) {
    415             Log.v(this, "requesting audio focus for stream: %d", stream);
    416             mAudioManager.requestAudioFocusForCall(stream,
    417                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
    418         }
    419         mAudioFocusStreamType = stream;
    420 
    421         setMode(mode);
    422     }
    423 
    424     private void abandonAudioFocus() {
    425         if (hasFocus()) {
    426             setMode(AudioManager.MODE_NORMAL);
    427             Log.v(this, "abandoning audio focus");
    428             mAudioManager.abandonAudioFocusForCall();
    429             mAudioFocusStreamType = STREAM_NONE;
    430             mCallToSpeedUpMTAudio = null;
    431         }
    432     }
    433 
    434     /**
    435      * Sets the audio mode.
    436      *
    437      * @param newMode Mode constant from AudioManager.MODE_*.
    438      */
    439     private void setMode(int newMode) {
    440         Preconditions.checkState(hasFocus());
    441         int oldMode = mAudioManager.getMode();
    442         Log.v(this, "Request to change audio mode from %d to %d", oldMode, newMode);
    443 
    444         if (oldMode != newMode) {
    445             if (oldMode == AudioManager.MODE_IN_CALL && newMode == AudioManager.MODE_RINGTONE) {
    446                 Log.i(this, "Transition from IN_CALL -> RINGTONE. Resetting to NORMAL first.");
    447                 mAudioManager.setMode(AudioManager.MODE_NORMAL);
    448             }
    449             mAudioManager.setMode(newMode);
    450             mMostRecentlyUsedMode = newMode;
    451         }
    452     }
    453 
    454     private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
    455         // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
    456         // ROUTE_WIRED_OR_EARPIECE so that callers dont have to make a call to check which is
    457         // supported before calling setAudioRoute.
    458         if (route == AudioState.ROUTE_WIRED_OR_EARPIECE) {
    459             route = AudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
    460             if (route == 0) {
    461                 Log.wtf(this, "One of wired headset or earpiece should always be valid.");
    462                 // assume earpiece in this case.
    463                 route = AudioState.ROUTE_EARPIECE;
    464             }
    465         }
    466         return route;
    467     }
    468 
    469     private int calculateSupportedRoutes() {
    470         int routeMask = AudioState.ROUTE_SPEAKER;
    471 
    472         if (mWiredHeadsetManager.isPluggedIn()) {
    473             routeMask |= AudioState.ROUTE_WIRED_HEADSET;
    474         } else {
    475             routeMask |= AudioState.ROUTE_EARPIECE;
    476         }
    477 
    478         if (mBluetoothManager.isBluetoothAvailable()) {
    479             routeMask |=  AudioState.ROUTE_BLUETOOTH;
    480         }
    481 
    482         return routeMask;
    483     }
    484 
    485     private AudioState getInitialAudioState(Call call) {
    486         int supportedRouteMask = calculateSupportedRoutes();
    487         int route = selectWiredOrEarpiece(
    488                 AudioState.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask);
    489 
    490         // We want the UI to indicate that "bluetooth is in use" in two slightly different cases:
    491         // (a) The obvious case: if a bluetooth headset is currently in use for an ongoing call.
    492         // (b) The not-so-obvious case: if an incoming call is ringing, and we expect that audio
    493         //     *will* be routed to a bluetooth headset once the call is answered. In this case, just
    494         //     check if the headset is available. Note this only applies when we are dealing with
    495         //     the first call.
    496         if (call != null && mBluetoothManager.isBluetoothAvailable()) {
    497             switch(call.getState()) {
    498                 case CallState.ACTIVE:
    499                 case CallState.ON_HOLD:
    500                 case CallState.DIALING:
    501                 case CallState.CONNECTING:
    502                 case CallState.RINGING:
    503                     route = AudioState.ROUTE_BLUETOOTH;
    504                     break;
    505                 default:
    506                     break;
    507             }
    508         }
    509 
    510         return new AudioState(false, route, supportedRouteMask);
    511     }
    512 
    513     private void setInitialAudioState(Call call, boolean force) {
    514         AudioState audioState = getInitialAudioState(call);
    515         Log.v(this, "setInitialAudioState %s, %s", audioState, call);
    516         setSystemAudioState(
    517                 force, audioState.isMuted(), audioState.getRoute(),
    518                 audioState.getSupportedRouteMask());
    519     }
    520 
    521     private void updateAudioForForegroundCall() {
    522         Call call = CallsManager.getInstance().getForegroundCall();
    523         if (call != null && call.getConnectionService() != null) {
    524             call.getConnectionService().onAudioStateChanged(call, mAudioState);
    525         }
    526     }
    527 
    528     /**
    529      * Returns the current foreground call in order to properly set the audio mode.
    530      */
    531     private Call getForegroundCall() {
    532         Call call = CallsManager.getInstance().getForegroundCall();
    533 
    534         // We ignore any foreground call that is in the ringing state because we deal with ringing
    535         // calls exclusively through the mIsRinging variable set by {@link Ringer}.
    536         if (call != null && call.getState() == CallState.RINGING) {
    537             return null;
    538         }
    539 
    540         return call;
    541     }
    542 
    543     private boolean hasRingingForegroundCall() {
    544         Call call = CallsManager.getInstance().getForegroundCall();
    545         return call != null && call.getState() == CallState.RINGING;
    546     }
    547 
    548     private boolean hasFocus() {
    549         return mAudioFocusStreamType != STREAM_NONE;
    550     }
    551 
    552     /**
    553      * Dumps the state of the {@link CallAudioManager}.
    554      *
    555      * @param pw The {@code IndentingPrintWriter} to write the state to.
    556      */
    557     public void dump(IndentingPrintWriter pw) {
    558         pw.println("mAudioState: " + mAudioState);
    559         pw.println("mBluetoothManager:");
    560         pw.increaseIndent();
    561         mBluetoothManager.dump(pw);
    562         pw.decreaseIndent();
    563         if (mWiredHeadsetManager != null) {
    564             pw.println("mWiredHeadsetManager:");
    565             pw.increaseIndent();
    566             mWiredHeadsetManager.dump(pw);
    567             pw.decreaseIndent();
    568         } else {
    569             pw.println("mWiredHeadsetManager: null");
    570         }
    571         pw.println("mAudioFocusStreamType: " + mAudioFocusStreamType);
    572         pw.println("mIsRinging: " + mIsRinging);
    573         pw.println("mIsTonePlaying: " + mIsTonePlaying);
    574         pw.println("mWasSpeakerOn: " + mWasSpeakerOn);
    575         pw.println("mMostRecentlyUsedMode: " + mMostRecentlyUsedMode);
    576     }
    577 }
    578