App ops: new operations for SMS.

Implementation required a new framework feature
to associate an app op with a broadcast.

Change-Id: I4ff41a52f7ad4ee8fd80cbf7b394f04d6c4315b3
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 1c5f32e..3c1fbfe 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -614,8 +614,8 @@
         Intent intent = makeIntent(UserHandle.USER_ALL);
         IntentReceiver receiver = new IntentReceiver();
         System.out.println("Broadcasting: " + intent);
-        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false,
-                mUserId);
+        mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null,
+                android.app.AppOpsManager.OP_NONE, true, false, mUserId);
         receiver.waitForFinish();
     }
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index c9930e9..50c508c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -92,7 +92,7 @@
         try {
             getDefault().broadcastIntent(
                 null, intent, null, null, Activity.RESULT_OK, null, null,
-                null /*permission*/, false, true, userId);
+                null /*permission*/, AppOpsManager.OP_NONE, false, true, userId);
         } catch (RemoteException ex) {
         }
     }
@@ -344,11 +344,12 @@
             String resultData = data.readString();
             Bundle resultExtras = data.readBundle();
             String perm = data.readString();
+            int appOp = data.readInt();
             boolean serialized = data.readInt() != 0;
             boolean sticky = data.readInt() != 0;
             int userId = data.readInt();
             int res = broadcastIntent(app, intent, resolvedType, resultTo,
-                    resultCode, resultData, resultExtras, perm,
+                    resultCode, resultData, resultExtras, perm, appOp,
                     serialized, sticky, userId);
             reply.writeNoException();
             reply.writeInt(res);
@@ -2174,7 +2175,7 @@
     public int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType,  IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized,
+            String requiredPermission, int appOp, boolean serialized,
             boolean sticky, int userId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -2188,6 +2189,7 @@
         data.writeString(resultData);
         data.writeBundle(map);
         data.writeString(requiredPermission);
+        data.writeInt(appOp);
         data.writeInt(serialized ? 1 : 0);
         data.writeInt(sticky ? 1 : 0);
         data.writeInt(userId);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a81b6fe..2a59a1d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -37,6 +37,7 @@
     public static final int MODE_IGNORED = 1;
     public static final int MODE_ERRORED = 2;
 
+    public static final int OP_NONE = -1;
     public static final int OP_COARSE_LOCATION = 0;
     public static final int OP_FINE_LOCATION = 1;
     public static final int OP_GPS = 2;
@@ -51,8 +52,17 @@
     public static final int OP_POST_NOTIFICATION = 11;
     public static final int OP_NEIGHBORING_CELLS = 12;
     public static final int OP_CALL_PHONE = 13;
+    public static final int OP_READ_SMS = 14;
+    public static final int OP_WRITE_SMS = 15;
+    public static final int OP_RECEIVE_SMS = 16;
+    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
+    public static final int OP_RECEIVE_MMS = 18;
+    public static final int OP_RECEIVE_WAP_PUSH = 19;
+    public static final int OP_SEND_SMS = 20;
+    public static final int OP_READ_ICC_SMS = 21;
+    public static final int OP_WRITE_ICC_SMS = 22;
     /** @hide */
