Allow a particular LocationRequest to be excluded from
AppOps monitoring as long as the client as the appropriate
permission (UPDATE_DEVICE_STATS).

Change-Id: I7223a53bc1551e6498302a22eb310c8c5b5684b0
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 7f056c2..cde84dc 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -462,6 +462,7 @@
         final ILocationListener mListener;
         final PendingIntent mPendingIntent;
         final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
+        final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
         final Object mKey;
 
         final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
@@ -474,7 +475,7 @@
         PowerManager.WakeLock mWakeLock;
 
         Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
-                String packageName, WorkSource workSource) {
+                String packageName, WorkSource workSource, boolean hideFromAppOps) {
             mListener = listener;
             mPendingIntent = intent;
             if (listener != null) {
@@ -490,6 +491,7 @@
                 workSource = null;
             }
             mWorkSource = workSource;
+            mHideFromAppOps = hideFromAppOps;
 
             updateMonitoring(true);
 
@@ -532,6 +534,10 @@
         }
 
         public void updateMonitoring(boolean allow) {
+            if (mHideFromAppOps) {
+                return;
+            }
+
             // First update monitoring of any location request (including high power).
             mOpMonitoring = updateMonitoring(allow, mOpMonitoring,
                     AppOpsManager.OP_MONITOR_LOCATION);
@@ -931,11 +937,16 @@
      * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
      * for battery).
      */
-    private void checkWorkSourceAllowed() {
+    private void checkDeviceStatsAllowed() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.UPDATE_DEVICE_STATS, null);
     }
 
+    private void checkUpdateAppOpsAllowed() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
+    }
+
     public static int resolutionLevelToOp(int allowedResolutionLevel) {
         if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
             if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1260,11 +1271,12 @@
     }
 
     private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
-            String packageName, WorkSource workSource) {
+            String packageName, WorkSource workSource, boolean hideFromAppOps) {
         IBinder binder = listener.asBinder();
         Receiver receiver = mReceivers.get(binder);
         if (receiver == null) {
-            receiver = new Receiver(listener, null, pid, uid, packageName, workSource);
+            receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
+                    hideFromAppOps);
             mReceivers.put(binder, receiver);
 
             try {
@@ -1278,10 +1290,11 @@
     }
 
     private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
-            WorkSource workSource) {
+            WorkSource workSource, boolean hideFromAppOps) {
         Receiver receiver = mReceivers.get(intent);
         if (receiver == null) {
-            receiver = new Receiver(null, intent, pid, uid, packageName, workSource);
+            receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
+                    hideFromAppOps);
             mReceivers.put(intent, receiver);
         }
         return receiver;
@@ -1343,16 +1356,16 @@
     }
 
     private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
-            int pid, int uid, String packageName, WorkSource workSource) {
+            int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
         if (intent == null && listener == null) {
             throw new IllegalArgumentException("need either listener or intent");
         } else if (intent != null && listener != null) {
             throw new IllegalArgumentException("cannot register both listener and intent");
         } else if (intent != null) {
             checkPendingIntent(intent);
-            return getReceiverLocked(intent, pid, uid, packageName, workSource);
+            return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
         } else {
-            return getReceiverLocked(listener, pid, uid, packageName, workSource);
+            return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
         }
     }
 
@@ -1366,7 +1379,11 @@
                 request.getProvider());
         WorkSource workSource = request.getWorkSource();
         if (workSource != null && workSource.size() > 0) {
-            checkWorkSourceAllowed();
+            checkDeviceStatsAllowed();
+        }
+        boolean hideFromAppOps = request.getHideFromAppOps();
+        if (hideFromAppOps) {
+            checkUpdateAppOpsAllowed();
         }
         LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel);
 
@@ -1381,7 +1398,7 @@
 
             synchronized (mLock) {
                 Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                        packageName, workSource);
+                        packageName, workSource, hideFromAppOps);
                 requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
             }
         } finally {
@@ -1434,8 +1451,9 @@
 
         synchronized (mLock) {
             WorkSource workSource = null;
-            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid, packageName,
-                    workSource);
+            boolean hideFromAppOps = false;
+            Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
+                    packageName, workSource, hideFromAppOps);
 
             // providers may use public location API's, need to clear identity
             long identity = Binder.clearCallingIdentity();