App ops: you can now turn off operations.

Also add new ops for calendar and wi-fi scans, finish
implementing rejection of content provider calls, fix
issues with rejecting location calls, fix bug in the
new pm call to retrieve apps with permissions.

Change-Id: I29d9f8600bfbbf6561abf6d491907e2bbf6af417
diff --git a/api/current.txt b/api/current.txt
index 81e5d4f..0c9e16a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5122,6 +5122,7 @@
     method public int bulkInsert(android.net.Uri, android.content.ContentValues[]);
     method public android.os.Bundle call(java.lang.String, java.lang.String, android.os.Bundle);
     method public abstract int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final android.content.Context getContext();
     method public final android.content.pm.PathPermission[] getPathPermissions();
     method public final java.lang.String getReadPermission();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5d24d69..b1d0305 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -44,19 +44,44 @@
     public static final int OP_WRITE_CONTACTS = 5;
     public static final int OP_READ_CALL_LOG = 6;
     public static final int OP_WRITE_CALL_LOG = 7;
+    public static final int OP_READ_CALENDAR = 8;
+    public static final int OP_WRITE_CALENDAR = 9;
+    public static final int OP_WIFI_SCAN = 10;
 
-    public static String opToString(int op) {
-        switch (op) {
-            case OP_COARSE_LOCATION: return "COARSE_LOCATION";
-            case OP_FINE_LOCATION: return "FINE_LOCATION";
-            case OP_GPS: return "GPS";
-            case OP_VIBRATE: return "VIBRATE";
-            case OP_READ_CONTACTS: return "READ_CONTACTS";
-            case OP_WRITE_CONTACTS: return "WRITE_CONTACTS";
-            case OP_READ_CALL_LOG: return "READ_CALL_LOG";
-            case OP_WRITE_CALL_LOG: return "WRITE_CALL_LOG";
-            default: return "Unknown(" + op + ")";
-        }
+    private static String[] sOpNames = new String[] {
+        "COARSE_LOCATION",
+        "FINE_LOCATION",
+        "GPS",
+        "VIBRATE",
+        "READ_CONTACTS",
+        "WRITE_CONTACTS",
+        "READ_CALL_LOG",
+        "WRITE_CALL_LOG",
+        "READ_CALENDAR",
+        "WRITE_CALENDAR",
+        "WIFI_SCAN",
+    };
+
+    private static String[] sOpPerms = new String[] {
+        android.Manifest.permission.ACCESS_COARSE_LOCATION,
+        android.Manifest.permission.ACCESS_FINE_LOCATION,
+        android.Manifest.permission.ACCESS_FINE_LOCATION,
+        android.Manifest.permission.VIBRATE,
+        android.Manifest.permission.READ_CONTACTS,
+        android.Manifest.permission.WRITE_CONTACTS,
+        android.Manifest.permission.READ_CALL_LOG,
+        android.Manifest.permission.WRITE_CALL_LOG,
+        android.Manifest.permission.READ_CALENDAR,
+        android.Manifest.permission.WRITE_CALENDAR,
+        android.Manifest.permission.ACCESS_WIFI_STATE,
+    };
+
+    public static String opToName(int op) {
+        return op < sOpNames.length ? sOpNames[op] : ("Unknown(" + op + ")");
+    }
+
+    public static String opToPermission(int op) {
+        return sOpPerms[op];
     }
 
     public static class PackageOps implements Parcelable {
@@ -120,12 +145,16 @@
 
     public static class OpEntry implements Parcelable {
         private final int mOp;
+        private final int mMode;
         private final long mTime;
+        private final long mRejectTime;
         private final int mDuration;
 
-        public OpEntry(int op, long time, int duration) {
+        public OpEntry(int op, int mode, long time, long rejectTime, int duration) {
             mOp = op;
+            mMode = mode;
             mTime = time;
+            mRejectTime = rejectTime;
             mDuration = duration;
         }
 
@@ -133,10 +162,18 @@
             return mOp;
         }
 
+        public int getMode() {
+            return mMode;
+        }
+
         public long getTime() {
             return mTime;
         }
 
+        public long getRejectTime() {
+            return mRejectTime;
+        }
+
         public boolean isRunning() {
             return mDuration == -1;
         }
@@ -153,13 +190,17 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mOp);
+            dest.writeInt(mMode);
             dest.writeLong(mTime);
+            dest.writeLong(mRejectTime);
             dest.writeInt(mDuration);
         }
 
         OpEntry(Parcel source) {
             mOp = source.readInt();
+            mMode = source.readInt();
             mTime = source.readLong();
+            mRejectTime = source.readLong();
             mDuration = source.readInt();
         }
 
@@ -195,6 +236,13 @@
         return null;
     }
 