-    public static final int _NUM_OP = 14;
+    public static final int _NUM_OP = 23;
 
     /**
      * This maps each operation to the operation that serves as the
@@ -77,6 +87,15 @@
             OP_POST_NOTIFICATION,
             OP_COARSE_LOCATION,
             OP_CALL_PHONE,
+            OP_READ_SMS,
+            OP_WRITE_SMS,
+            OP_READ_SMS,
+            OP_READ_SMS,
+            OP_READ_SMS,
+            OP_READ_SMS,
+            OP_WRITE_SMS,
+            OP_READ_SMS,
+            OP_WRITE_SMS,
     };
 
     /**
@@ -98,6 +117,15 @@
             "POST_NOTIFICATION",
             "NEIGHBORING_CELLS",
             "CALL_PHONE",
+            "READ_SMS",
+            "WRITE_SMS",
+            "RECEIVE_SMS",
+            "RECEIVE_EMERGECY_SMS",
+            "RECEIVE_MMS",
+            "RECEIVE_WAP_PUSH",
+            "SEND_SMS",
+            "READ_ICC_SMS",
+            "WRITE_ICC_SMS",
     };
 
     /**
@@ -119,6 +147,15 @@
             android.Manifest.permission.ACCESS_WIFI_STATE,
             null, // neighboring cells shares the coarse location perm
             android.Manifest.permission.CALL_PHONE,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.WRITE_SMS,
+            android.Manifest.permission.RECEIVE_SMS,
+            android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
+            android.Manifest.permission.RECEIVE_MMS,
+            android.Manifest.permission.RECEIVE_WAP_PUSH,
+            android.Manifest.permission.SEND_SMS,
+            android.Manifest.permission.READ_SMS,
+            android.Manifest.permission.WRITE_SMS,
     };
 
     public static int opToSwitch(int op) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2cf9f59..a40fe75 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1042,7 +1042,7 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, false,
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
                 getUserId());
         } catch (RemoteException e) {
         }
@@ -1056,7 +1056,21 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, false, false,
+                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
+                false, false, getUserId());
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+        warnIfCallingFromSystemProcess();
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            intent.setAllowFds(false);
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
+                Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false,
                 getUserId());
         } catch (RemoteException e) {
         }
@@ -1071,7 +1085,7 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, true, false,
+                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false,
                 getUserId());
         } catch (RemoteException e) {
         }
@@ -1082,6 +1096,15 @@
             String receiverPermission, BroadcastReceiver resultReceiver,
             Handler scheduler, int initialCode, String initialData,
             Bundle initialExtras) {
+        sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,
+                resultReceiver, scheduler, initialCode, initialData, initialExtras);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
@@ -1105,8 +1128,8 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
-                initialCode, initialData, initialExtras, receiverPermission,
-                true, false, getUserId());
+                initialCode, initialData, initialExtras, receiverPermission, appOp,
+                    true, false, getUserId());
         } catch (RemoteException e) {
         }
     }
@@ -1117,8 +1140,8 @@
         try {
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
-                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false,
-                    user.getIdentifier());
+                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
+                    AppOpsManager.OP_NONE, false, false, user.getIdentifier());
         } catch (RemoteException e) {
         }
     }
@@ -1131,7 +1154,7 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, false, false,
+                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, false, false,
                 user.getIdentifier());
         } catch (RemoteException e) {
         }
@@ -1164,7 +1187,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission,
-                true, false, user.getIdentifier());
+                    AppOpsManager.OP_NONE, true, false, user.getIdentifier());
         } catch (RemoteException e) {
         }
     }
@@ -1177,7 +1200,7 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, true,
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true,
                 getUserId());
         } catch (RemoteException e) {
         }
@@ -1212,7 +1235,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
-                true, true, getUserId());
+                    AppOpsManager.OP_NONE, true, true, getUserId());
         } catch (RemoteException e) {
         }
     }
@@ -1239,7 +1262,7 @@
             intent.setAllowFds(false);
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, false, true, user.getIdentifier());
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier());
         } catch (RemoteException e) {
         }
     }
@@ -1272,7 +1295,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
-                true, true, user.getIdentifier());
+                    AppOpsManager.OP_NONE, true, true, user.getIdentifier());
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e58ff62..cf4c729 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -85,7 +85,7 @@
     public int broadcastIntent(IApplicationThread caller, Intent intent,
             String resolvedType, IIntentReceiver resultTo, int resultCode,
             String resultData, Bundle map, String requiredPermission,
-            boolean serialized, boolean sticky, int userId) throws RemoteException;
+            int appOp, boolean serialized, boolean sticky, int userId) throws RemoteException;
     public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
     public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map, boolean abortBroadcast) throws RemoteException;
     public void attachApplication(IApplicationThread app) throws RemoteException;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f7c28b6..c964af4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1138,6 +1138,14 @@
             String receiverPermission);
 
     /**
+     * Like {@link #sendBroadcast(Intent, String)}, but also allows specification
+     * of an assocated app op as per {@link android.app.AppOpsManager}.
+     * @hide
+     */
+    public abstract void sendBroadcast(Intent intent,
+            String receiverPermission, int appOp);
+
+    /**
      * Broadcast the given intent to all interested BroadcastReceivers, delivering
      * them one at a time to allow more preferred receivers to consume the
      * broadcast before it is delivered to less preferred receivers.  This
@@ -1208,6 +1216,17 @@
             Bundle initialExtras);
 
     /**
+     * Like {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler,
+     * int, String, android.os.Bundle)}, but also allows specification
+     * of an assocated app op as per {@link android.app.AppOpsManager}.
+     * @hide
+     */
+    public abstract void sendOrderedBroadcast(Intent intent,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras);
+
+    /**
      * Version of {@link #sendBroadcast(Intent)} that allows you to specify the
      * user the broadcast will be sent to.  This is not available to applications
      * that are not pre-installed on the system image.  Using it requires holding
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index b63f45e..736dd99 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -349,6 +349,12 @@
         mBase.sendBroadcast(intent, receiverPermission);
     }
 
+    /** @hide */
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+        mBase.sendBroadcast(intent, receiverPermission, appOp);
+    }
+
     @Override
     public void sendOrderedBroadcast(Intent intent,
             String receiverPermission) {
@@ -365,6 +371,17 @@
                 initialData, initialExtras);
     }
 
