Fix bug #1873249i: Apps can DoS/brick device

This is the problem where various things are listening for broadcasts
(such as battery status, PIN/PUK/Network) that an application can send
to cause harm to the system.

Solving this is tricky because many of these broadcasts are sticky,
and I have never figured out how to do permissions with sticky
broadcasts in a sane way.  So instead, I am going to punt on the
general problem and just brute force it:

There is new a way for system components to declare specific
broadcast actions to be protected, which means that only the system
and the phone can send them.  This is good enough for now.  None
of it is exposed in the public API so we can make something a little
less stupid in the future if we ever need to.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 263f927..7615614 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1112,11 +1112,17 @@
 
     /**
      * Broadcast Action: Sent after the screen turns off.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SCREEN_OFF = "android.intent.action.SCREEN_OFF";
     /**
      * Broadcast Action: Sent after the screen turns on.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
@@ -1124,6 +1130,9 @@
     /**
      * Broadcast Action: Sent when the user is present after device wakes up (e.g when the
      * keyguard is gone).
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_USER_PRESENT= "android.intent.action.USER_PRESENT";
@@ -1134,6 +1143,9 @@
      * in manifests, only by exlicitly registering for it with
      * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
      * Context.registerReceiver()}.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
@@ -1152,6 +1164,9 @@
      * <ul>
      *   <li><em>time-zone</em> - The java.util.TimeZone.getID() value identifying the new time zone.</li>
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_TIMEZONE_CHANGED = "android.intent.action.TIMEZONE_CHANGED";
@@ -1177,6 +1192,9 @@
      * such as installing alarms.  You must hold the
      * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission
      * in order to receive this broadcast.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
@@ -1184,12 +1202,18 @@
      * Broadcast Action: This is broadcast when a user action should request a
      * temporary system dialog to dismiss.  Some examples of temporary system
      * dialogs are the notification window-shade and the recent tasks dialog.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
     /**
      * Broadcast Action: Trigger the download and eventual installation
      * of a package.
      * <p>Input: {@link #getData} is the URI of the package file to download.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL";
@@ -1203,6 +1227,9 @@
      * <li> {@link #EXTRA_REPLACING} is set to true if this is following
      * an {@link #ACTION_PACKAGE_REMOVED} broadcast for the same package.
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
@@ -1214,6 +1241,9 @@
      * <ul>
      * <li> {@link #EXTRA_UID} containing the integer uid assigned to the new package.
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
@@ -1229,6 +1259,9 @@
      * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
      * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
@@ -1238,6 +1271,9 @@
      * <ul>
      * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
@@ -1251,6 +1287,9 @@
      * <ul>
      * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
@@ -1263,12 +1302,18 @@
      * <ul>
      * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
     /**
      * Broadcast Action: A user ID has been removed from the system.  The user
      * ID number is stored in the extra data under {@link #EXTRA_UID}.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
@@ -1287,6 +1332,9 @@
      * application to make sure it sees the new changes.  Some system code that
      * can not be restarted will need to watch for this action and handle it
      * appropriately.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      *
      * @see android.content.res.Configuration
      */
@@ -1298,15 +1346,21 @@
      *
      * <p class="note">
      * You can <em>not</em> receive this through components declared
