Add ringer code to actually play a ringtone.

Change-Id: I575ea90a9af44358a68ff3e83ff9850fd9a59ba3
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
index 35575eb..fce29da 100644
--- a/src/com/android/telecomm/Ringer.java
+++ b/src/com/android/telecomm/Ringer.java
@@ -16,6 +16,14 @@
 
 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 android.util.Log;
+
 /**
  * Controls ringing and vibration for incoming calls.
  *
@@ -24,17 +32,118 @@
  */
 final class Ringer {
 
+    private static final String TAG = Ringer.class.getSimpleName();
+
+    // Message codes used with {@link #mRingtoneHandler}.
+    private static final int EVENT_PLAY_RING = 1;
+    private static final int EVENT_STOP_RING = 2;
+
+    /**
+     * Handler used to send messages to the ringtone-playing thread.
+     */
+    private Handler mRingtoneHandler;
+
+    /**
+     * The active ringtone. Accessed only from the thread looping {@link #mRingtoneHandler}.
+     */
+    private Ringtone mRingtone;
+
     /**
      * Starts the vibration, ringer, and/or call-waiting tone.
      */
     void startRinging() {
-        // TODO(santoscordon): Fill in.
+        // TODO(santoscordon): Double-check that we want to play the ringtone. e.g., don't play if
+        // the volume is currently set to 0.
+
+        ThreadUtil.checkOnMainThread();
+        Handler handler = getRingtoneHandler();
+
+        Log.d(TAG, "Posting play");
+        handler.obtainMessage(EVENT_PLAY_RING, getCurrentRingtone()).sendToTarget();
     }
 
     /**
      * Stops the vibration, ringer, and/or call-waiting tone.
      */
     void stopRinging() {
-        // TODO(santoscordon): Fill in.
+        ThreadUtil.checkOnMainThread();
+        if (mRingtoneHandler != null) {
+            Log.d(TAG, "Posting stop");
+            mRingtoneHandler.sendEmptyMessage(EVENT_STOP_RING);
+            mRingtoneHandler = null;
+        }
+    }
+
+    /**
+     * Returns the handler to use for playing ringtones.
+     */
+    private Handler getRingtoneHandler() {
+        if (mRingtoneHandler == null) {
+            // TODO(santoscordon): Clean this up. Needs more investigation for multi-incoming calls
+            // and this multiple thread approach.
+            HandlerThread thread = new HandlerThread("ringer");
+            thread.start();
+
+            mRingtoneHandler = new Handler(thread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch(msg.what) {
+                        case EVENT_PLAY_RING:
+                            handlePlayRingtone(this, (Ringtone) msg.obj);
+                            break;
+                        case EVENT_STOP_RING:
+                            handleStopRingtone(this);
+                            break;
+                    }
+                }
+            };
+        }
+        return mRingtoneHandler;
+    }
+
+    /**
+     * @return The user's currently-selected ringtone.
+     */
+    private Ringtone getCurrentRingtone() {
+        // TODO(santoscordon): Needs support for custom ringtones.
+        return RingtoneManager.getRingtone(
+                TelecommApp.getInstance(), Settings.System.DEFAULT_RINGTONE_URI);
+    }
+
+    /**
+     * Plays the ringtone. Processed by {@link #mRingtoneHandler}.
+     *
+     * @param handler The handler that invoked this method.
+     */
+    private void handlePlayRingtone(Handler handler, Ringtone ringtone) {
+        ThreadUtil.checkNotOnMainThread();
+        // Verify that we haven't been asked to stop the ringtone before we start playing it.
+        if (!handler.hasMessages(EVENT_STOP_RING)) {
+
+            // Check to see if a ringtone already exists and is playing.
+            if (mRingtone != null && mRingtone.isPlaying()) {
+                mRingtone.stop();
+            }
+            mRingtone = ringtone;
+            mRingtone.play();
+        }
+
+        // TODO(santoscordon): Requires reposting EVENT_PLAY_RINGTONE in the case where the ringtone
+        // ends. This method only plays one loop of the ringtone.
+    }
+
+    /**
+     * Stops the ringtone and cleans up references.
+     *
+     * @param handler The handler that invoked this method.
+     */
+    private void handleStopRingtone(Handler handler) {
+        ThreadUtil.checkNotOnMainThread();
+        if (mRingtone != null) {
+            mRingtone.stop();
+            mRingtone = null;
+        }
+
+        handler.getLooper().quitSafely();
     }
 }