Dump of previous vibrations.

It's hard to find previous vibrations when you chase excessive use that
impacts the battery. A dump of previous vibrations allows us to
investigate where the vibrations come from.

Bug: 21933068
Change-Id: I14944732927c73401e5adc7345ea9823092b1883
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 20f6f1c..30f4dce 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -47,7 +47,10 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.ListIterator;
@@ -58,6 +61,8 @@
     private static final boolean DEBUG = false;
 
     private final LinkedList<Vibration> mVibrations;
+    private final LinkedList<VibrationInfo> mPreviousVibrations;
+    private final int mPreviousVibrationsLimit;
     private Vibration mCurrentVibration;
     private final WorkSource mTmpWorkSource = new WorkSource();
     private final Handler mH = new Handler();
@@ -146,6 +151,47 @@
         }
     }
 
+    private static class VibrationInfo {
+        long timeout;
+        long startTime;
+        long[] pattern;
+        int repeat;
+        int usageHint;
+        int uid;
+        String opPkg;
+
+        public VibrationInfo(long timeout, long startTime, long[] pattern, int repeat,
+                int usageHint, int uid, String opPkg) {
+            this.timeout = timeout;
+            this.startTime = startTime;
+            this.pattern = pattern;
+            this.repeat = repeat;
+            this.usageHint = usageHint;
+            this.uid = uid;
+            this.opPkg = opPkg;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder()
+                    .append("timeout: ")
+                    .append(timeout)
+                    .append(", startTime: ")
+                    .append(startTime)
+                    .append(", pattern: ")
+                    .append(Arrays.toString(pattern))
+                    .append(", repeat: ")
+                    .append(repeat)
+                    .append(", usageHint: ")
+                    .append(usageHint)
+                    .append(", uid: ")
+                    .append(uid)
+                    .append(", opPkg: ")
+                    .append(opPkg)
+                    .toString();
+        }
+    }
+
     VibratorService(Context context) {
         // Reset the hardware to a default state, in case this is a runtime
         // restart instead of a fresh boot.
@@ -161,7 +207,11 @@
         mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
-        mVibrations = new LinkedList<Vibration>();
+        mPreviousVibrationsLimit = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
+
+        mVibrations = new LinkedList<>();
+        mPreviousVibrations = new LinkedList<>();
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
@@ -252,6 +302,7 @@
                 removeVibrationLocked(token);
                 doCancelVibrateLocked();
                 mCurrentVibration = vib;
+                addToPreviousVibrationsLocked(vib);
                 startVibrationLocked(vib);
             }
         } finally {
@@ -315,6 +366,7 @@
                     mCurrentVibration = vib;
                     startVibrationLocked(vib);
                 }
+                addToPreviousVibrationsLocked(vib);
             }
         }
         finally {
@@ -322,6 +374,14 @@
         }
     }
 
+    private void addToPreviousVibrationsLocked(Vibration vib) {
+        if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
+            mPreviousVibrations.removeFirst();
+        }
+        mPreviousVibrations.addLast(new VibratorService.VibrationInfo(vib.mTimeout, vib.mStartTime,
+                vib.mPattern, vib.mRepeat, vib.mUsageHint, vib.mUid, vib.mOpPkg));
+    }
+
     @Override // Binder call
     public void cancelVibrate(IBinder token) {
         mContext.enforceCallingOrSelfPermission(
@@ -649,7 +709,6 @@
                 if (!mDone) {
                     // If this vibration finished naturally, start the next
                     // vibration.
-                    mVibrations.remove(mVibration);
                     unlinkVibration(mVibration);
                     startNextVibrationLocked();
                 }
@@ -685,4 +744,23 @@
             }
         }
     };
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+
+            pw.println("Permission Denial: can't dump vibrator service from from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+        pw.println("Previous vibrations:");
+        synchronized (mVibrations) {
+            for (VibrationInfo info : mPreviousVibrations) {
+                pw.print("  ");
+                pw.println(info.toString());
+            }
+        }
+    }
 }