-     * in manifests, only by exlicitly registering for it with
+     * in manifests, only by explicitly registering for it with
      * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
      * Context.registerReceiver()}.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BATTERY_CHANGED = "android.intent.action.BATTERY_CHANGED";
     /**
      * Broadcast Action:  Indicates low battery condition on the device.
      * This broadcast corresponds to the "Low battery warning" system dialog.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BATTERY_LOW = "android.intent.action.BATTERY_LOW";
@@ -1314,6 +1368,9 @@
      * Broadcast Action:  Indicates the battery is now okay after being low.
      * This will be sent after {@link #ACTION_BATTERY_LOW} once the battery has
      * gone back up to an okay state.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_BATTERY_OKAY = "android.intent.action.BATTERY_OKAY";
@@ -1323,6 +1380,9 @@
      * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
      * stay active to receive this notification.  This action can be used to implement actions
      * that wait until power is available to trigger.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
@@ -1332,6 +1392,9 @@
      * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to
      * stay active to receive this notification.  This action can be used to implement actions
      * that wait until power is available to trigger.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
@@ -1341,16 +1404,25 @@
      * off, not sleeping).  Once the broadcast is complete, the final shutdown
      * will proceed and all unsaved data lost.  Apps will not normally need
      * to handle this, since the forground activity will be paused as well.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
     /**
      * Broadcast Action:  Indicates low memory condition on the device
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_STORAGE_LOW = "android.intent.action.DEVICE_STORAGE_LOW";
     /**
      * Broadcast Action:  Indicates low memory condition on the device no longer exists
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_DEVICE_STORAGE_OK = "android.intent.action.DEVICE_STORAGE_OK";
@@ -1515,6 +1587,9 @@
      *   then cell radio and possibly other radios such as bluetooth or WiFi may have also been
      *   turned off</li>
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_AIRPLANE_MODE_CHANGED = "android.intent.action.AIRPLANE_MODE";
@@ -1593,6 +1668,9 @@
      * <p>You must hold the
      * {@link android.Manifest.permission#PROCESS_OUTGOING_CALLS}
      * permission to receive this Intent.</p>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_NEW_OUTGOING_CALL =
@@ -1601,6 +1679,9 @@
     /**
      * Broadcast Action: Have the device reboot.  This is only for use by
      * system code.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_REBOOT =
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bf2a895..e587ca7 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -71,6 +71,8 @@
     
     void removePermission(String name);
     
+    boolean isProtectedBroadcast(String actionName);
+    
     int checkSignatures(String pkg1, String pkg2);
     
     String[] getPackagesForUid(int uid);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0e2deed..cebb696 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -720,7 +720,7 @@
                 sa.recycle();
 
                 if (name != null && !pkg.requestedPermissions.contains(name)) {
-                    pkg.requestedPermissions.add(name);
+                    pkg.requestedPermissions.add(name.intern());
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -851,21 +851,6 @@
 
                 XmlUtils.skipCurrentTag(parser);
 
-            } else if (tagName.equals("instrumentation")) {
-                if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
-                    return null;
-                }
-            } else if (tagName.equals("eat-comment")) {
-                // Just skip this tag
-                XmlUtils.skipCurrentTag(parser);
-                continue;
-            } else if (RIGID_PARSER) {
-                outError[0] = "Bad element under <manifest>: "
-                    + parser.getName();
-                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-                return null;
-
-
             } else if (tagName.equals("supports-density")) {
                 sa = res.obtainAttributes(attrs,
                         com.android.internal.R.styleable.AndroidManifestSupportsDensity);
@@ -900,6 +885,43 @@
                 sa.recycle();
                 
                 XmlUtils.skipCurrentTag(parser);
+                
+            } else if (tagName.equals("protected-broadcast")) {
+                sa = res.obtainAttributes(attrs,
+                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);
+
+                String name = sa.getNonResourceString(
+                        com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);
+
+                sa.recycle();
+
+                if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
+                    if (pkg.protectedBroadcasts == null) {
+                        pkg.protectedBroadcasts = new ArrayList<String>();
+                    }
+                    if (!pkg.protectedBroadcasts.contains(name)) {
+                        pkg.protectedBroadcasts.add(name.intern());
+                    }
+                }
+
+                XmlUtils.skipCurrentTag(parser);
+                
+            } else if (tagName.equals("instrumentation")) {
+                if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
+                    return null;
+                }
+                
+            } else if (tagName.equals("eat-comment")) {
+                // Just skip this tag
+                XmlUtils.skipCurrentTag(parser);
+                continue;
+                
+            } else if (RIGID_PARSER) {
+                outError[0] = "Bad element under <manifest>: "
+                    + parser.getName();
+                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+                return null;
+
             } else {
                 Log.w(TAG, "Bad element under <manifest>: "
                       + parser.getName());
@@ -1429,7 +1451,7 @@
                 sa.recycle();
 
                 if (lname != null && !owner.usesLibraries.contains(lname)) {
-                    owner.usesLibraries.add(lname);
+                    owner.usesLibraries.add(lname.intern());
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -2210,8 +2232,8 @@
             return null;
         }
 
-        boolean success = true;
-
+        name = name.intern();
+        
         TypedValue v = sa.peekValue(
                 com.android.internal.R.styleable.AndroidManifestMetaData_resource);
         if (v != null && v.resourceId != 0) {
@@ -2224,7 +2246,7 @@
             if (v != null) {
                 if (v.type == TypedValue.TYPE_STRING) {
                     CharSequence cs = v.coerceToString();
-                    data.putString(name, cs != null ? cs.toString() : null);
+                    data.putString(name, cs != null ? cs.toString().intern() : null);
                 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
                     data.putBoolean(name, v.data != 0);
                 } else if (v.type >= TypedValue.TYPE_FIRST_INT
@@ -2405,6 +2427,8 @@
 
         public final ArrayList<String> requestedPermissions = new ArrayList<String>();
 
+        public ArrayList<String> protectedBroadcasts;
+        
         public final ArrayList<String> usesLibraries = new ArrayList<String>();
         public String[] usesLibraryFiles = null;
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 51e6c1e..4805193 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -68,6 +68,12 @@
     public static final int PHONE_UID = 1001;
 
     /**
+     * Defines the UID/GID for the user shell.
+     * @hide
+     */
+    public static final int SHELL_UID = 2000;
+
+    /**
      * Defines the UID/GID for the WIFI supplicant process.
      * @hide
      */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 4078fa6..0cfa506 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1620,6 +1620,9 @@
          *
          * It is recommended to display <em>plmn</em> before / above <em>spn</em> if
          * both are displayed.