+    public void setMode(int code, int uid, String packageName, int mode) {
+        try {
+            mService.setMode(code, uid, packageName, mode);
+        } catch (RemoteException e) {
+        }
+    }
+
     public int checkOp(int op, int uid, String packageName) {
         try {
             int mode = mService.checkOperation(op, uid, packageName);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a6f7abc..4b977ab 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -190,8 +190,13 @@
         public Cursor query(String callingPkg, Uri uri, String[] projection,
                 String selection, String[] selectionArgs, String sortOrder,
                 ICancellationSignal cancellationSignal) {
-            // XXX need content provider to help return correct result.
-            enforceReadPermission(callingPkg, uri);
+            if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+                // The read is not allowed...  to fake it out, we replace the given
+                // selection statement with a dummy one that will always be false.
+                // This way we will get a cursor back that has the correct structure
+                // but contains no rows.
+                selection = "'A' = 'B'";
+            }
             return ContentProvider.this.query(uri, projection, selection, selectionArgs, sortOrder,
                     CancellationSignal.fromTransport(cancellationSignal));
         }
@@ -203,8 +208,14 @@
 
         @Override
         public Uri insert(String callingPkg, Uri uri, ContentValues initialValues) {
-            // XXX need content provider to help return correct result.
-            enforceWritePermission(callingPkg, uri);
+            if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
+                // If not allowed, we need to return some reasonable URI.  Maybe the
+                // content provider should be responsible for this, but for now we
+                // will just return the base URI with a dummy '0' tagged on to it.
+                // You shouldn't be able to read if you can't write, anyway, so it
+                // shouldn't matter much what is returned.
+                return uri.buildUpon().appendPath("0").build();
+            }
             return ContentProvider.this.insert(uri, initialValues);
         }
 
@@ -1189,12 +1200,10 @@
      * Print the Provider's state into the given stream.  This gets invoked if
      * you run "adb shell dumpsys activity provider &lt;provider_component_name&gt;".
      *
-     * @param prefix Desired prefix to prepend at each line of output.
      * @param fd The raw file descriptor that the dump is being sent to.
      * @param writer The PrintWriter to which you should dump your state.  This will be
      * closed for you after you return.
      * @param args additional arguments to the dump request.
-     * @hide
      */
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         writer.println("nothing to dump");
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 69118fe..b79bdee 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -11,7 +11,7 @@
  */
 public class WorkSource implements Parcelable {
     static final String TAG = "WorkSource";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     int mNum;
     int[] mUids;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index a4eb4c5..c4f1bc48 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -21,6 +21,7 @@
 interface IAppOpsService {
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
+    void setMode(int code, int uid, String packageName, int mode);
     int checkOperation(int code, int uid, String packageName);
     int noteOperation(int code, int uid, String packageName);
     int startOperation(int code, int uid, String packageName);
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 3d9ddae..bf2a5ae 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -38,6 +38,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.AtomicFile;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -93,12 +94,15 @@
 
     final static class Op {
         public final int op;
+        public int mode;
         public int duration;
         public long time;
+        public long rejectTime;
         public int nesting;
 
         public Op(int _op) {
             op = _op;
+            mode = AppOpsManager.MODE_ALLOWED;
         }
     }
 
@@ -133,8 +137,8 @@
             resOps = new ArrayList<AppOpsManager.OpEntry>();
             for (int j=0; j<pkgOps.size(); j++) {
                 Op curOp = pkgOps.valueAt(j);
-                resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.time,
-                        curOp.duration));
+                resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
+                        curOp.rejectTime, curOp.duration));
             }
         } else {
             for (int j=0; j<ops.length; j++) {
@@ -143,8 +147,8 @@
                     if (resOps == null) {
                         resOps = new ArrayList<AppOpsManager.OpEntry>();
                     }
-                    resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.time,
-                            curOp.duration));
+                    resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
+                            curOp.rejectTime, curOp.duration));
                 }
             }
         }
