App ops: track system windows, monitoring changes.

Change-Id: I273e82bdad66ada3bf0f7ec9176bc304b9ee1ee8
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 748b3cb..e94d03c 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -25,6 +25,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 
 import android.app.AppOpsManager;
@@ -34,7 +35,9 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.AtomicFile;
@@ -45,6 +48,7 @@
 import android.util.Xml;
 
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -82,7 +86,7 @@
     final SparseArray<HashMap<String, Ops>> mUidOps
             = new SparseArray<HashMap<String, Ops>>();
 
-    final static class Ops extends SparseArray<Op> {
+    public final static class Ops extends SparseArray<Op> {
         public final String packageName;
         public final int uid;
 
@@ -92,7 +96,7 @@
         }
     }
 
-    final static class Op {
+    public final static class Op {
         public final int op;
         public int mode;
         public int duration;
@@ -106,6 +110,34 @@
         }
     }
 
+    final SparseArray<ArrayList<Callback>> mOpModeWatchers
+            = new SparseArray<ArrayList<Callback>>();
+    final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
+            = new HashMap<String, ArrayList<Callback>>();
+    final HashMap<IBinder, Callback> mModeWatchers
+            = new HashMap<IBinder, Callback>();
+
+    public final class Callback implements DeathRecipient {
+        final IAppOpsCallback mCallback;
+
+        public Callback(IAppOpsCallback callback) {
+            mCallback = callback;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void unlinkToDeath() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            stopWatchingMode(mCallback);
+        }
+    }
+
     public AppOpsService(File storagePath) {
         mFile = new AtomicFile(storagePath);
         mHandler = new Handler();
@@ -205,15 +237,94 @@
     public void setMode(int code, int uid, String packageName, int mode) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
+        ArrayList<Callback> repCbs = null;
+        code = AppOpsManager.opToSwitch(code);
         synchronized (this) {
-            Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, true);
+            Op op = getOpLocked(code, uid, packageName, true);
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
+                    ArrayList<Callback> cbs = mOpModeWatchers.get(code);
+                    if (cbs != null) {
+                        if (repCbs == null) {
+                            repCbs = new ArrayList<Callback>();
+                        }
+                        repCbs.addAll(cbs);
+                    }
+                    cbs = mPackageModeWatchers.get(packageName);
+                    if (cbs != null) {
+                        if (repCbs == null) {
+                            repCbs = new ArrayList<Callback>();
+                        }
+                        repCbs.addAll(cbs);
+                    }
                     scheduleWriteNowLocked();
                 }
             }
         }
+        if (repCbs != null) {
+            for (int i=0; i<repCbs.size(); i++) {
+                try {
+                    repCbs.get(i).mCallback.opChanged(code, packageName);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+
+    @Override
+    public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
+        synchronized (this) {
+            op = AppOpsManager.opToSwitch(op);
+            Callback cb = mModeWatchers.get(callback.asBinder());
+            if (cb == null) {
+                cb = new Callback(callback);
+                mModeWatchers.put(callback.asBinder(), cb);
+            }
+            if (op != AppOpsManager.OP_NONE) {
+                ArrayList<Callback> cbs = mOpModeWatchers.get(op);
+                if (cbs == null) {
+                    cbs = new ArrayList<Callback>();
+                    mOpModeWatchers.put(op, cbs);
+                }
+                cbs.add(cb);
+            }
+            if (packageName != null) {
+                ArrayList<Callback> cbs = mPackageModeWatchers.get(packageName);
+                if (cbs == null) {
+                    cbs = new ArrayList<Callback>();
+                    mPackageModeWatchers.put(packageName, cbs);
+                }
+                cbs.add(cb);
+            }
+        }
+    }
+
+    @Override
+    public void stopWatchingMode(IAppOpsCallback callback) {
+        synchronized (this) {
+            Callback cb = mModeWatchers.remove(callback.asBinder());
+            if (cb != null) {
+                cb.unlinkToDeath();
+                for (int i=0; i<mOpModeWatchers.size(); i++) {
+                    ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
+                    cbs.remove(cb);
+                    if (cbs.size() <= 0) {
+                        mOpModeWatchers.removeAt(i);
+                    }
+                }
+                if (mPackageModeWatchers.size() > 0) {
+                    Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
+                    while (it.hasNext()) {
+                        ArrayList<Callback> cbs = it.next();
+                        cbs.remove(cb);
+                        if (cbs.size() <= 0) {
+                            it.remove();
+                        }
+                    }
+                }
+            }
+        }
     }
 
     @Override