Add vibration code to Ringer

When there's a new incoming call, vibrate (according to preferences
and volume settings) the device as well as ringing it.

Bug: 13365911
Change-Id: Id67999a173d36aa4d8472a52ac4ab95eaaea0fb0
diff --git a/src/com/android/telecomm/Ringer.java b/src/com/android/telecomm/Ringer.java
index bb32cc3..fdc9349 100644
--- a/src/com/android/telecomm/Ringer.java
+++ b/src/com/android/telecomm/Ringer.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.media.AudioManager;
+import android.os.SystemVibrator;
+import android.os.Vibrator;
+import android.provider.Settings;
 import android.telecomm.CallState;
 
 import com.google.common.collect.Lists;
@@ -28,6 +31,15 @@
  * Controls the ringtone player.
  */
 final class Ringer extends CallsManagerListenerBase {
+    private static final long[] VIBRATION_PATTERN = new long[] {
+        0, // No delay before starting
+        1000, // How long to vibrate
+        1000, // How long to wait before vibrating again
+    };
+
+    /** Indicate that we want the pattern to repeat at the step which turns on vibration. */
+    private static final int VIBRATION_PATTERN_REPEAT = 1;
+
     private final AsyncRingtonePlayer mRingtonePlayer = new AsyncRingtonePlayer();
 
     /**
@@ -38,8 +50,19 @@
 
     private final CallAudioManager mCallAudioManager;
 
+    private final Vibrator mVibrator;
+
+    /**
+     * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls.
+     */
+    private boolean mIsVibrating = false;
+
     Ringer(CallAudioManager callAudioManager) {
         mCallAudioManager = callAudioManager;
+
+        // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
+        // vibrator object will be isolated from others.
+        mVibrator = new SystemVibrator(TelecommApp.getInstance());
     }
 
     @Override
@@ -111,6 +134,12 @@
         } else {
             Log.v(this, "startRinging, skipping because volume is 0");
         }
+
+        if (shouldVibrate(TelecommApp.getInstance()) && !mIsVibrating) {
+            mVibrator.vibrate(VIBRATION_PATTERN, VIBRATION_PATTERN_REPEAT,
+                    AudioManager.STREAM_RING);
+            mIsVibrating = true;
+        }
     }
 
     private void stopRinging() {
@@ -119,5 +148,28 @@
         // Even though stop is asynchronous it's ok to update the audio manager. Things like audio
         // focus are voluntary so releasing focus too early is not detrimental.
         mCallAudioManager.setIsRinging(false);
+
+        if (mIsVibrating) {
+            mVibrator.cancel();
+            mIsVibrating = false;
+        }
+    }
+
+    private boolean shouldVibrate(Context context) {
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = audioManager.getRingerMode();
+        if (getVibrateWhenRinging(context)) {
+            return ringerMode != AudioManager.RINGER_MODE_SILENT;
+        } else {
+            return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+        }
+    }
+
+    private boolean getVibrateWhenRinging(Context context) {
+        if (!mVibrator.hasVibrator()) {
+            return false;
+        }
+        return Settings.System.getInt(context.getContentResolver(),
+                Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
     }
 }