Audio focus and ring changes (again).

- Went back to using ringtone.  Ringtone class has fallback logic that is
  necessary to keep (without duplicating).
- Moved the ringtone player code into AsyncRingtonePlayer and kept the
  CallsManager listener code inside ringer into CallAudioManager.java
- Consolidated AudioFocus acquisition into CallAudioManager.

bug: 13365906

Change-Id: I8d7b6a999f594b8f81497aa3f5b7ac5916fdd18e
diff --git a/src/com/android/telecomm/AsyncRingtonePlayer.java b/src/com/android/telecomm/AsyncRingtonePlayer.java
new file mode 100644
index 0000000..ae78c5b
--- /dev/null
+++ b/src/com/android/telecomm/AsyncRingtonePlayer.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm;
+
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.provider.Settings;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * Plays the default ringtone. Uses {@link Ringtone} in a separate thread so that this class can be
+ * used from the main thread.
+ */
+class AsyncRingtonePlayer {
+    // Message codes used with the ringtone thread.
+    static final int EVENT_PLAY = 1;
+    static final int EVENT_STOP = 2;
+
+    /** Handler running on the ringtone thread. */
+    private Handler mHandler;
+
+    /** The current ringtone. Only used by the ringtone thread. */
+    private Ringtone mRingtone;
+
+    /** Plays the ringtone. */
+    void play() {
+        Log.d(this, "Posting play.");
+        postMessage(EVENT_PLAY, true /* shouldCreateHandler */);
+    }
+
+    /** Stops playing the ringtone. */
+    void stop() {
+        Log.d(this, "Posting stop.");
+        postMessage(EVENT_STOP, false /* shouldCreateHandler */);
+    }
+
+    /**
+     * Posts a message to the ringtone-thread handler. Creates the handler if specified by the
+     * parameter shouldCreateHandler.
+     *
+     * @param messageCode The message to post.
+     * @param shouldCreateHandler True when a handler should be created to handle this message.
+     */
+    private void postMessage(int messageCode, boolean shouldCreateHandler) {
+        synchronized(this) {
+            if (mHandler == null && shouldCreateHandler) {
+                mHandler = getNewHandler();
+            }
+
+            if (mHandler == null) {
+                Log.d(this, "Message %d skipped because there is no handler.", messageCode);
+            } else {
+                mHandler.sendEmptyMessage(messageCode);
+            }
+        }
+    }
+
+    /**
+     * Creates a new ringtone Handler running in its own thread.
+     */
+    private Handler getNewHandler() {
+        Preconditions.checkState(mHandler == null);
+
+        HandlerThread thread = new HandlerThread("ringtone-player");
+        thread.start();
+
+        return new Handler(thread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                switch(msg.what) {
+                    case EVENT_PLAY:
+                        handlePlay();
+                        break;
+                    case EVENT_STOP:
+                        handleStop();
+                        break;
+                }
+            }
+        };
+    }
+
+    /**
+     * Starts the actual playback of the ringtone. Executes on ringtone-thread.
+     */
+    private void handlePlay() {
+        ThreadUtil.checkNotOnMainThread();
+        Log.i(this, "Play ringtone.");
+
+        if (mRingtone == null) {
+            mRingtone = getCurrentRingtone();
+
+            // Cancel everything if there is no ringtone.
+            if (mRingtone == null) {
+                handleStop();
+                return;
+            }
+        }
+
+        if (mRingtone.isPlaying()) {
+            Log.d(this, "Ringtone already playing.");
+        } else {
+            mRingtone.play();
+            Log.d(this, "Ringtone.play() invoked.");
+        }
+    }
+
+    /**
+     * Stops the playback of the ringtone. Executes on the ringtone-thread.
+     */
+    private void handleStop() {
+        ThreadUtil.checkNotOnMainThread();
+        Log.i(this, "Stop ringtone.");
+
+        if (mRingtone != null) {
+            Log.d(this, "Ringtone.stop() invoked.");
+            mRingtone.stop();
+            mRingtone = null;
+        }
+
+        synchronized(this) {
+            if (mHandler.hasMessages(EVENT_PLAY)) {
+                Log.v(this, "Keeping alive ringtone thread for pending messages.");
+            } else {
+                mHandler.removeMessages(EVENT_STOP);
+                mHandler.getLooper().quitSafely();
+                mHandler = null;
+                Log.v(this, "Handler cleared.");
+            }
+        }
+    }
+
+    private Ringtone getCurrentRingtone() {
+        // TODO: Needs support for custom ringtones.
+        return RingtoneManager.getRingtone(
+                TelecommApp.getInstance(), Settings.System.DEFAULT_RINGTONE_URI);
+    }
+}
diff --git a/src/com/android/telecomm/CallAudioManager.java b/src/com/android/telecomm/CallAudioManager.java
index 07d5dbe..0d58f86 100644
--- a/src/com/android/telecomm/CallAudioManager.java
+++ b/src/com/android/telecomm/CallAudioManager.java
@@ -20,40 +20,174 @@
 import android.media.AudioManager;
 import android.telecomm.CallState;
 
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
 /**
  * This class manages audio modes, streams and other properties.
  */
 final class CallAudioManager extends CallsManagerListenerBase {
+    private AsyncRingtonePlayer mRinger = new AsyncRingtonePlayer();
+
+    private boolean mHasAudioFocus = false;
+
+    /**
+     * Used to keep ordering of unanswered incoming calls. The existence of multiple call services
+     * means that there can easily exist multiple incoming calls and explicit ordering is useful for
+     * maintaining the proper state of the ringer.
+     */
+    private final List<String> mUnansweredCallIds = Lists.newLinkedList();
+
+    /**
+     * Denotes when the ringer is disabled. This is useful in temporarily disabling the ringer when
+     * the a call is answered/rejected by the user, but the call hasn't actually moved out of the
+     * ringing state.
+     */
+    private boolean mIsRingingDisabled = false;
+
+    @Override
+    public void onCallAdded(Call call) {
+        if (call.getState() == CallState.RINGING) {
+            mUnansweredCallIds.add(call.getId());
+        }
+        updateAudio();
+    }
+
+    @Override
+    public void onCallRemoved(Call call) {
+        removeFromUnansweredCallIds(call.getId());
+        updateAudio();
+    }
+
     @Override
     public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
-        switch (newState) {
-            case ACTIVE:
-                updateAudioFocusForActiveCall(call);
-                break;
-            case DISCONNECTED:
-                updateAudioFocusForNoCall();
-                break;
-            default:
-                break;
+        if (oldState == CallState.RINGING) {
+            removeFromUnansweredCallIds(call.getId());
+        }
+
+        updateAudio();
+    }
+
+    @Override
+    public void onIncomingCallAnswered(Call call) {
+        mIsRingingDisabled = true;
+        updateAudio();
+    }
+
+    @Override
+    public void onIncomingCallRejected(Call call) {
+        mIsRingingDisabled = true;
+        updateAudio();
+    }
+
+    /**
+     * Reads the current state of all calls from CallsManager and sets the appropriate audio modes
+     * as well as triggers the start/stop of the ringer.
+     */
+    private void updateAudio() {
+        CallsManager callsManager = CallsManager.getInstance();
+
+        boolean hasRingingCall = !mIsRingingDisabled && !mUnansweredCallIds.isEmpty();
+        boolean hasLiveCall = callsManager.hasCallWithState(CallState.ACTIVE, CallState.DIALING);
+
+        int mode = hasRingingCall ? AudioManager.MODE_RINGTONE :
+               hasLiveCall ? AudioManager.MODE_IN_CALL :
+               AudioManager.MODE_NORMAL;
+
+        boolean needsFocus = (mode != AudioManager.MODE_NORMAL);
+
+        // Acquiring focus needs to be first, unlike releasing focus, which happens at the end.
+        if (needsFocus) {
+            acquireFocus(hasRingingCall);
+            setMode(mode);
+        }
+
+        if (hasRingingCall) {
+            mRinger.play();
+        } else {
+            mRinger.stop();
+        }
+
+        if (!needsFocus) {
+            setMode(AudioManager.MODE_NORMAL);
+            releaseFocus();
         }
     }
 
-    private void updateAudioFocusForActiveCall(Call call) {
-        Log.v(this, "onForegroundCallChanged, requesting audio focus");
-        Context context = TelecommApp.getInstance();
-        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        audioManager.requestAudioFocusForCall(AudioManager.STREAM_VOICE_CALL,
-                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-        audioManager.setMode(AudioManager.MODE_IN_CALL);
-        audioManager.setMicrophoneMute(false);
-        audioManager.setSpeakerphoneOn(false);
+    /**
+     * Acquires audio focus.
+     *
+     * @param isForRinging True if this focus is for playing the ringer.
+     */
+    private void acquireFocus(boolean isForRinging) {
+        if (!mHasAudioFocus) {
+            int stream = isForRinging ? AudioManager.STREAM_RING : AudioManager.STREAM_VOICE_CALL;
+
+            AudioManager audioManager = getAudioManager();
+            audioManager.requestAudioFocusForCall(stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+            audioManager.setMicrophoneMute(false);
+            audioManager.setSpeakerphoneOn(false);
+            mHasAudioFocus = true;
+        }
     }
 
-    private void updateAudioFocusForNoCall() {
-        Log.v(this, "updateAudioFocusForNoCall, abandoning audio focus");
-        Context context = TelecommApp.getInstance().getApplicationContext();
-        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        audioManager.setMode(AudioManager.MODE_NORMAL);
-        audioManager.abandonAudioFocusForCall();
+    /**
+     * Releases focus.
+     */
+    void releaseFocus() {
+        if (mHasAudioFocus) {
+            AudioManager audioManager = getAudioManager();
+
+            // Reset speakerphone and mute in case they were changed by telecomm.
+            audioManager.setMicrophoneMute(false);
+            audioManager.setSpeakerphoneOn(false);
+            audioManager.abandonAudioFocusForCall();
+
+            mHasAudioFocus = false;
+            Log.v(this, "Focus released");
+        }
+
+    }
+
+    /**
+     * Sets the audio mode.
+     *
+     * @param mode Mode constant from AudioManager.MODE_*.
+     */
+    void setMode(int mode) {
+        if (mHasAudioFocus) {
+            AudioManager audioManager = getAudioManager();
+            if (mode != audioManager.getMode()) {
+                Log.v(this, "Audio mode set to %d.", mode);
+                audioManager.setMode(mode);
+                Log.v(this, "Audio mode actually set to %d.", audioManager.getMode());
+            }
+        } else {
+            Log.wtf(this, "Trying to set audio mode to %d without focus.", mode);
+        }
+    }
+
+    /**
+     * Removes the specified call from the list of unanswered incoming calls.
+     *
+     * @param callId The ID of the call.
+     */
+    private void removeFromUnansweredCallIds(String callId) {
+        if (!mUnansweredCallIds.isEmpty()) {
+            // If the call is the top-most call, then no longer disable the ringer.
+            if (callId.equals(mUnansweredCallIds.get(0))) {
+                mIsRingingDisabled = false;
+            }
+
+            mUnansweredCallIds.remove(callId);
+        }
+    }
+
+    /**
+     * Returns the system audio manager.
+     */
+    private AudioManager getAudioManager() {
+        return (AudioManager) TelecommApp.getInstance().getSystemService(Context.AUDIO_SERVICE);
     }
 }
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index 452f8e1..106f9af 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -78,8 +78,6 @@
 
     private final CallsManagerListener mInCallController;
 
-    private final CallsManagerListener mRinger;
-
     private VoicemailManager mVoicemailManager;
 
     private final List<OutgoingCallValidator> mOutgoingCallValidators = Lists.newArrayList();
@@ -95,7 +93,6 @@
         mPhoneStateBroadcaster = new PhoneStateBroadcaster();
         mCallAudioManager = new CallAudioManager();
         mInCallController = new InCallController();
-        mRinger = new Ringer();
     }
 
     static CallsManager getInstance() {
@@ -232,7 +229,6 @@
             mPhoneStateBroadcaster.onIncomingCallAnswered(call);
             mCallAudioManager.onIncomingCallAnswered(call);
             mInCallController.onIncomingCallAnswered(call);
-            mRinger.onIncomingCallAnswered(call);
 
             // We do not update the UI until we get confirmation of the answer() through
             // {@link #markCallAsActive}. However, if we ever change that to look more responsive,
@@ -258,7 +254,6 @@
             mPhoneStateBroadcaster.onIncomingCallRejected(call);
             mCallAudioManager.onIncomingCallRejected(call);
             mInCallController.onIncomingCallRejected(call);
-            mRinger.onIncomingCallRejected(call);
 
             call.reject();
         }
@@ -342,6 +337,21 @@
     }
 
     /**
+     * @return True if there exists a call with the specific state.
+     */
+    boolean hasCallWithState(CallState... states) {
+        for (Call call : mCalls.values()) {
+            for (CallState state : states) {
+                if (call.getState() == state) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * Adds the specified call to the main list of live calls.
      *
      * @param call The call to add.
@@ -353,7 +363,6 @@
         mPhoneStateBroadcaster.onCallAdded(call);
         mCallAudioManager.onCallAdded(call);
         mInCallController.onCallAdded(call);
-        mRinger.onCallAdded(call);
         updateForegroundCall();
     }
 
@@ -364,13 +373,11 @@
         mPhoneStateBroadcaster.onCallRemoved(call);
         mCallAudioManager.onCallRemoved(call);
         mInCallController.onCallRemoved(call);
-        mRinger.onCallRemoved(call);
         updateForegroundCall();
     }
 
     /**
-     * Sets the specified state on the specified call. Updates the ringer if the call is exiting
-     * the RINGING state.
+     * Sets the specified state on the specified call.
      *
      * @param callId The ID of the call to update.
      * @param newState The new state of the call.
@@ -389,8 +396,7 @@
     }
 
     /**
-     * Sets the specified state on the specified call. Updates the ringer if the call is exiting
-     * the RINGING state.
+     * Sets the specified state on the specified call.
      *
      * @param call The call.
      * @param newState The new state of the call.
@@ -413,7 +419,6 @@
                 mPhoneStateBroadcaster.onCallStateChanged(call, oldState, newState);
                 mCallAudioManager.onCallStateChanged(call, oldState, newState);
                 mInCallController.onCallStateChanged(call, oldState, newState);
-                mRinger.onCallStateChanged(call, oldState, newState);
                 updateForegroundCall();
             }
         }
@@ -444,7 +449,6 @@
             mPhoneStateBroadcaster.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
             mCallAudioManager.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
             mInCallController.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
-            mRinger.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
         }
     }
 }
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
deleted file mode 100644
index bc24560..0000000
--- a/src/com/android/telecomm/Ringer.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.telecomm;
-
-import android.content.Context;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnPreparedListener;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.telecomm.CallState;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.io.IOException;
-import java.util.List;
-
-/**
- * Controls ringing and vibration for incoming calls.
- */
-final class Ringer extends CallsManagerListenerBase implements OnErrorListener, OnPreparedListener {
-    // States for the Ringer.
-    /** Actively playing the ringer. */
-    private static final int RINGING = 1;
-
-    /** Ringer currently stopped. */
-    private static final int STOPPED = 2;
-
-    /** {@link #mMediaPlayer} is preparing, expected to ring once prepared. */
-    private static final int PREPARING_WITH_RING = 3;
-
-    /** {@link #mMediaPlayer} is preparing, expected to stop once prepared. */
-    private static final int PREPARING_WITH_STOP = 4;
-
-    /**
-     * The current state of the ringer.
-     */
-    private int mState = STOPPED;
-
-    /** The active media player for the ringer. */
-    private MediaPlayer mMediaPlayer;
-
-    /*
-     * Used to keep ordering of unanswered incoming calls. The existence of multiple call services
-     * means that there can easily exist multiple incoming calls and explicit ordering is useful for
-     * maintaining the proper state of the ringer.
-     */
-    private final List<String> mUnansweredCallIds = Lists.newLinkedList();
-
-    @Override
-    public void onCallAdded(Call call) {
-        if (call.isIncoming() && call.getState() == CallState.RINGING) {
-            if (mUnansweredCallIds.contains(call.getId())) {
-                Log.wtf(this, "New ringing call is already in list of unanswered calls");
-            }
-            mUnansweredCallIds.add(call.getId());
-            if (mUnansweredCallIds.size() == 1) {
-                // Start the ringer if we are the top-most incoming call (the only one in this
-                // case).
-                startRinging();
-            }
-        }
-    }
-
-    @Override
-    public void onCallRemoved(Call call) {
-        removeFromUnansweredCallIds(call.getId());
-    }
-
-    @Override
-    public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
-        if (newState != CallState.RINGING) {
-            removeFromUnansweredCallIds(call.getId());
-        }
-    }
-
-    @Override
-    public void onIncomingCallAnswered(Call call) {
-        onRespondedToIncomingCall(call);
-    }
-
-    @Override
-    public void onIncomingCallRejected(Call call) {
-        onRespondedToIncomingCall(call);
-    }
-
-    private void onRespondedToIncomingCall(Call call) {
-        // Only stop the ringer if this call is the top-most incoming call.
-        if (!mUnansweredCallIds.isEmpty() && mUnansweredCallIds.get(0).equals(call.getId())) {
-            stopRinging();
-        }
-    }
-
-    /**
-     * Removes the specified call from the list of unanswered incoming calls and updates the ringer
-     * based on the new state of {@link #mUnansweredCallIds}. Safe to call with a call ID that
-     * is not present in the list of incoming calls.
-     *
-     * @param callId The ID of the call.
-     */
-    private void removeFromUnansweredCallIds(String callId) {
-        if (mUnansweredCallIds.remove(callId)) {
-            if (mUnansweredCallIds.isEmpty()) {
-                stopRinging();
-            } else {
-                startRinging();
-            }
-        }
-    }
-
-    /**
-     * Starts the vibration, ringer, and/or call-waiting tone.
-     * TODO(santoscordon): vibration and call-waiting tone.
-     */
-    private void startRinging() {
-        // Check if we are muted before playing the ringer.
-        if (getAudioManager().getStreamVolume(AudioManager.STREAM_RING) > 0) {
-            moveToState(RINGING);
-        } else {
-            Log.d(this, "Ringer play skipped due to muted volume.");
-        }
-    }
-
-    /**
-     * Stops the vibration, ringer, and/or call-waiting tone.
-     */
-    private void stopRinging() {
-        moveToState(STOPPED);
-    }
-
-    /**
-     * Handles asynchronous media player "prepared" response by playing the ringer if we are
-     * still expected to or uninitializing it if we've been asked to stop.
-     *
-     * {@inheritDoc}
-     */
-    @Override
-    public void onPrepared(MediaPlayer mediaPlayer) {
-        Preconditions.checkState(mMediaPlayer == null);
-
-        // See {@link #moveToState} for state transitions.
-        if (PREPARING_WITH_RING == mState) {
-            Log.i(this, "Playing the ringer.");
-            setRingerAudioMode();
-            mMediaPlayer = mediaPlayer;
-            mMediaPlayer.start();
-            setState(RINGING);
-        } else if (PREPARING_WITH_STOP == mState) {
-            mediaPlayer.release();
-            setState(STOPPED);
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
-        Log.i(this, "Mediaplayer failed to initialize. What: %d, extra: %d.", what, extra);
-        resetMediaPlayer();
-        setState(STOPPED);
-        return true;
-    }
-
-    /**
-     * Transitions the state of the ringer. State machine below. Any missing arrows imply that the
-     * state remains the same (e.g., (r) on RING state keeps it at RING state).
-     *
-     * +----------------(s)----------------------------+
-     * |                                               |
-     * +----------------(e)-------+                    |
-     * |                          |                    |
-     * +-> STOPPED -(r)-> PREPARING_WITH_RING +-(p)-> RING
-     *       ^                    ^           |
-     *       |                    |           |
-     *     (p,e)                 (r)          |
-     *       |                    |           +-(s)-> PREPARING_WITH_STOP
-     *       |                    |                      | |
-     *       |                    +----------------------+ |
-     *       +---------------------------------------------+
-     *
-     * STOPPED - Ringer completely stopped, like its initial state.
-     * PREPARING_TO_RING - Media player preparing asynchronously to start ringing.
-     * RINGING - The ringtone is currently playing.
-     * PREPARING_TO_STOP - Media player is still preparing, but we've already been asked to stop.
-     *
-     * (r) - {@link #startRinging}
-     * (s) - {@link #stopRinging}
-     * (p) - {@link #onPrepared}
-     * (e) - {@link #onError}
-     */
-    private void moveToState(int newState) {
-        // Only this method sets PREPARING_* states.
-        Preconditions.checkState(newState == RINGING || newState == STOPPED);
-
-        if (newState == mState) {
-            return;
-        }
-
-        if (RINGING == newState) {
-            if (STOPPED == mState) {
-                // If we are stopped, we need to preparing the media player and wait for it to
-                // start the ring. New state set by prepareForRinging.
-                if (prepareForRinging()) {
-                    setState(PREPARING_WITH_RING);
-                }
-            } else if (PREPARING_WITH_STOP == mState) {
-                // We are currently preparing the media player, but expect it to put the ringer into
-                // stop once prepared...change that to ring.
-                setState(PREPARING_WITH_RING);
-            }
-        } else if (STOPPED == newState) {
-            if (RINGING == mState) {
-                // We are currently ringing, so just stop it.
-                stopPlayingRinger();
-                setState(STOPPED);
-            } else if (PREPARING_WITH_RING == mState) {
-                // We are preparing the media player, make sure that when it is finished, it moves
-                // to STOPPED instead of ringing.
-                setState(PREPARING_WITH_STOP);
-            }
-        }
-    }
-
-    /**
-     * Sets the ringer state and checks the current thread.
-     *
-     * @param newState The new state to set.
-     */
-    private void setState(int newState) {
-        ThreadUtil.checkOnMainThread();
-        Log.v(this, "setState, %d -> %d", mState, newState);
-        mState = newState;
-    }
-
-    /**
-     * Starts media player's asynchronous prepare. Response returned in either {@link #onError} or
-     * {@link #onPrepared}.
-     *
-     * @return True if the prepare was successfully started.
-     */
-    private boolean prepareForRinging() {
-        Log.i(this, "Preparing the ringer.");
-
-        Uri ringtoneUri = getCurrentRingtoneUri();
-        if (ringtoneUri == null) {
-            Log.e(this, null, "Ringtone not set.");
-            return false;
-        }
-
-        MediaPlayer mediaPlayer = new MediaPlayer();
-        mediaPlayer.setOnErrorListener(this);
-        mediaPlayer.setOnPreparedListener(this);
-        mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING);
-
-        try {
-            mediaPlayer.setDataSource(TelecommApp.getInstance(), ringtoneUri);
-            mediaPlayer.prepareAsync();
-            return true;
-        } catch (IOException e) {
-            mediaPlayer.reset();
-            mediaPlayer.release();
-
-            Log.e(this, e, "Failed to initialize media player for ringer: %s.", ringtoneUri);
-            return false;
-        }
-    }
-
-    /**
-     * Stops and uninitializes the media player.
-     */
-    private void stopPlayingRinger() {
-        Preconditions.checkNotNull(mMediaPlayer);
-        Log.i(this, "Stopping the ringer.");
-
-        resetMediaPlayer();
-        unsetRingerAudioMode();
-    }
-
-    /**
-     * Stops and uninitializes the media player.
-     */
-    private void resetMediaPlayer() {
-        if (mMediaPlayer != null) {
-            // Ringtone.java does not do stop() before release, but it's safer to do so and none of
-            // the documentation suggests that stop() should be skipped.
-            mMediaPlayer.stop();
-            mMediaPlayer.release();
-            mMediaPlayer = null;
-        }
-    }
-
-    /**
-     * @return The default ringtone Uri.
-     */
-    private Uri getCurrentRingtoneUri() {
-        return RingtoneManager.getActualDefaultRingtoneUri(
-                TelecommApp.getInstance(), RingtoneManager.TYPE_RINGTONE);
-    }
-
-    /**
-     * Sets the audio mode for playing the ringtone.
-     */
-    private void setRingerAudioMode() {
-        AudioManager audioManager = getAudioManager();
-        audioManager.requestAudioFocusForCall(
-                AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-        audioManager.setMode(AudioManager.MODE_RINGTONE);
-    }
-
-    /**
-     * Returns the audio mode to the normal state after ringing.
-     */
-    private void unsetRingerAudioMode() {
-        AudioManager audioManager = getAudioManager();
-        audioManager.setMode(AudioManager.MODE_NORMAL);
-        audioManager.abandonAudioFocusForCall();
-    }
-
-    /**
-     * Returns the system audio manager.
-     */
-    private AudioManager getAudioManager() {
-        return (AudioManager) TelecommApp.getInstance().getSystemService(Context.AUDIO_SERVICE);
-    }
-}