Fix issue #4902856: Don't let apps register non-explicit PendingIntents

Location manager now checks for such intents, and logs a warning when
they are given to it.  Nothing thrown yet, it needs to check the
targetSdkVersion of the caller somehow.

When sending the pending intent, we require that the recipient hold the
appropriate permission.  This should pretty much close the security hole.

Includes a bunch of infrastructure in the activity manager needed to
support all this.

Change-Id: I4dba7a98a7b8bbb9e347666451aa9cb1efad1848
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 85f40c9..fdf4a3a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -251,12 +251,13 @@
             IBinder b = data.readStrongBinder();
             IApplicationThread app =
                 b != null ? ApplicationThreadNative.asInterface(b) : null;
+            String packageName = data.readString();
             b = data.readStrongBinder();
             IIntentReceiver rec
                 = b != null ? IIntentReceiver.Stub.asInterface(b) : null;
             IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
             String perm = data.readString();
-            Intent intent = registerReceiver(app, rec, filter, perm);
+            Intent intent = registerReceiver(app, packageName, rec, filter, perm);
             reply.writeNoException();
             if (intent != null) {
                 reply.writeInt(1);
@@ -1503,6 +1504,16 @@
             return true;
         }
 
+        case IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IIntentSender r = IIntentSender.Stub.asInterface(
+                data.readStrongBinder());
+            boolean res = isIntentSenderTargetedToPackage(r);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -1702,7 +1713,7 @@
         reply.recycle();
         return res;
     }
