Initial stab at background check.

Actually, this implementation is more what we want for ephemeral
apps.  I am realizing the two are not really the same thing. :(

For this implementation, we now keep track of how long a uid has
been in the background, and after a certain amount of time
(currently 1 minute) we mark it as "idle".  Any packages associated
with that uid are then no longer allowed to run in the background.
This means, until the app next goes in the foreground:

- No manifest broadcast receivers in the app will execute.
- No services can be started (binding services is still okay,
  as this is outside dependencies on the app that should still
  be represented).
- All alarms for the app are cancelled and no more can be set.
- All jobs for the app are cancelled and no more can be scheduled.
- All syncs for the app are cancelled and no more can be requested.

Change-Id: If53714ca4beed35faf2e89f916ce9eaaabd9290d
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 96c1e2a..c131628 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -54,7 +54,6 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -557,12 +556,12 @@
                 ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
                 try {
                     if (reportedPackageNames == null) {
-                        callback.mCallback.opChanged(code, null);
+                        callback.mCallback.opChanged(code, uid, null);
                     } else {
                         final int reportedPackageCount = reportedPackageNames.size();
                         for (int j = 0; j < reportedPackageCount; j++) {
                             String reportedPackageName = reportedPackageNames.valueAt(j);
-                            callback.mCallback.opChanged(code, reportedPackageName);
+                            callback.mCallback.opChanged(code, uid, reportedPackageName);
                         }
                     }
                 } catch (RemoteException e) {
@@ -620,7 +619,7 @@
             try {
                 for (int i = 0; i < repCbs.size(); i++) {
                     try {
-                        repCbs.get(i).mCallback.opChanged(code, packageName);
+                        repCbs.get(i).mCallback.opChanged(code, uid, packageName);
                     } catch (RemoteException e) {
                     }
                 }
@@ -630,39 +629,51 @@
         }
     }
 
-    private static HashMap<Callback, ArrayList<Pair<String, Integer>>> addCallbacks(
-            HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks,
-            String packageName, int op, ArrayList<Callback> cbs) {
+    private static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks(
+            HashMap<Callback, ArrayList<ChangeRec>> callbacks,
+            int op, int uid, String packageName, ArrayList<Callback> cbs) {
         if (cbs == null) {
             return callbacks;
         }
         if (callbacks == null) {
-            callbacks = new HashMap<Callback, ArrayList<Pair<String, Integer>>>();
+            callbacks = new HashMap<>();
         }
         boolean duplicate = false;
         for (int i=0; i<cbs.size(); i++) {
             Callback cb = cbs.get(i);
-            ArrayList<Pair<String, Integer>> reports = callbacks.get(cb);
+            ArrayList<ChangeRec> reports = callbacks.get(cb);
             if (reports == null) {
-                reports = new ArrayList<Pair<String, Integer>>();
+                reports = new ArrayList<>();
                 callbacks.put(cb, reports);
             } else {
                 final int reportCount = reports.size();
                 for (int j = 0; j < reportCount; j++) {
-                    Pair<String, Integer> report = reports.get(j);
-                    if (report.second == op && report.first.equals(packageName)) {
+                    ChangeRec report = reports.get(j);
+                    if (report.op == op && report.pkg.equals(packageName)) {
                         duplicate = true;
                         break;
                     }
                 }
             }
             if (!duplicate) {
-                reports.add(new Pair<>(packageName, op));
+                reports.add(new ChangeRec(op, uid, packageName));
             }
         }
         return callbacks;
     }
 
+    static final class ChangeRec {
+        final int op;
+        final int uid;
+        final String pkg;
+
+        ChangeRec(int _op, int _uid, String _pkg) {
+            op = _op;
+            uid = _uid;
+            pkg = _pkg;
+        }
+    }
+
     @Override
     public void resetAllModes(int reqUserId, String reqPackageName) {
         final int callingPid = Binder.getCallingPid();
@@ -682,7 +693,7 @@
             }
         }
 
-        HashMap<Callback, ArrayList<Pair<String, Integer>>> callbacks = null;
+        HashMap<Callback, ArrayList<ChangeRec>> callbacks = null;
         synchronized (this) {
             boolean changed = false;
             for (int i = mUidStates.size() - 1; i >= 0; i--) {
@@ -699,9 +710,9 @@
                                 uidState.opModes = null;
                             }
                             for (String packageName : getPackagesForUid(uidState.uid)) {
-                                callbacks = addCallbacks(callbacks, packageName, code,
+                                callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
                                         mOpModeWatchers.get(code));
-                                callbacks = addCallbacks(callbacks, packageName, code,
+                                callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
                                         mPackageModeWatchers.get(packageName));
                             }
                         }
@@ -734,9 +745,9 @@
                                 && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
                             curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
                             changed = true;
-                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
+                            callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
                                     mOpModeWatchers.get(curOp.op));
-                            callbacks = addCallbacks(callbacks, packageName, curOp.op,
+                            callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
                                     mPackageModeWatchers.get(packageName));
                             if (curOp.time == 0 && curOp.rejectTime == 0) {
                                 pkgOps.removeAt(j);
@@ -757,13 +768,13 @@
             }
         }
         if (callbacks != null) {
-            for (Map.Entry<Callback, ArrayList<Pair<String, Integer>>> ent : callbacks.entrySet()) {
+            for (Map.Entry<Callback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
                 Callback cb = ent.getKey();
-                ArrayList<Pair<String, Integer>> reports = ent.getValue();
+                ArrayList<ChangeRec> reports = ent.getValue();
                 for (int i=0; i<reports.size(); i++) {
-                    Pair<String, Integer> rep = reports.get(i);
+                    ChangeRec rep = reports.get(i);
                     try {
-                        cb.mCallback.opChanged(rep.second, rep.first);
+                        cb.mCallback.opChanged(rep.op, rep.uid, rep.pkg);
                     } catch (RemoteException e) {
                     }
                 }
@@ -1163,8 +1174,10 @@
                     if (pkgUid != uid) {
                         // Oops!  The package name is not valid for the uid they are calling
                         // under.  Abort.
+                        RuntimeException ex = new RuntimeException("here");
+                        ex.fillInStackTrace();
                         Slog.w(TAG, "Bad call: specified package " + packageName
-                                + " under uid " + uid + " but it is really " + pkgUid);
+                                + " under uid " + uid + " but it is really " + pkgUid, ex);
                         return null;
                     }
                 } finally {