@@ -198,6 +202,20 @@
     }
 
     @Override
+    public void setMode(int code, int uid, String packageName, int mode) {
+        uid = handleIncomingUid(uid);
+        synchronized (this) {
+            Op op = getOpLocked(code, uid, packageName, true);
+            if (op != null) {
+                if (op.mode != mode) {
+                    op.mode = mode;
+                    scheduleWriteNowLocked();
+                }
+            }
+        }
+    }
+
+    @Override
     public int checkOperation(int code, int uid, String packageName) {
         uid = handleIncomingUid(uid);
         synchronized (this) {
@@ -205,8 +223,8 @@
             if (op == null) {
                 return AppOpsManager.MODE_ALLOWED;
             }
+            return op.mode;
         }
-        return AppOpsManager.MODE_ALLOWED;
     }
 
     @Override
@@ -215,16 +233,26 @@
         synchronized (this) {
             Op op = getOpLocked(code, uid, packageName, true);
             if (op == null) {
+                if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+                        + " package " + packageName);
                 return AppOpsManager.MODE_IGNORED;
             }
             if (op.duration == -1) {
                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
                         + " code " + code + " time=" + op.time + " duration=" + op.duration);
             }
-            op.time = System.currentTimeMillis();
             op.duration = 0;
+            if (op.mode != AppOpsManager.MODE_ALLOWED) {
+                if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code " + code
+                        + " uid " + uid + " package " + packageName);
+                op.rejectTime = System.currentTimeMillis();
+                return op.mode;
+            }
+            if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+                    + " package " + packageName);
+            op.time = System.currentTimeMillis();
+            return AppOpsManager.MODE_ALLOWED;
         }
-        return AppOpsManager.MODE_ALLOWED;
     }
 
     @Override
@@ -233,15 +261,25 @@
         synchronized (this) {
             Op op = getOpLocked(code, uid, packageName, true);
             if (op == null) {
+                if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+                        + " package " + packageName);
                 return AppOpsManager.MODE_IGNORED;
             }
+            if (op.mode != AppOpsManager.MODE_ALLOWED) {
+                if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code " + code
+                        + " uid " + uid + " package " + packageName);
+                op.rejectTime = System.currentTimeMillis();
+                return op.mode;
+            }
+            if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+                    + " package " + packageName);
             if (op.nesting == 0) {
                 op.time = System.currentTimeMillis();
                 op.duration = -1;
             }
             op.nesting++;
+            return AppOpsManager.MODE_ALLOWED;
         }
-        return AppOpsManager.MODE_ALLOWED;
     }
 
     @Override
@@ -319,6 +357,21 @@
         return ops;
     }
 
+    private void scheduleWriteLocked() {
+        if (!mWriteScheduled) {
+            mWriteScheduled = true;
+            mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
+        }
+    }
+
+    private void scheduleWriteNowLocked() {
+        if (!mWriteScheduled) {
+            mWriteScheduled = true;
+        }
+        mHandler.removeCallbacks(mWriteRunner);
+        mHandler.post(mWriteRunner);
+    }
+
     private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
         Ops ops = getOpsLocked(uid, packageName, edit);
         if (ops == null) {
@@ -332,9 +385,8 @@
             op = new Op(code);
             ops.put(code, op);
         }
-        if (edit && !mWriteScheduled) {
-            mWriteScheduled = true;
-            mHandler.postDelayed(mWriteRunner, WRITE_DELAY);
+        if (edit) {
+            scheduleWriteLocked();
         }
         return op;
     }
