New "app ops" service.

Initial implementation, tracking use of the vibrator, GPS,
and location reports.

Also includes an update to battery stats to also keep track of
vibrator usage (since I had to be in the vibrator code anyway
to instrument it).

The service itself is only half-done.  Currently no API to
retrieve the data (which once there will allow us to show you
which apps are currently causing the GPS to run and who has
recently accessed your location), it doesn't persist its data
like it should, and no way to tell it to reject app requests
for various operations.

But hey, it's a start!

Change-Id: I05b8d76cc4a4f7f37bc758c1701f51f9e0550e15
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index df91dec..69379f1 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +31,7 @@
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Binder;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -39,6 +41,9 @@
 import android.util.Slog;
 import android.view.InputDevice;
 
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IBatteryStats;
+
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.ListIterator;
@@ -54,6 +59,8 @@
 
     private final Context mContext;
     private final PowerManager.WakeLock mWakeLock;
+    private final IAppOpsService mAppOpsService;
+    private final IBatteryStats mBatteryStatsService;
     private InputManager mIm;
 
     volatile VibrateThread mThread;
@@ -64,6 +71,8 @@
     private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
     private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
 
+    private int mCurVibUid = -1;
+
     native static boolean vibratorExists();
     native static void vibratorOn(long milliseconds);
     native static void vibratorOff();
@@ -75,23 +84,25 @@
         private final long[]  mPattern;
         private final int     mRepeat;
         private final int     mUid;
+        private final String  mPackageName;
 
-        Vibration(IBinder token, long millis, int uid) {
-            this(token, millis, null, 0, uid);
+        Vibration(IBinder token, long millis, int uid, String packageName) {
+            this(token, millis, null, 0, uid, packageName);
         }
 
-        Vibration(IBinder token, long[] pattern, int repeat, int uid) {
-            this(token, 0, pattern, repeat, uid);
+        Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
+            this(token, 0, pattern, repeat, uid, packageName);
         }
 
         private Vibration(IBinder token, long millis, long[] pattern,
-                int repeat, int uid) {
+                int repeat, int uid, String packageName) {
             mToken = token;
             mTimeout = millis;
             mStartTime = SystemClock.uptimeMillis();
             mPattern = pattern;
             mRepeat = repeat;
             mUid = uid;
+            mPackageName = packageName;
         }
 
         public void binderDied() {
@@ -131,6 +142,9 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mWakeLock.setReferenceCounted(true);
 
+        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
+        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
         mVibrations = new LinkedList<Vibration>();
 
         IntentFilter filter = new IntentFilter();
@@ -164,7 +178,7 @@
         return doVibratorExists();
     }
 
-    public void vibrate(long milliseconds, IBinder token) {
+    public void vibrate(String packageName, long milliseconds, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
@@ -180,12 +194,18 @@
             return;
         }
 
-        Vibration vib = new Vibration(token, milliseconds, uid);
-        synchronized (mVibrations) {
-            removeVibrationLocked(token);
-            doCancelVibrateLocked();
-            mCurrentVibration = vib;
-            startVibrationLocked(vib);
+        Vibration vib = new Vibration(token, milliseconds, uid, packageName);
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mVibrations) {
+                removeVibrationLocked(token);
+                doCancelVibrateLocked();
+                mCurrentVibration = vib;
+                startVibrationLocked(vib);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -199,7 +219,7 @@
         return true;
     }
 
-    public void vibratePattern(long[] pattern, int repeat, IBinder token) {
+    public void vibratePattern(String packageName, long[] pattern, int repeat, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
@@ -224,7 +244,7 @@
                 return;
             }
 
-            Vibration vib = new Vibration(token, pattern, repeat, uid);
+            Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
             try {
                 token.linkToDeath(vib, 0);
             } catch (RemoteException e) {
@@ -291,11 +311,13 @@
         }
         doVibratorOff();
         mH.removeCallbacks(mVibrationRunnable);
+        reportFinishVibrationLocked();
     }
 
     // Lock held on mVibrations
     private void startNextVibrationLocked() {
         if (mVibrations.size() <= 0) {
+            reportFinishVibrationLocked();
             mCurrentVibration = null;
             return;
         }
@@ -305,8 +327,19 @@
 
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
+        try {
+            int mode = mAppOpsService.startOperation(AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                if (mode == AppOpsManager.MODE_ERRORED) {
+                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
+                }
+                mH.post(mVibrationRunnable);
+                return;
+            }
+        } catch (RemoteException e) {
+        }
         if (vib.mTimeout != 0) {
-            doVibratorOn(vib.mTimeout);
+            doVibratorOn(vib.mTimeout, vib.mUid);
             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
         } else {
             // mThread better be null here. doCancelVibrate should always be
@@ -316,6 +349,17 @@
         }
     }
 
+    private void reportFinishVibrationLocked() {
+        if (mCurrentVibration != null) {
+            try {
+                mAppOpsService.finishOperation(AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
+                        mCurrentVibration.mPackageName);
+            } catch (RemoteException e) {
+            }
+            mCurrentVibration = null;
+        }
+    }
+
     // Lock held on mVibrations
     private Vibration removeVibrationLocked(IBinder token) {
         ListIterator<Vibration> iter = mVibrations.listIterator(0);
@@ -413,8 +457,13 @@
         return vibratorExists();
     }
 
-    private void doVibratorOn(long millis) {
+    private void doVibratorOn(long millis, int uid) {
         synchronized (mInputDeviceVibrators) {
+            try {
+                mBatteryStatsService.noteVibratorOn(uid, millis);
+                mCurVibUid = uid;
+            } catch (RemoteException e) {
+            }
             final int vibratorCount = mInputDeviceVibrators.size();
             if (vibratorCount != 0) {
                 for (int i = 0; i < vibratorCount; i++) {
@@ -428,6 +477,13 @@
 
     private void doVibratorOff() {
         synchronized (mInputDeviceVibrators) {
+            if (mCurVibUid >= 0) {
+                try {
+                    mBatteryStatsService.noteVibratorOff(mCurVibUid);
+                } catch (RemoteException e) {
+                }
+                mCurVibUid = -1;
+            }
             final int vibratorCount = mInputDeviceVibrators.size();
             if (vibratorCount != 0) {
                 for (int i = 0; i < vibratorCount; i++) {
@@ -470,10 +526,11 @@
         public void run() {
             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
             synchronized (this) {
+                final long[] pattern = mVibration.mPattern;
+                final int len = pattern.length;
+                final int repeat = mVibration.mRepeat;
+                final int uid = mVibration.mUid;
                 int index = 0;
-                long[] pattern = mVibration.mPattern;
-                int len = pattern.length;
-                int repeat = mVibration.mRepeat;
                 long duration = 0;
 
                 while (!mDone) {
@@ -493,7 +550,7 @@
                         // duration is saved for delay() at top of loop
                         duration = pattern[index++];
                         if (duration > 0) {
-                            VibratorService.this.doVibratorOn(duration);
+                            VibratorService.this.doVibratorOn(duration, uid);
                         }
                     } else {
                         if (repeat < 0) {