Merge "Take advantage of new audio-coupled haptic patterns." into pi-dev
diff --git a/src/com/android/server/telecom/AsyncRingtonePlayer.java b/src/com/android/server/telecom/AsyncRingtonePlayer.java
index 7ed1c85..8a7be4d 100644
--- a/src/com/android/server/telecom/AsyncRingtonePlayer.java
+++ b/src/com/android/server/telecom/AsyncRingtonePlayer.java
@@ -92,7 +92,7 @@
         HandlerThread thread = new HandlerThread("ringtone-player");
         thread.start();
 
-        return new Handler(thread.getLooper()) {
+        return new Handler(thread.getLooper(), null /*callback*/, true /*async*/) {
             @Override
             public void handleMessage(Message msg) {
                 switch(msg.what) {
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index e8b2b26..c4f9b63 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -24,6 +24,7 @@
 import android.telecom.TelecomManager;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
+import android.media.Ringtone;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Vibrator;
@@ -37,7 +38,8 @@
  */
 @VisibleForTesting
 public class Ringer {
-    VibrationEffect mVibrationEffect;
+    @VisibleForTesting
+    public VibrationEffect mDefaultVibrationEffect;
 
     private static final long[] PULSE_PATTERN = {0,12,250,12,500, // priming  + interval
             50,50,50,50,50,50,50,50,50,50,50,50,50,50, // ease-in
@@ -125,11 +127,11 @@
         mInCallController = inCallController;
 
         if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) {
-            mVibrationEffect = VibrationEffect.createWaveform(SIMPLE_VIBRATION_PATTERN,
+            mDefaultVibrationEffect = VibrationEffect.createWaveform(SIMPLE_VIBRATION_PATTERN,
                     SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT);
         } else {
-            mVibrationEffect = VibrationEffect.createWaveform(PULSE_PATTERN, PULSE_AMPLITUDE,
-                    REPEAT_VIBRATION_AT);
+            mDefaultVibrationEffect = VibrationEffect.createWaveform(PULSE_PATTERN,
+                    PULSE_AMPLITUDE, REPEAT_VIBRATION_AT);
         }
     }
 
@@ -174,6 +176,7 @@
 
         stopCallWaiting();
 
+        VibrationEffect effect;
         if (isRingerAudible) {
             mRingingCall = foregroundCall;
             Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER);
@@ -182,15 +185,16 @@
             // 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(mRingtoneFactory, foregroundCall);
+            effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall);
         } else {
             Log.i(this, "startRinging: skipping because ringer would not be audible. " +
                     "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s",
                     isVolumeOverZero, shouldRingForContact, isRingtonePresent);
+            effect = mDefaultVibrationEffect;
         }
 
         if (shouldVibrate(mContext, foregroundCall) && !mIsVibrating && shouldRingForContact) {
-            mVibratingCall = foregroundCall;
-            mVibrator.vibrate(mVibrationEffect, VIBRATION_ATTRIBUTES);
+            mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
             mIsVibrating = true;
         } else if (mIsVibrating) {
             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
@@ -199,6 +203,20 @@
         return shouldAcquireAudioFocus;
     }
 
+    private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) {
+        VibrationEffect effect = null;
+        Ringtone ringtone = factory.getRingtone(call);
+        Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null;
+        if (ringtoneUri != null) {
+            effect = VibrationEffect.get(ringtoneUri, mContext);
+        }
+
+        if (effect == null) {
+            effect = mDefaultVibrationEffect;
+        }
+        return effect;
+    }
+
     public void startCallWaiting(Call call) {
         if (mSystemSettingsUtil.isTheaterModeOn(mContext)) {
             return;
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 2a45649..25460de 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -18,9 +18,11 @@
 
 import android.app.NotificationManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.Ringtone;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
@@ -45,6 +47,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -52,6 +55,8 @@
 
 @RunWith(JUnit4.class)
 public class RingerTest extends TelecomTestCase {
+    private static final Uri FAKE_RINGTONE_URI = Uri.parse("content://media/fake/audio/1729");
+
     @Mock InCallTonePlayer.Factory mockPlayerFactory;
     @Mock SystemSettingsUtil mockSystemSettingsUtil;
     @Mock AsyncRingtonePlayer mockRingtonePlayer;
@@ -179,7 +184,8 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator).vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
+        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
+                any(AudioAttributes.class));
     }
 
     @SmallTest
@@ -194,7 +200,26 @@
         assertFalse(mRingerUnderTest.startRinging(mockCall2, false));
         verify(mockTonePlayer).stopTone();
         verify(mockRingtonePlayer, never()).play(any(RingtoneFactory.class), any(Call.class));
-        verify(mockVibrator).vibrate(any(VibrationEffect.class), any(AudioAttributes.class));
+        verify(mockVibrator).vibrate(eq(mRingerUnderTest.mDefaultVibrationEffect),
+                any(AudioAttributes.class));
+    }
+
+    @SmallTest
+    @Test
+    public void testCustomVibrationForRingtone() {
+        Resources resources = mContext.getResources();
+        when(resources.getStringArray(com.android.internal.R.array.config_ringtoneEffectUris))
+                .thenReturn(new String[] { FAKE_RINGTONE_URI.toString() });
+        mRingerUnderTest.startCallWaiting(mockCall1);
+        Ringtone mockRingtone = mock(Ringtone.class);
+        when(mockRingtoneFactory.getRingtone(any(Call.class))).thenReturn(mockRingtone);
+        when(mockRingtone.getUri()).thenReturn(FAKE_RINGTONE_URI);
+        enableVibrationWhenRinging();
+        assertTrue(mRingerUnderTest.startRinging(mockCall2, false));
+        verify(mockTonePlayer).stopTone();
+        verify(mockRingtonePlayer).play(any(RingtoneFactory.class), any(Call.class));
+        verify(mockVibrator).vibrate(eq(VibrationEffect.get(FAKE_RINGTONE_URI, mContext)),
+                any(AudioAttributes.class));
     }
 
     @SmallTest