@@ -441,8 +493,22 @@
             String tagName = parser.getName();
             if (tagName.equals("op")) {
                 Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
-                op.time = Long.parseLong(parser.getAttributeValue(null, "t"));
-                op.duration = Integer.parseInt(parser.getAttributeValue(null, "d"));
+                String mode = parser.getAttributeValue(null, "m");
+                if (mode != null) {
+                    op.mode = Integer.parseInt(mode);
+                }
+                String time = parser.getAttributeValue(null, "t");
+                if (time != null) {
+                    op.time = Long.parseLong(time);
+                }
+                time = parser.getAttributeValue(null, "r");
+                if (time != null) {
+                    op.rejectTime = Long.parseLong(time);
+                }
+                String dur = parser.getAttributeValue(null, "d");
+                if (dur != null) {
+                    op.duration = Integer.parseInt(dur);
+                }
                 HashMap<String, Ops> pkgOps = mUidOps.get(uid);
                 if (pkgOps == null) {
                     pkgOps = new HashMap<String, Ops>();
@@ -499,8 +565,21 @@
                             AppOpsManager.OpEntry op = ops.get(j);
                             out.startTag(null, "op");
                             out.attribute(null, "n", Integer.toString(op.getOp()));
-                            out.attribute(null, "t", Long.toString(op.getTime()));
-                            out.attribute(null, "d", Integer.toString(op.getDuration()));
+                            if (op.getMode() != AppOpsManager.MODE_ALLOWED) {
+                                out.attribute(null, "m", Integer.toString(op.getMode()));
+                            }
+                            long time = op.getTime();
+                            if (time != 0) {
+                                out.attribute(null, "t", Long.toString(time));
+                            }
+                            time = op.getRejectTime();
+                            if (time != 0) {
+                                out.attribute(null, "r", Long.toString(time));
+                            }
+                            int dur = op.getDuration();
+                            if (dur != 0) {
+                                out.attribute(null, "d", Integer.toString(dur));
+                            }
                             out.endTag(null, "op");
                         }
                         out.endTag(null, "uid");
@@ -532,6 +611,7 @@
 
         synchronized (this) {
             pw.println("Current AppOps Service state:");
+            final long now = System.currentTimeMillis();
             for (int i=0; i<mUidOps.size(); i++) {
                 pw.print("  Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
                 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
@@ -539,10 +619,16 @@
                     pw.print("    Package "); pw.print(ops.packageName); pw.println(":");
                     for (int j=0; j<ops.size(); j++) {
                         Op op = ops.valueAt(j);
-                        pw.print("      "); pw.print(AppOpsManager.opToString(op.op));
-                        pw.print(": time=");
-                        TimeUtils.formatDuration(System.currentTimeMillis()-op.time, pw);
-                        pw.print(" ago");
+                        pw.print("      "); pw.print(AppOpsManager.opToName(op.op));
+                        pw.print(": mode="); pw.print(op.mode);
+                        if (op.time != 0) {
+                            pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
+                            pw.print(" ago");
+                        }
+                        if (op.rejectTime != 0) {
+                            pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
+                            pw.print(" ago");
+                        }
                         if (op.duration == -1) {
                             pw.println(" (running)");
                         } else {
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index b351fc2..f63719e 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -529,9 +529,6 @@
         }
 
         public boolean callLocationChangedLocked(Location location) {
-            if (!reportLocationAccessNoThrow(mUid, mPackageName, mAllowedResolutionLevel)) {
-                return true;
-            }
             if (mListener != null) {
                 try {
                     synchronized (this) {
@@ -802,14 +799,20 @@
         }
     }
 
-    boolean reportLocationAccessNoThrow(int uid, String packageName, int allowedResolutionLevel) {
-        int op;
+    public static int resolutionLevelToOp(int allowedResolutionLevel) {
         if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
             if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
-                op = AppOpsManager.OP_COARSE_LOCATION;
+                return AppOpsManager.OP_COARSE_LOCATION;
             } else {
-                op = AppOpsManager.OP_FINE_LOCATION;
+                return AppOpsManager.OP_FINE_LOCATION;
             }
+        }
+        return -1;
+    }
+
+    boolean reportLocationAccessNoThrow(int uid, String packageName, int allowedResolutionLevel) {
+        int op = resolutionLevelToOp(allowedResolutionLevel);
+        if (op >= 0) {
             if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
@@ -818,13 +821,8 @@
     }
 
     boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) {
-        int op;
-        if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
-            if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
-                op = AppOpsManager.OP_COARSE_LOCATION;
-            } else {
-                op = AppOpsManager.OP_FINE_LOCATION;
-            }
+        int op = resolutionLevelToOp(allowedResolutionLevel);
+        if (op >= 0) {
             if (mAppOps.checkOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
@@ -1019,11 +1017,14 @@
         if (records != null) {
             for (UpdateRecord record : records) {
                 if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
-                    LocationRequest locationRequest = record.mRequest;
-                    providerRequest.locationRequests.add(locationRequest);
-                    if (locationRequest.getInterval() < providerRequest.interval) {
-                        providerRequest.reportLocation = true;
-                        providerRequest.interval = locationRequest.getInterval();
+                    if (checkLocationAccess(record.mReceiver.mUid, record.mReceiver.mPackageName,
+                            record.mReceiver.mAllowedResolutionLevel)) {
+                        LocationRequest locationRequest = record.mRequest;
+                        providerRequest.locationRequests.add(locationRequest);
+                        if (locationRequest.getInterval() < providerRequest.interval) {
+                            providerRequest.reportLocation = true;
+                            providerRequest.interval = locationRequest.getInterval();
+                        }
                     }
                 }
             }
@@ -1144,9 +1145,6 @@
      * and consistency requirements.
      *
      * @param request the LocationRequest from which to create a sanitized version
-     * @param shouldBeCoarse whether the sanitized version should be held to coarse resolution
-     * constraints
-     * @param fastestCoarseIntervalMS minimum interval allowed for coarse resolution
      * @return a version of request that meets the given resolution and consistency requirements
      * @hide
      */
@@ -1340,16 +1338,18 @@
         final int uid = Binder.getCallingUid();
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (!reportLocationAccessNoThrow(uid, packageName, allowedResolutionLevel)) {
-                return null;
-            }
-            
             if (mBlacklist.isBlacklisted(packageName)) {
                 if (D) Log.d(TAG, "not returning last loc for blacklisted app: " +
                         packageName);
                 return null;
             }
 
+            if (!reportLocationAccessNoThrow(uid, packageName, allowedResolutionLevel)) {
+                if (D) Log.d(TAG, "not returning last loc for no op app: " +
+                        packageName);
+                return null;
+            }
+
             synchronized (mLock) {
                 // Figure out the provider. Either its explicitly request (deprecated API's),
                 // or use the fused provider
@@ -1402,7 +1402,8 @@
         }
         long identity = Binder.clearCallingIdentity();
         try {
-            mGeofenceManager.addFence(sanitizedRequest, geofence, intent, uid, packageName);
+            mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
+                    uid, packageName);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1703,6 +1704,13 @@
                 continue;
             }
 
+            if (!reportLocationAccessNoThrow(receiver.mUid, receiver.mPackageName,
+                    receiver.mAllowedResolutionLevel)) {
+                if (D) Log.d(TAG, "skipping loc update for no op app: " +
+                        receiver.mPackageName);
+                continue;
+            }
+
             Location notifyLocation = null;
             if (receiver.mAllowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
                 notifyLocation = coarseLocation;  // use coarse location
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 5789a53..ad6eb4d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -18,6 +18,7 @@
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -127,6 +128,7 @@
     private int mMulticastDisabled;
 
     private final IBatteryStats mBatteryStats;
+    private final AppOpsManager mAppOps;
 
     private boolean mEnableTrafficStatsPoll = false;
     private int mTrafficStatsPollToken = 0;
@@ -381,6 +383,7 @@
         mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
         mWifiStateMachine.enableRssiPolling(true);
         mBatteryStats = BatteryStatsService.getService();
+        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
 
         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
         Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
@@ -845,10 +848,15 @@
      * a list of {@link ScanResult} objects.
      * @return the list of results
      */
-    public List<ScanResult> getScanResults() {
+    public List<ScanResult> getScanResults(String callingPackage) {
         enforceAccessPermission();
         int userId = UserHandle.getCallingUserId();
+        int uid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
+        if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
+                != AppOpsManager.MODE_ALLOWED) {
+            return new ArrayList<ScanResult>();
+        }
         try {
             int currentUser = ActivityManager.getCurrentUser();
             if (userId != currentUser) {
diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java
index f9be719..e24bf76 100644
--- a/services/java/com/android/server/location/GeofenceManager.java
+++ b/services/java/com/android/server/location/GeofenceManager.java
@@ -21,6 +21,7 @@
 import java.util.LinkedList;
 import java.util.List;
 
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -68,6 +69,7 @@
 
     private final Context mContext;
     private final LocationManager mLocationManager;
+    private final AppOpsManager mAppOps;
     private final PowerManager.WakeLock mWakeLock;
     private final GeofenceHandler mHandler;
     private final LocationBlacklist mBlacklist;
@@ -107,6 +109,7 @@
     public GeofenceManager(Context context, LocationBlacklist blacklist) {
         mContext = context;
         mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+        mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         mHandler = new GeofenceHandler();
@@ -114,14 +117,14 @@
     }
 
     public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
-            int uid, String packageName) {
+            int allowedResolutionLevel, int uid, String packageName) {
         if (D) {
             Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
                     + ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
         }
 
         GeofenceState state = new GeofenceState(geofence,
-                request.getExpireAt(), packageName, intent);
+                request.getExpireAt(), allowedResolutionLevel, uid, packageName, intent);
         synchronized (mLock) {
             // first make sure it doesn't already exist
             for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -261,6 +264,18 @@
                     continue;
                 }
 
+                int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
+                if (op >= 0) {
+                    if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
+                            state.mPackageName) != AppOpsManager.MODE_ALLOWED) {
+                        if (D) {
+                            Slog.d(TAG, "skipping geofence processing for no op app: "
+                                    + state.mPackageName);
+                        }
+                        continue;
+                    }
+                }
+
                 needUpdates = true;
                 if (location != null) {
                     int event = state.processLocation(location);
diff --git a/services/java/com/android/server/location/GeofenceState.java b/services/java/com/android/server/location/GeofenceState.java
index 11705ff..3ebe20a 100644
--- a/services/java/com/android/server/location/GeofenceState.java
+++ b/services/java/com/android/server/location/GeofenceState.java
@@ -35,6 +35,8 @@
     public final Geofence mFence;
     private final Location mLocation;
     public final long mExpireAt;
+    public final int mAllowedResolutionLevel;
+    public final int mUid;
     public final String mPackageName;
     public final PendingIntent mIntent;
 
@@ -42,12 +44,14 @@
     double mDistanceToCenter;  // current distance to center of fence
 
     public GeofenceState(Geofence fence, long expireAt,
-            String packageName, PendingIntent intent) {
+            int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) {
         mState = STATE_UNKNOWN;
         mDistanceToCenter = Double.MAX_VALUE;
 
         mFence = fence;
         mExpireAt = expireAt;
+        mAllowedResolutionLevel = allowedResolutionLevel;
+        mUid = uid;
         mPackageName = packageName;
         mIntent = intent;
 
diff --git a/services/java/com/android/server/location/LocationBlacklist.java b/services/java/com/android/server/location/LocationBlacklist.java
index 2437a37..6f22689 100644
--- a/services/java/com/android/server/location/LocationBlacklist.java
+++ b/services/java/com/android/server/location/LocationBlacklist.java
@@ -67,9 +67,9 @@
 
     private void reloadBlacklistLocked() {
         mWhitelist = getStringArrayLocked(WHITELIST_CONFIG_NAME);
-        Slog.i(TAG, "whitelist: " + Arrays.toString(mWhitelist));
+        if (D) Slog.d(TAG, "whitelist: " + Arrays.toString(mWhitelist));
         mBlacklist = getStringArrayLocked(BLACKLIST_CONFIG_NAME);
-        Slog.i(TAG, "blacklist: " + Arrays.toString(mBlacklist));
+        if (D) Slog.d(TAG, "blacklist: " + Arrays.toString(mBlacklist));
     }
 
     private void reloadBlacklist() {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index b893062..626002d 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1348,7 +1348,8 @@
                 continue;
             }
 
-            if (!ps.grantedPermissions
+            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            if (!gp.grantedPermissions
                     .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) {
                 continue;
             }
@@ -2918,8 +2919,9 @@
     private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
             String[] permissions, boolean[] tmp, int flags, int userId) {
         int numMatch = 0;
+        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
         for (int i=0; i<permissions.length; i++) {
-            if (ps.grantedPermissions.contains(permissions[i])) {
+            if (gp.grantedPermissions.contains(permissions[i])) {
                 tmp[i] = true;
                 numMatch++;
             } else {
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 55de065..0be453c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -45,7 +45,7 @@
 
     void startScan(boolean forceActive);
 
-    List<ScanResult> getScanResults();
+    List<ScanResult> getScanResults(String callingPackage);
 
     void disconnect();
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4861759..c08db07 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -777,7 +777,7 @@
      */
     public List<ScanResult> getScanResults() {
         try {
-            return mService.getScanResults();
+            return mService.getScanResults(mContext.getBasePackageName());
         } catch (RemoteException e) {
             return null;
         }