+         * 
+         * <p>Note this is a protected intent that can only be sent
+         * by the system.
          */
         public static final String SPN_STRINGS_UPDATED_ACTION =
                 "android.provider.Telephony.SPN_STRINGS_UPDATED";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 23967f4..e964cda 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -21,6 +21,39 @@
     package="android" android:sharedUserId="android.uid.system"
     android:sharedUserLabel="@string/android_system_label">
 
+    <!-- ================================================ -->
+    <!-- Special broadcasts that only the system can send -->
+    <!-- ================================================ -->
+    <eat-comment />
+    
+    <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
+    <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
+    <protected-broadcast android:name="android.intent.action.USER_PRESENT" />
+    <protected-broadcast android:name="android.intent.action.TIME_TICK" />
+    <protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" />
+    <protected-broadcast android:name="android.intent.action.CLOSE_SYSTEM_DIALOGS" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+    <protected-broadcast android:name="android.intent.action.UID_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BATTERY_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BATTERY_LOW" />
+    <protected-broadcast android:name="android.intent.action.BATTERY_OKAY" />
+    <protected-broadcast android:name="android.intent.action.ACTION_POWER_CONNECTED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_SHUTDOWN" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_LOW" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
+    <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
+    <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
+    <protected-broadcast android:name="android.intent.action.REBOOT" />
+    
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 12a76ba..9dc483c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -887,11 +887,12 @@
         <attr name="largeScreens" format="boolean" />
     </declare-styleable>
 
-    <!-- The <code>expandable</code> specifies if this package supports screen metrics
-         other than 320x480 dip.
-         <p>This appears as a child tag of the
+    <!-- Private tag to declare system protected broadcast actions.
+
+         <p>This appears as a child tag of the root
          {@link #AndroidManifest manifest} tag. -->
-    <declare-styleable name="AndroidManifestExpandable" parent="AndroidManifest">
+    <declare-styleable name="AndroidManifestProtectedBroadcast" parent="AndroidManifest">
+        <attr name="name" />
     </declare-styleable>
 
     <!-- The <code>provider</code> tag declares a
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 06435c8..0d190ca 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -56,8 +56,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -251,6 +249,9 @@
     final HashMap<String, PackageParser.PermissionGroup> mPermissionGroups =
             new HashMap<String, PackageParser.PermissionGroup>();
 
+    // Broadcast actions that are only available to the system.
+    final HashSet<String> mProtectedBroadcasts = new HashSet<String>();
+    
     boolean mSystemReady;
     boolean mSafeMode;
     boolean mHasSystemUidErrors;
@@ -1128,6 +1129,12 @@
         }
     }
 