-    public Intent registerReceiver(IApplicationThread caller,
+    public Intent registerReceiver(IApplicationThread caller, String packageName,
             IIntentReceiver receiver,
             IntentFilter filter, String perm) throws RemoteException
     {
@@ -1710,6 +1721,7 @@
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+        data.writeString(packageName);
         data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
         filter.writeToParcel(data, 0);
         data.writeString(perm);
@@ -3385,5 +3397,18 @@
         reply.recycle();
     }
 
+    public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(sender.asBinder());
+        mRemote.transact(IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean res = reply.readInt() != 0;
+        data.recycle();
+        reply.recycle();
+        return res;
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 94a4afa..8749d3e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -61,7 +61,6 @@
 import android.net.wifi.WifiManager;
 import android.nfc.NfcManager;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.DropBoxManager;
 import android.os.Environment;
@@ -81,7 +80,6 @@
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
-import android.view.Display;
 import android.view.WindowManagerImpl;
 import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.InputMethodManager;
@@ -142,6 +140,7 @@
             new HashMap<String, SharedPreferencesImpl>();
 
     /*package*/ LoadedApk mPackageInfo;
+    private String mBasePackageName;
     private Resources mResources;
     /*package*/ ActivityThread mMainThread;
     private Context mOuterContext;
@@ -1030,7 +1029,7 @@
         }
         try {
             return ActivityManagerNative.getDefault().registerReceiver(
-                    mMainThread.getApplicationThread(),
+                    mMainThread.getApplicationThread(), mBasePackageName,
                     rd, filter, broadcastPermission);
         } catch (RemoteException e) {
             return null;
@@ -1397,7 +1396,7 @@
         if (pi != null) {
             ContextImpl c = new ContextImpl();
             c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
-            c.init(pi, null, mMainThread, mResources);
+            c.init(pi, null, mMainThread, mResources, mBasePackageName);
             if (c.mResources != null) {
                 return c;
             }
@@ -1450,6 +1449,7 @@
      */
     public ContextImpl(ContextImpl context) {
         mPackageInfo = context.mPackageInfo;
+        mBasePackageName = context.mBasePackageName;
         mResources = context.mResources;
         mMainThread = context.mMainThread;
         mContentResolver = context.mContentResolver;
@@ -1458,13 +1458,14 @@
 
     final void init(LoadedApk packageInfo,
             IBinder activityToken, ActivityThread mainThread) {
-        init(packageInfo, activityToken, mainThread, null);
+        init(packageInfo, activityToken, mainThread, null, null);
     }
 
     final void init(LoadedApk packageInfo,
                 IBinder activityToken, ActivityThread mainThread,
-                Resources container) {
+                Resources container, String basePackageName) {
         mPackageInfo = packageInfo;
+        mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
         mResources = mPackageInfo.getResources(mainThread);
 
         if (mResources != null && container != null
@@ -1485,6 +1486,7 @@
 
     final void init(Resources resources, ActivityThread mainThread) {
         mPackageInfo = null;
+        mBasePackageName = null;
         mResources = resources;
         mMainThread = mainThread;
         mContentResolver = new ApplicationContentResolver(this, mainThread);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index e2588cf..9e20764 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -103,7 +103,7 @@
             throws RemoteException;
     public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
     public boolean willActivityBeVisible(IBinder token) throws RemoteException;
-    public Intent registerReceiver(IApplicationThread caller,
+    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter,
             String requiredPermission) throws RemoteException;
     public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
@@ -361,6 +361,8 @@
     public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
     public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
 
+    public boolean isIntentSenderTargetedToPackage(IIntentSender sender) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -587,4 +589,5 @@
     int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
     int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+132;
     int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+133;
+    int IS_INTENT_SENDER_TARGETED_TO_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+134;
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 5b43b65..b4827cb 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -365,7 +365,7 @@
      * is no longer allowing more intents to be sent through it.
      */
     public void send() throws CanceledException {
-        send(null, 0, null, null, null);
+        send(null, 0, null, null, null, null);
     }
 
     /**
@@ -379,7 +379,7 @@
      * is no longer allowing more intents to be sent through it.
      */
     public void send(int code) throws CanceledException {
-        send(null, code, null, null, null);
+        send(null, code, null, null, null, null);
     }
 
     /**
@@ -399,7 +399,7 @@
      */
     public void send(Context context, int code, Intent intent)
             throws CanceledException {
-        send(context, code, intent, null, null);
+        send(context, code, intent, null, null, null);
     }
 
     /**
@@ -420,7 +420,7 @@
      */
     public void send(int code, OnFinished onFinished, Handler handler)
             throws CanceledException {
-        send(null, code, null, onFinished, handler);
+        send(null, code, null, onFinished, handler, null);
     }
 
     /**
@@ -449,20 +449,64 @@
      * @see #send(int)
      * @see #send(Context, int, Intent)
      * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, OnFinished, Handler, String)
      *
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
      */
     public void send(Context context, int code, Intent intent,
             OnFinished onFinished, Handler handler) throws CanceledException {
+        send(context, code, intent, onFinished, handler, null);
+    }
+
+    /**
+     * Perform the operation associated with this PendingIntent, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * <p>For the intent parameter, a PendingIntent
+     * often has restrictions on which fields can be supplied here, based on
+     * how the PendingIntent was retrieved in {@link #getActivity},
+     * {@link #getBroadcast}, or {@link #getService}.
+     *
+     * @param context The Context of the caller.  This may be null if
+     * <var>intent</var> is also null.
+     * @param code Result code to supply back to the PendingIntent's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use null to not modify the original Intent.
+     * @param onFinished The object to call back on when the send has
+     * completed, or null for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If null, the callback will happen from the thread
+     * pool of the process.
+     * @param requiredPermission Name of permission that a recipient of the PendingIntent
+     * is required to hold.  This is only valid for broadcast intents, and
+     * corresponds to the permission argument in
+     * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+     * If null, no permission is required.
+     *
+     * @see #send()
+     * @see #send(int)
+     * @see #send(Context, int, Intent)
+     * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, OnFinished, Handler)
+     *
+     * @throws CanceledException Throws CanceledException if the PendingIntent
+     * is no longer allowing more intents to be sent through it.
+     */
+    public void send(Context context, int code, Intent intent,
+            OnFinished onFinished, Handler handler, String requiredPermission)
+            throws CanceledException {
         try {
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
             int res = mTarget.send(code, intent, resolvedType,
                     onFinished != null
-                    ? new FinishedDispatcher(this, onFinished, handler)
-                    : null);
+                            ? new FinishedDispatcher(this, onFinished, handler)
+                            : null,
+                    requiredPermission);
             if (res < 0) {
                 throw new CanceledException();
             }
@@ -491,6 +535,20 @@
     }
 
     /**
+     * @hide
+     * Check to verify that this PendingIntent targets a specific package.
+     */
+    public boolean isTargetedToPackage() {
+        try {
+            return ActivityManagerNative.getDefault()
+                .isIntentSenderTargetedToPackage(mTarget);
+        } catch (RemoteException e) {
+            // Should never happen.
+            return false;
+        }
+    }
+
+    /**
      * Comparison operator on two PendingIntent objects, such that true
      * is returned then they both represent the same operation from the
      * same package.  This allows you to use {@link #getActivity},
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index aecec66..fed6d81 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1029,6 +1029,12 @@
      *
      * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
      *
+     * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+     * registered with this method will correctly respect the
+     * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+     * Prior to that, it would be ignored and delivered to all matching registered
+     * receivers.  Be careful if using this for security.</p>
+     *
      * <p class="note">Note: this method <em>cannot be called from a
      * {@link BroadcastReceiver} component;</em> that is, from a BroadcastReceiver
      * that is declared in an application's manifest.  It is okay, however, to call
@@ -1059,6 +1065,12 @@
      *
      * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
      *
+     * <p>As of {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, receivers
+     * registered with this method will correctly respect the
+     * {@link Intent#setPackage(String)} specified for an Intent being broadcast.
+     * Prior to that, it would be ignored and delivered to all matching registered
+     * receivers.  Be careful if using this for security.</p>
+     *
      * @param receiver The BroadcastReceiver to handle the broadcast.
      * @param filter Selects the Intent broadcasts to be received.
      * @param broadcastPermission String naming a permissions that a
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index b7da472..7dbd6f2 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -22,5 +22,5 @@
 /** @hide */
 interface IIntentSender {
     int send(int code, in Intent intent, String resolvedType,
-            IIntentReceiver finishedReceiver);
+            IIntentReceiver finishedReceiver, String requiredPermission);
 }
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 007a715..4db4bdc 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -154,14 +154,47 @@
      */
     public void sendIntent(Context context, int code, Intent intent,
             OnFinished onFinished, Handler handler) throws SendIntentException {
+        sendIntent(context, code, intent, onFinished, handler, null);
+    }
+
+    /**
+     * Perform the operation associated with this IntentSender, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * @param context The Context of the caller.  This may be null if
+     * <var>intent</var> is also null.
+     * @param code Result code to supply back to the IntentSender's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use null to not modify the original Intent.
+     * @param onFinished The object to call back on when the send has
+     * completed, or null for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If null, the callback will happen from the thread
+     * pool of the process.
+     * @param requiredPermission Name of permission that a recipient of the PendingIntent
+     * is required to hold.  This is only valid for broadcast intents, and
+     * corresponds to the permission argument in
+     * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+     * If null, no permission is required.
+     *
+     *
+     * @throws SendIntentException Throws CanceledIntentException if the IntentSender
+     * is no longer allowing more intents to be sent through it.
+     */
+    public void sendIntent(Context context, int code, Intent intent,
+            OnFinished onFinished, Handler handler, String requiredPermission)
+            throws SendIntentException {
         try {
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
             int res = mTarget.send(code, intent, resolvedType,
                     onFinished != null
-                    ? new FinishedDispatcher(this, onFinished, handler)
-                    : null);
+                            ? new FinishedDispatcher(this, onFinished, handler)
+                            : null,
+                    requiredPermission);
             if (res < 0) {
                 throw new SendIntentException();
             }
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 1d3e3ac..b3d7220 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -41,7 +41,7 @@
 /**
  * {@hide}
  */
-public class IntentResolver<F extends IntentFilter, R extends Object> {
+public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
     final private static String TAG = "IntentResolver";
     final private static boolean DEBUG = false;
     final private static boolean localLOGV = DEBUG || false;
@@ -333,14 +333,19 @@
         return false;
     }
 
-    protected String packageForFilter(F filter) {
-        return null;
-    }
+    /**
+     * Return the package that owns this filter.  This must be implemented to
+     * provide correct filtering of Intents that have specified a package name
+     * they are to be delivered to.
+     */
+    protected abstract String packageForFilter(F filter);
     
+    @SuppressWarnings("unchecked")
     protected R newResult(F filter, int match) {
         return (R)filter;
     }
 
+    @SuppressWarnings("unchecked")
     protected void sortResults(List<R> results) {
         Collections.sort(results, mResolvePrioritySorter);
     }
@@ -502,6 +507,7 @@
             String resolvedType, String scheme, List<F> src, List<R> dest) {
         final String action = intent.getAction();
         final Uri data = intent.getData();
+        final String packageName = intent.getPackage();
 
         final boolean excludingStopped = intent.isExcludingStopped();
 
@@ -520,6 +526,14 @@
                 continue;
             }
 
+            // Is delivery being limited to filters owned by a particular package?
+            if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+                if (debug) {
+                    Slog.v(TAG, "  Filter is not from package " + packageName + "; skipping");
+                }
+                continue;
+            }
+
             // Do we already have this one?
             if (!allowFilterResult(filter, dest)) {
                 if (debug) {
@@ -561,6 +575,7 @@
     }
 
     // Sorts a List of IntentFilter objects into descending priority order.
+    @SuppressWarnings("rawtypes")
     private static final Comparator mResolvePrioritySorter = new Comparator() {
         public int compare(Object o1, Object o2) {
             final int q1 = ((IntentFilter) o1).getPriority();
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 656ec4d..56afe7f 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -195,6 +195,7 @@
         final Object mKey;
         final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
         int mPendingBroadcasts;
+        String requiredPermissions;
 
         Receiver(ILocationListener listener) {
             mListener = listener;
@@ -284,7 +285,8 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler);
+                        mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+                                requiredPermissions);
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -319,7 +321,8 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler);
+                        mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+                                requiredPermissions);
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -358,7 +361,8 @@
                     synchronized (this) {
                         // synchronize to ensure incrementPendingBroadcastsLocked()
                         // is called before decrementPendingBroadcasts()
-                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler);
+                        mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+                                requiredPermissions);
                         // call this after broadcasting so we do not increment
                         // if we throw an exeption.
                         incrementPendingBroadcastsLocked();
@@ -572,22 +576,30 @@
         return Settings.Secure.isLocationProviderEnabled(resolver, provider);
     }
 
-    private void checkPermissionsSafe(String provider) {
-        if ((LocationManager.GPS_PROVIDER.equals(provider)
-                 || LocationManager.PASSIVE_PROVIDER.equals(provider))
-            && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
-                != PackageManager.PERMISSION_GRANTED)) {
-            throw new SecurityException("Provider " + provider
-                    + " requires ACCESS_FINE_LOCATION permission");
+    private String checkPermissionsSafe(String provider, String lastPermission) {
+        if (LocationManager.GPS_PROVIDER.equals(provider)
+                 || LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+            if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Provider " + provider
+                        + " requires ACCESS_FINE_LOCATION permission");
+            }
+            return ACCESS_FINE_LOCATION;
         }
-        if (LocationManager.NETWORK_PROVIDER.equals(provider)
-            && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
-                != PackageManager.PERMISSION_GRANTED)
-            && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
-                != PackageManager.PERMISSION_GRANTED)) {
-            throw new SecurityException("Provider " + provider
-                    + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
+
+        // Assume any other provider requires the coarse or fine permission.
+        if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
+                == PackageManager.PERMISSION_GRANTED) {
+            return ACCESS_FINE_LOCATION.equals(lastPermission)
+                    ? lastPermission : ACCESS_COARSE_LOCATION;
         }
+        if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
+                == PackageManager.PERMISSION_GRANTED) {
+            return ACCESS_FINE_LOCATION;
+        }
+
+        throw new SecurityException("Provider " + provider
+                + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
     }
 
     private boolean isAllowedProviderSafe(String provider) {
@@ -1099,8 +1111,21 @@
         }
     }
 