+    /** @hide */
+    @Override
+    public void sendOrderedBroadcast(
+        Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+        Handler scheduler, int initialCode, String initialData,
+        Bundle initialExtras) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission, appOp,
+                resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
     @Override
     public void sendBroadcastAsUser(Intent intent, UserHandle user) {
         mBase.sendBroadcastAsUser(intent, user);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 252cae2..44f2d12 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import android.app.AppOpsManager;
 import com.android.internal.R;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessStats;
@@ -992,7 +993,7 @@
                                 | Intent.FLAG_RECEIVER_FOREGROUND);
                     }
                     broadcastIntentLocked(null, null, intent,
-                            null, null, 0, null, null, null,
+                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                             false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
 
                     if (mShowDialogs) {
@@ -3725,7 +3726,7 @@
         }
 
         broadcastIntentLocked(null, null, intent, null,
-                null, 0, null, null, null, false, false, -1,
+                null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1,
                 Process.SYSTEM_UID, UserHandle.USER_ALL);
     }
 
@@ -3788,7 +3789,7 @@
         intent.putExtra(Intent.EXTRA_UID, uid);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
         broadcastIntentLocked(null, null, intent,
-                null, null, 0, null, null, null,
+                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                 false, false,
                 MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
     }
@@ -3800,7 +3801,7 @@
                 | Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         broadcastIntentLocked(null, null, intent,
-                null, null, 0, null, null, null,
+                null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                 false, false,
                 MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
     }
@@ -4479,7 +4480,8 @@
                         broadcastIntentLocked(null, null, intent,
                                 null, null, 0, null, null,
                                 android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
-                                false, false, MY_PID, Process.SYSTEM_UID, userId);
+                                AppOpsManager.OP_NONE, false, false, MY_PID, Process.SYSTEM_UID,
+                                userId);
                     }
                 }
             }
@@ -7911,7 +7913,8 @@
                             Slog.i(TAG, "Sending system update to " + intent.getComponent()
                                     + " for user " + users[j]);
                             broadcastIntentLocked(null, null, intent, null, finisher,
-                                    0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+                                    0, null, null, null, AppOpsManager.OP_NONE,
+                                    true, false, MY_PID, Process.SYSTEM_UID,
                                     users[j]);
                             if (finisher != null) {
                                 mWaitingUpdate = true;
@@ -8044,7 +8047,7 @@
                         | Intent.FLAG_RECEIVER_FOREGROUND);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
                 broadcastIntentLocked(null, null, intent,
-                        null, null, 0, null, null, null,
+                        null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                         false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
                 intent = new Intent(Intent.ACTION_USER_STARTING);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -8057,7 +8060,7 @@
                                     throws RemoteException {
                             }
                         }, 0, null, null,
-                        android.Manifest.permission.INTERACT_ACROSS_USERS,
+                        android.Manifest.permission.INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
                         true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -11552,8 +11555,8 @@
                     Intent intent = (Intent)allSticky.get(i);
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
-                            null, -1, -1, null, receivers, null, 0, null, null,
-                            false, true, true, -1);
+                            null, -1, -1, null, AppOpsManager.OP_NONE, receivers, null, 0,
+                            null, null, false, true, true, -1);
                     queue.enqueueParallelBroadcastLocked(r);
                     queue.scheduleBroadcastsLocked();
                 }
