I'm feeling the good vibrations.

When the device enters a non-interactive state, we normally
cancel all active vibrations as a safety precaution.  However if
the system is performing haptic feedback then we want to allow
it to run to completion.

Bug: 14319563
Change-Id: I673781bbf32562e45c1595689e6b423bd178ea73
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 28eb948..09828fb 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -46,6 +46,7 @@
 import com.android.internal.app.IBatteryStats;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.ListIterator;
 
@@ -130,6 +131,10 @@
             }
             return true;
         }
+
+        public boolean isSystemHapticFeedback() {
+            return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0;
+        }
     }
 
     VibratorService(Context context) {
@@ -592,20 +597,32 @@
                 }
             }
         }
-    };
+    }
 
     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                 synchronized (mVibrations) {
-                    doCancelVibrateLocked();
-
-                    int size = mVibrations.size();
-                    for(int i = 0; i < size; i++) {
-                        unlinkVibration(mVibrations.get(i));
+                    // When the system is entering a non-interactive state, we want
+                    // to cancel vibrations in case a misbehaving app has abandoned
+                    // them.  However it may happen that the system is currently playing
+                    // haptic feedback as part of the transition.  So we don't cancel
+                    // system vibrations.
+                    if (mCurrentVibration != null
+                            && !mCurrentVibration.isSystemHapticFeedback()) {
+                        doCancelVibrateLocked();
                     }
 
-                    mVibrations.clear();
+                    // Clear all remaining vibrations.
+                    Iterator<Vibration> it = mVibrations.iterator();
+                    while (it.hasNext()) {
+                        Vibration vibration = it.next();
+                        if (vibration != mCurrentVibration) {
+                            unlinkVibration(vibration);
+                            it.remove();
+                        }
+                    }
                 }
             }
         }