+    void validatePendingIntent(PendingIntent intent) {
+        if (intent.isTargetedToPackage()) {
+            return;
+        }
+        Slog.i(TAG, "Given Intent does not require a specific package: "
+                + intent);
+        // XXX we should really throw a security exception, if the caller's
+        // targetSdkVersion is high enough.
+        //throw new SecurityException("Given Intent does not require a specific package: "
+        //        + intent);
+    }
+
     public void requestLocationUpdatesPI(String provider, Criteria criteria,
             long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
+        validatePendingIntent(intent);
         if (criteria != null) {
             // FIXME - should we consider using multiple providers simultaneously
             // rather than only the best one?
@@ -1132,7 +1157,8 @@
             throw new IllegalArgumentException("provider=" + provider);
         }
 
-        checkPermissionsSafe(provider);
+        receiver.requiredPermissions = checkPermissionsSafe(provider,
+                receiver.requiredPermissions);
 
         // so wakelock calls will succeed
         final int callingUid = Binder.getCallingUid();
@@ -1300,7 +1326,7 @@
         }
 
         // first check for permission to the provider
-        checkPermissionsSafe(provider);
+        checkPermissionsSafe(provider, null);
         // and check for ACCESS_LOCATION_EXTRA_COMMANDS
         if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
                 != PackageManager.PERMISSION_GRANTED)) {
@@ -1432,7 +1458,8 @@
                             synchronized (this) {
                                 // synchronize to ensure incrementPendingBroadcasts()
                                 // is called before decrementPendingBroadcasts()
-                                intent.send(mContext, 0, enteredIntent, this, mLocationHandler);
+                                intent.send(mContext, 0, enteredIntent, this, mLocationHandler,
+                                        ACCESS_FINE_LOCATION);
                                 // call this after broadcasting so we do not increment
                                 // if we throw an exeption.
                                 incrementPendingBroadcasts();
@@ -1457,7 +1484,8 @@
                             synchronized (this) {
                                 // synchronize to ensure incrementPendingBroadcasts()
                                 // is called before decrementPendingBroadcasts()
-                                intent.send(mContext, 0, exitedIntent, this, mLocationHandler);
+                                intent.send(mContext, 0, exitedIntent, this, mLocationHandler,
+                                        ACCESS_FINE_LOCATION);
                                 // call this after broadcasting so we do not increment
                                 // if we throw an exeption.
                                 incrementPendingBroadcasts();
@@ -1526,6 +1554,7 @@
 
     public void addProximityAlert(double latitude, double longitude,
         float radius, long expiration, PendingIntent intent) {
+        validatePendingIntent(intent);
         try {
             synchronized (mLock) {
                 addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
@@ -1626,7 +1655,7 @@
             return null;
         }
 
-        checkPermissionsSafe(provider);
+        checkPermissionsSafe(provider, null);
 
         Bundle b = new Bundle();
         b.putBoolean("network", p.requiresNetwork());
@@ -1668,7 +1697,7 @@
     }
 
     private boolean _isProviderEnabledLocked(String provider) {
-        checkPermissionsSafe(provider);
+        checkPermissionsSafe(provider, null);
 
         LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
@@ -1694,7 +1723,7 @@
     }
 
     private Location _getLastKnownLocationLocked(String provider) {
-        checkPermissionsSafe(provider);
+        checkPermissionsSafe(provider, null);
 
         LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4ec71c1..bf877f6 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -622,6 +622,11 @@
             }
             return true;
         }
+
+        @Override
+        protected String packageForFilter(BroadcastFilter filter) {
+            return filter.packageName;
+        }
     };
 
     /**
@@ -1825,6 +1830,8 @@
                 // We already have the app running, or are waiting for it to
                 // come up (we have a pid but not yet its thread), so keep it.
                 if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
+                // If this is a new package in the process, add the package to the list
+                app.addPackage(info.packageName);
                 return app;
             } else {
                 // An application record is attached to a previous process,
@@ -2278,7 +2285,7 @@
             }
         }
         
-        return pir.sendInner(0, fillInIntent, resolvedType,
+        return pir.sendInner(0, fillInIntent, resolvedType, null,
                 null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
     }
     
@@ -4162,6 +4169,27 @@
         return null;
     }
 
+    public boolean isIntentSenderTargetedToPackage(IIntentSender pendingResult) {
+        if (!(pendingResult instanceof PendingIntentRecord)) {
+            return false;
+        }
+        try {
+            PendingIntentRecord res = (PendingIntentRecord)pendingResult;
+            if (res.key.allIntents == null) {
+                return false;
+            }
+            for (int i=0; i<res.key.allIntents.length; i++) {
+                Intent intent = res.key.allIntents[i];
+                if (intent.getPackage() != null && intent.getComponent() != null) {
+                    return false;
+                }
+            }
+            return true;
+        } catch (ClassCastException e) {
+        }
+        return false;
+    }
+
     public void setProcessLimit(int max) {
         enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
                 "setProcessLimit()");
@@ -9895,6 +9923,7 @@
         ProcessRecord app = getProcessRecordLocked(appName, r.appInfo.uid);
         if (app != null && app.thread != null) {
             try {
+                app.addPackage(r.appInfo.packageName);
                 realStartServiceLocked(r, app);
                 return true;
             } catch (RemoteException e) {
@@ -10945,7 +10974,7 @@
         mBroadcastsScheduled = true;
     }
 
-    public Intent registerReceiver(IApplicationThread caller,
+    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission) {
         synchronized(this) {
             ProcessRecord callerApp = null;
@@ -10957,6 +10986,13 @@
                             + " (pid=" + Binder.getCallingPid()
                             + ") when registering receiver " + receiver);
                 }
+                if (callerApp.info.uid != Process.SYSTEM_UID &&
+                        !callerApp.pkgList.contains(callerPackage)) {
+                    throw new SecurityException("Given caller package " + callerPackage
+                            + " is not running in process " + callerApp);
+                }
+            } else {
+                callerPackage = null;
             }
 
             List allSticky = null;
@@ -11001,7 +11037,7 @@
                 }
                 mRegisteredReceivers.put(receiver.asBinder(), rl);
             }
-            BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
+            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission);
             rl.add(bf);
             if (!bf.debugCheck()) {
                 Slog.w(TAG, "==> For Dynamic broadast");
@@ -12155,6 +12191,7 @@
                     info.activityInfo.applicationInfo.uid);
             if (app != null && app.thread != null) {
                 try {
+                    app.addPackage(info.activityInfo.packageName);
                     processCurBroadcastLocked(r, app);
                     return;
                 } catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b94ee58..b1da69f 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -652,6 +652,7 @@
         
         if (app != null && app.thread != null) {
             try {
+                app.addPackage(r.info.packageName);
                 realStartActivityLocked(r, app, andResume, checkConfig);
                 return;
             } catch (RemoteException e) {
diff --git a/services/java/com/android/server/am/BroadcastFilter.java b/services/java/com/android/server/am/BroadcastFilter.java
index 2e784d3..b49bc22 100644
--- a/services/java/com/android/server/am/BroadcastFilter.java
+++ b/services/java/com/android/server/am/BroadcastFilter.java
@@ -25,12 +25,14 @@
 class BroadcastFilter extends IntentFilter {
     // Back-pointer to the list this filter is in.
     final ReceiverList receiverList;
+    final String packageName;
     final String requiredPermission;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
-            String _requiredPermission) {
+            String _packageName, String _requiredPermission) {
         super(_filter);
         receiverList = _receiverList;
+        packageName = _packageName;
         requiredPermission = _requiredPermission;
     }
     
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index ee6e420..8ed0cc1 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -177,13 +177,13 @@
     }
 
     public int send(int code, Intent intent, String resolvedType,
-            IIntentReceiver finishedReceiver) {
+            IIntentReceiver finishedReceiver, String requiredPermission) {
         return sendInner(code, intent, resolvedType, finishedReceiver,
-                null, null, 0, 0, 0);
+                requiredPermission, null, null, 0, 0, 0);
     }
     
     int sendInner(int code, Intent intent, String resolvedType,
-            IIntentReceiver finishedReceiver,
+            IIntentReceiver finishedReceiver, String requiredPermission,
             IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues) {
         synchronized(owner) {
@@ -246,8 +246,8 @@
                             // that the broadcast be delivered synchronously
                             owner.broadcastIntentInPackage(key.packageName, uid,
                                     finalIntent, resolvedType,
-                                    finishedReceiver, code, null, null, null,
-                                    (finishedReceiver != null), false);
+                                    finishedReceiver, code, null, null,
+                                    requiredPermission, (finishedReceiver != null), false);
                             sendFinish = false;
                         } catch (RuntimeException e) {
                             Slog.w(ActivityManagerService.TAG,