@@ -11702,7 +11705,7 @@
     private final int broadcastIntentLocked(ProcessRecord callerApp,
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
-            Bundle map, String requiredPermission,
+            Bundle map, String requiredPermission, int appOp,
             boolean ordered, boolean sticky, int callingPid, int callingUid,
             int userId) {
         intent = new Intent(intent);
@@ -11948,7 +11951,7 @@
             // components to be launched.
             final BroadcastQueue queue = broadcastQueueForIntent(intent);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
-                    callerPackage, callingPid, callingUid, requiredPermission,
+                    callerPackage, callingPid, callingUid, requiredPermission, appOp,
                     registeredReceivers, resultTo, resultCode, resultData, map,
                     ordered, sticky, false, userId);
             if (DEBUG_BROADCAST) Slog.v(
@@ -12038,7 +12041,7 @@
                 || resultTo != null) {
             BroadcastQueue queue = broadcastQueueForIntent(intent);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
-                    callerPackage, callingPid, callingUid, requiredPermission,
+                    callerPackage, callingPid, callingUid, requiredPermission, appOp,
                     receivers, resultTo, resultCode, resultData, map, ordered,
                     sticky, false, userId);
             if (DEBUG_BROADCAST) Slog.v(
@@ -12090,7 +12093,7 @@
     public final int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky, int userId) {
+            String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {
         enforceNotIsolatedCaller("broadcastIntent");
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
@@ -12102,7 +12105,7 @@
             int res = broadcastIntentLocked(callerApp,
                     callerApp != null ? callerApp.info.packageName : null,
                     intent, resolvedType, resultTo,
-                    resultCode, resultData, map, requiredPermission, serialized, sticky,
+                    resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
                     callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
@@ -12119,7 +12122,7 @@
             final long origId = Binder.clearCallingIdentity();
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
                     resultTo, resultCode, resultData, map, requiredPermission,
-                    serialized, sticky, -1, uid, userId);
+                    AppOpsManager.OP_NONE, serialized, sticky, -1, uid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -12505,13 +12508,14 @@
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING
                         | Intent.FLAG_RECEIVER_FOREGROUND);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
-                        null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                        null, AppOpsManager.OP_NONE, false, false, MY_PID,
+                        Process.SYSTEM_UID, UserHandle.USER_ALL);
                 if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                     intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                     broadcastIntentLocked(null, null, intent,
-                            null, null, 0, null, null,
-                            null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
+                            false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
         }
@@ -14355,7 +14359,7 @@
                             | Intent.FLAG_RECEIVER_FOREGROUND);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                     broadcastIntentLocked(null, null, intent,
-                            null, null, 0, null, null, null,
+                            null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                             false, false, MY_PID, Process.SYSTEM_UID, userId);
                 }
 
@@ -14370,7 +14374,8 @@
                                             boolean sticky, int sendingUser) {
                                         userInitialized(uss, userId);
                                     }
-                                }, 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID,
+                                }, 0, null, null, null, AppOpsManager.OP_NONE,
+                                true, false, MY_PID, Process.SYSTEM_UID,
                                 userId);
                         uss.initializing = true;
                     } else {
@@ -14398,7 +14403,7 @@
                                         throws RemoteException {
                                 }
                             }, 0, null, null,
-                            android.Manifest.permission.INTERACT_ACROSS_USERS,
+                            android.Manifest.permission.INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
                             true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
@@ -14419,7 +14424,7 @@
                         | Intent.FLAG_RECEIVER_FOREGROUND);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, oldUserId);
                 broadcastIntentLocked(null, null, intent,
-                        null, null, 0, null, null, null,
+                        null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                         false, false, MY_PID, Process.SYSTEM_UID, oldUserId);
             }
             if (newUserId >= 0) {
@@ -14428,7 +14433,7 @@
                         | Intent.FLAG_RECEIVER_FOREGROUND);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
                 broadcastIntentLocked(null, null, intent,
-                        null, null, 0, null, null, null,
+                        null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                         false, false, MY_PID, Process.SYSTEM_UID, newUserId);
                 intent = new Intent(Intent.ACTION_USER_SWITCHED);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -14436,7 +14441,7 @@
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null,
-                        android.Manifest.permission.MANAGE_USERS,
+                        android.Manifest.permission.MANAGE_USERS, AppOpsManager.OP_NONE,
                         false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             }
         } finally {
@@ -14541,7 +14546,7 @@
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null,
-                        android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+                        android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE,
                         false, false, MY_PID, Process.SYSTEM_UID, userId);
             }
             int num = mUserLru.size();
@@ -14657,14 +14662,14 @@
                             uss.mState = UserStartedState.STATE_SHUTDOWN;
                         }
                         broadcastIntentLocked(null, null, shutdownIntent,
-                                null, shutdownReceiver, 0, null, null, null,
+                                null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
                                 true, false, MY_PID, Process.SYSTEM_UID, userId);
                     }
                 };
                 // Kick things off.
                 broadcastIntentLocked(null, null, stoppingIntent,
                         null, stoppingReceiver, 0, null, null,
-                        android.Manifest.permission.INTERACT_ACROSS_USERS,
+                        android.Manifest.permission.INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
                         true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             } finally {
                 Binder.restoreCallingIdentity(ident);
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index bada7f0..e8e8f25 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -22,6 +22,7 @@
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
+import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -409,6 +410,16 @@
                 skip = true;
             }
         }