+    public boolean isProtectedBroadcast(String actionName) {
+        synchronized (mPackages) {
+            return mProtectedBroadcasts.contains(actionName);
+        }
+    }
+    
     public int checkSignatures(String pkg1, String pkg2) {
         synchronized (mPackages) {
             PackageParser.Package p1 = mPackages.get(pkg1);
@@ -2500,6 +2507,13 @@
                 if (Config.LOGD) Log.d(TAG, "  Instrumentation: " + r);
             }
     
+            if (pkg.protectedBroadcasts != null) {
+                N = pkg.protectedBroadcasts.size();
+                for (i=0; i<N; i++) {
+                    mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
+                }
+            }
+            
             pkgSetting.setTimeStamp(scanFileTime);
         }
         
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index aad542a..134ab80 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10832,6 +10832,29 @@
             mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
         }
 
+        /*
+         * Prevent non-system code (defined here to be non-persistent
+         * processes) from sending protected broadcasts.
+         */
+        if (callingUid == Process.SYSTEM_UID || callingUid == Process.PHONE_UID
+                || callingUid == Process.SHELL_UID || callingUid == 0) {
+            // Always okay.
+        } else if (callerApp == null || !callerApp.persistent) {
+            try {
+                if (ActivityThread.getPackageManager().isProtectedBroadcast(
+                        intent.getAction())) {
+                    String msg = "Permission Denial: not allowed to send broadcast "
+                            + intent.getAction() + " from pid="
+                            + callingPid + ", uid=" + callingUid;
+                    Log.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception", e);
+                return BROADCAST_SUCCESS;
+            }
+        }
+        
         // Add to the sticky list if requested.
         if (sticky) {
             if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 9152211..02e9800 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -32,6 +32,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_SERVICE_STATE_CHANGED = "android.intent.action.SERVICE_STATE";
 
@@ -50,6 +53,9 @@
      *
      * <p class="note">
      * Requires no permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_RADIO_TECHNOLOGY_CHANGED
             = "android.intent.action.RADIO_TECHNOLOGY";
@@ -66,6 +72,9 @@
      *
      * <p class="note">
      * Requires no permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED
             = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
@@ -89,6 +98,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_SIGNAL_STRENGTH_CHANGED = "android.intent.action.SIG_STR";
 
@@ -110,6 +122,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED
             = "android.intent.action.ANY_DATA_STATE";
@@ -127,6 +142,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_DATA_CONNECTION_FAILED
             = "android.intent.action.DATA_CONNECTION_FAILED";
@@ -148,6 +166,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_SIM_STATE_CHANGED
             = "android.intent.action.SIM_STATE_CHANGED";
@@ -163,6 +184,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_NETWORK_SET_TIME = "android.intent.action.NETWORK_SET_TIME";
 
@@ -178,6 +202,9 @@
      *
      * <p class="note">
      * Requires the READ_PHONE_STATE permission.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_NETWORK_SET_TIMEZONE
             = "android.intent.action.NETWORK_SET_TIMEZONE";
@@ -187,6 +214,9 @@
      * <p class="note">.
      * This is to pop up a notice to show user that the phone is in emergency callback mode
      * and atacalls and outgoing sms are blocked.
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      */
     public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
             = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
@@ -197,6 +227,9 @@
      * <ul>
      *   <li><em>mdn</em> - An Integer of the updated MDN number.</li>
      * </ul>
+     * 
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
      *
      * <p class="note">
      */