Add custom ringtone support.

Bug: 15313520

Change-Id: Ib4a553139b5b9d7852bdeb57ad5e33dfa35f9be8
diff --git a/src/com/android/telecomm/AsyncRingtonePlayer.java b/src/com/android/telecomm/AsyncRingtonePlayer.java
index 1c5cafe..7bec55f 100644
--- a/src/com/android/telecomm/AsyncRingtonePlayer.java
+++ b/src/com/android/telecomm/AsyncRingtonePlayer.java
@@ -19,6 +19,7 @@
 import android.media.AudioManager;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
@@ -42,15 +43,15 @@
     private Ringtone mRingtone;
 
     /** Plays the ringtone. */
-    void play() {
+    void play(Uri ringtone) {
         Log.d(this, "Posting play.");
-        postMessage(EVENT_PLAY, true /* shouldCreateHandler */);
+        postMessage(EVENT_PLAY, true /* shouldCreateHandler */, ringtone);
     }
 
     /** Stops playing the ringtone. */
     void stop() {
         Log.d(this, "Posting stop.");
-        postMessage(EVENT_STOP, false /* shouldCreateHandler */);
+        postMessage(EVENT_STOP, false /* shouldCreateHandler */, null);
     }
 
     /**
@@ -60,7 +61,7 @@
      * @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) {
+    private void postMessage(int messageCode, boolean shouldCreateHandler, Uri ringtone) {
         synchronized(this) {
             if (mHandler == null && shouldCreateHandler) {
                 mHandler = getNewHandler();
@@ -69,7 +70,7 @@
             if (mHandler == null) {
                 Log.d(this, "Message %d skipped because there is no handler.", messageCode);
             } else {
-                mHandler.sendEmptyMessage(messageCode);
+                mHandler.obtainMessage(messageCode, ringtone).sendToTarget();
             }
         }
     }
@@ -88,7 +89,7 @@
             public void handleMessage(Message msg) {
                 switch(msg.what) {
                     case EVENT_PLAY:
-                        handlePlay();
+                        handlePlay((Uri) msg.obj);
                         break;
                     case EVENT_STOP:
                         handleStop();
@@ -101,12 +102,12 @@
     /**
      * Starts the actual playback of the ringtone. Executes on ringtone-thread.
      */
-    private void handlePlay() {
+    private void handlePlay(Uri ringtoneUri) {
         ThreadUtil.checkNotOnMainThread();
         Log.i(this, "Play ringtone.");
 
         if (mRingtone == null) {
-            mRingtone = getCurrentRingtone();
+            mRingtone = getRingtone(ringtoneUri);
 
             // Cancel everything if there is no ringtone.
             if (mRingtone == null) {
@@ -148,10 +149,12 @@
         }
     }
 
-    private Ringtone getCurrentRingtone() {
-        // TODO: Needs support for custom ringtones.
-        Ringtone ringtone = RingtoneManager.getRingtone(
-                TelecommApp.getInstance(), Settings.System.DEFAULT_RINGTONE_URI);
+    private Ringtone getRingtone(Uri ringtoneUri) {
+        if (ringtoneUri == null) {
+            ringtoneUri = Settings.System.DEFAULT_RINGTONE_URI;
+        }
+
+        Ringtone ringtone = RingtoneManager.getRingtone(TelecommApp.getInstance(), ringtoneUri);
         ringtone.setStreamType(AudioManager.STREAM_RING);
         return ringtone;
     }
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index c51632c..2bcc978 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -709,6 +709,10 @@
         mHandoffCallServiceDescriptor = descriptor;
     }
 
+    Uri getRingtone() {
+        return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
+    }
+
     /**
      * @return True if the call is ringing, else logs the action name.
      */
@@ -737,6 +741,7 @@
         mQueryToken++;  // Updated so that previous queries can no longer set the information.
         mCallerInfo = null;
         if (!TextUtils.isEmpty(number)) {
+            Log.v(this, "Looking up information for: %s.", Log.piiHandle(number));
             CallerInfoAsyncQuery.startQuery(
                     mQueryToken,
                     TelecommApp.getInstance(),
@@ -758,6 +763,7 @@
 
         if (mQueryToken == token) {
             mCallerInfo = callerInfo;
+            Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
 
             if (mCallerInfo.person_id != 0) {
                 Uri personUri =
diff --git a/src/com/android/telecomm/CallAudioManager.java b/src/com/android/telecomm/CallAudioManager.java
index fb8ed9e..43b102b 100644
--- a/src/com/android/telecomm/CallAudioManager.java
+++ b/src/com/android/telecomm/CallAudioManager.java
@@ -173,7 +173,7 @@
         if (newIsPluggedIn) {
             newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
         } else if (mWasSpeakerOn) {
-            Call call = CallsManager.getInstance().getForegroundCall();
+            Call call = getForegroundCall();
             if (call != null && call.isAlive()) {
                 // Restore the speaker state.
                 newRoute = CallAudioState.ROUTE_SPEAKER;
@@ -263,7 +263,7 @@
         if (mIsRinging) {
             requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
         } else {
-            Call call = CallsManager.getInstance().getForegroundCall();
+            Call call = getForegroundCall();
             if (call != null) {
                 int mode = TelephonyUtil.isCurrentlyPSTNCall(call) ?
                         AudioManager.MODE_IN_CALL : AudioManager.MODE_IN_COMMUNICATION;
@@ -282,10 +282,9 @@
         Log.v(this, "setSystemAudioStreamAndMode, stream: %d -> %d", mAudioFocusStreamType, stream);
         Preconditions.checkState(stream != STREAM_NONE);
 
-        // Only request audio focus once. If the stream type changes there's no need to abandon
-        // and re-request audio focus.  The system doesn't really care about the stream we requested
-        // focus for so just silently switch.
-        if (mAudioFocusStreamType == STREAM_NONE) {
+        // Even if we already have focus, if the stream is different we update audio manager to give
+        // it a hint about the purpose of our focus.
+        if (mAudioFocusStreamType != stream) {
             Log.v(this, "requesting audio focus for stream: %d", stream);
             mAudioManager.requestAudioFocusForCall(stream,
                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
@@ -389,4 +388,18 @@
             call.getCallService().onAudioStateChanged(call, mAudioState);
         }
     }
+
+    /**
+     * Returns the current foreground call in order to properly set the audio mode.
+     */
+    private Call getForegroundCall() {
+        Call call = CallsManager.getInstance().getForegroundCall();
+
+        // We ignore any foreground call that is in the ringing state because we deal with ringing
+        // calls exclusively through the mIsRinging variable set by {@link Ringer}.
+        if (call != null && call.getState() == CallState.RINGING) {
+            call = null;
+        }
+        return call;
+    }
 }
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
index fee0a63..5b5beff 100644
--- a/src/com/android/telecomm/Ringer.java
+++ b/src/com/android/telecomm/Ringer.java
@@ -23,8 +23,7 @@
 import android.provider.Settings;
 import android.telecomm.CallState;
 
-import com.google.common.collect.Lists;
-
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -46,7 +45,7 @@
      * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming
      * calls and explicit ordering is useful for maintaining the proper state of the ringer.
      */
-    private final List<Call> mRingingCalls = Lists.newLinkedList();
+    private final List<Call> mRingingCalls = new LinkedList<>();
 
     private final CallAudioManager mCallAudioManager;
     private final CallsManager mCallsManager;
@@ -78,7 +77,7 @@
     }
 
     @Override
-    public void onCallAdded(Call call) {
+    public void onCallAdded(final Call call) {
         if (call.isIncoming() && call.getState() == CallState.RINGING) {
             if (mRingingCalls.contains(call)) {
                 Log.wtf(this, "New ringing call is already in list of unanswered calls");
@@ -88,7 +87,6 @@
         }
     }
 
-
     @Override
     public void onCallRemoved(Call call) {
         removeFromUnansweredCall(call);
@@ -180,7 +178,11 @@
                 // is available, then we send it a signal to do its own ringtone and we dont need
                 // to play the ringtone on the device.
                 if (!mCallAudioManager.isBluetoothDeviceAvailable()) {
-                    mRingtonePlayer.play();
+                    // Because we wait until a contact info query to complete before processing a
+                    // call (for the purposes of direct-to-voicemail), the information about custom
+                    // ringtones should be available by the time this code executes. We can safely
+                    // request the custom ringtone from the call and expect it to be current.
+                    mRingtonePlayer.play(foregroundCall.getRingtone());
                 }
             } else {
                 Log.v(this, "startRingingOrCallWaiting, skipping because volume is 0");
diff --git a/src/com/android/telecomm/Timeouts.java b/src/com/android/telecomm/Timeouts.java
index afd6d29..d13091f 100644
--- a/src/com/android/telecomm/Timeouts.java
+++ b/src/com/android/telecomm/Timeouts.java
@@ -18,7 +18,6 @@
 
 import android.provider.Settings;
 import android.telecomm.CallServiceProvider;
-import android.telecomm.CallServiceSelector;
 
 /**
  * A helper class which serves only to make it easier to lookup timeout values. This class should