+        if (r.appOp != AppOpsManager.OP_NONE) {
+            int mode = mService.mAppOpsService.checkOperation(r.appOp,
+                    filter.receiverList.uid, filter.packageName);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                if (DEBUG_BROADCAST)  Slog.v(TAG,
+                        "App op " + r.appOp + " not allowed for broadcast to uid "
+                        + filter.receiverList.uid + " pkg " + filter.packageName);
+                skip = true;
+            }
+        }
 
         if (!skip) {
             // If this is not being sent as an ordered broadcast, then we
@@ -706,6 +717,17 @@
                     skip = true;
                 }
             }
+            if (r.appOp != AppOpsManager.OP_NONE) {
+                int mode = mService.mAppOpsService.checkOperation(r.appOp,
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName);
+                if (mode != AppOpsManager.MODE_ALLOWED) {
+                    if (DEBUG_BROADCAST)  Slog.v(TAG,
+                            "App op " + r.appOp + " not allowed for broadcast to uid "
+                            + info.activityInfo.applicationInfo.uid + " pkg "
+                            + info.activityInfo.packageName);
+                    skip = true;
+                }
+            }
             boolean isSingleton = false;
             try {
                 isSingleton = mService.isSingleton(info.activityInfo.processName,
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index 1cf5b9c..a98afb6 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.app.AppOpsManager;
 import android.content.IIntentReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -46,6 +47,7 @@
     final boolean initialSticky; // initial broadcast from register to sticky?
     final int userId;       // user id this broadcast was for
     final String requiredPermission; // a permission the caller has required
+    final int appOp;        // an app op that is associated with this broadcast
     final List receivers;   // contains BroadcastFilter and ResolveInfo
     IIntentReceiver resultTo; // who receives final result if non-null
     long dispatchTime;      // when dispatch started on this set of receivers
@@ -90,8 +92,9 @@
                 pw.print(callerApp != null ? callerApp.toShortString() : "null");
                 pw.print(" pid="); pw.print(callingPid);
                 pw.print(" uid="); pw.println(callingUid);
-        if (requiredPermission != null) {
-            pw.print(prefix); pw.print("requiredPermission="); pw.println(requiredPermission);
+        if (requiredPermission != null || appOp != AppOpsManager.OP_NONE) {
+            pw.print(prefix); pw.print("requiredPermission="); pw.print(requiredPermission);
+                    pw.print("  appOp="); pw.println(appOp);
         }
         pw.print(prefix); pw.print("dispatchClockTime=");
                 pw.println(new Date(dispatchClockTime));
@@ -164,7 +167,7 @@
 
     BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
-            int _callingPid, int _callingUid, String _requiredPermission,
+            int _callingPid, int _callingUid, String _requiredPermission, int _appOp,
             List _receivers, IIntentReceiver _resultTo, int _resultCode,
             String _resultData, Bundle _resultExtras, boolean _serialized,
             boolean _sticky, boolean _initialSticky,
@@ -176,6 +179,7 @@
         callingPid = _callingPid;
         callingUid = _callingUid;
         requiredPermission = _requiredPermission;
+        appOp = _appOp;
         receivers = _receivers;
         resultTo = _resultTo;
         resultCode = _resultCode;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 829e67a..46d2cca 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -25,7 +25,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.ArrayUtils.removeInt;
-import static libcore.io.OsConstants.S_ISLNK;
 import static libcore.io.OsConstants.S_IRWXU;
 import static libcore.io.OsConstants.S_IRGRP;
 import static libcore.io.OsConstants.S_IXGRP;
@@ -112,7 +111,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.Environment.UserEnvironment;
-import android.provider.Settings.Secure;
 import android.security.SystemKeyStore;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -149,13 +147,11 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
-import libcore.io.OsConstants;
 import libcore.io.StructStat;
 
 /**
@@ -5441,7 +5437,8 @@
                                 + " " + intent.getExtras(), here);
                     }
                     am.broadcastIntent(null, intent, null, finishedReceiver,
-                            0, null, null, null, finishedReceiver != null, false, id);
+                            0, null, null, null, android.app.AppOpsManager.OP_NONE,
+                            finishedReceiver != null, false, id);
                 }
             } catch (RemoteException ex) {
             }
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 248fbf1..cfc6bd7 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -299,6 +299,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void sendOrderedBroadcast(Intent intent,
             String receiverPermission) {
@@ -312,6 +318,14 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
+            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
+           Bundle initialExtras) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void sendBroadcastAsUser(Intent intent, UserHandle user) {
         throw new UnsupportedOperationException();