OSDN Git Service

Start restricting service calls with implicit intents.
authorDianne Hackborn <hackbod@google.com>
Sun, 4 Aug 2013 23:50:16 +0000 (16:50 -0700)
committerDianne Hackborn <hackbod@google.com>
Mon, 5 Aug 2013 23:53:26 +0000 (16:53 -0700)
The bindService() and startService() calls have always had
undefined behavior when used with an implicit Intent and there
are multiple matching services.  Because of this, it is not
safe for applications to use such Intents when interacting with
services, yet the platform would merrily go about doing... something.

In KLP I want to cause this case to be invalid, resulting in
an exception thrown back to the app.  Unfortunately there are
lots of (scary) things relying on this behavior, so we can't
immediately turn it into an exception, even one qualified by the
caller's target SDK version.

In this change, we start loggin a WTF when such a call happens,
and clean up some stuff in Bluetooth that was doing this behavior.

Change-Id: I62e25d07890588d2362104e20b054aebb6c0e007

core/java/android/app/ContextImpl.java
core/java/android/bluetooth/BluetoothA2dp.java
core/java/android/bluetooth/BluetoothHeadset.java
core/java/android/bluetooth/BluetoothHealth.java
core/java/android/bluetooth/BluetoothInputDevice.java
core/java/android/bluetooth/BluetoothPan.java
core/java/android/bluetooth/BluetoothPbap.java
core/java/android/content/Context.java
core/java/android/content/Intent.java
core/java/android/os/Build.java
services/java/com/android/server/BluetoothManagerService.java

index ab11903..60c0288 100644 (file)
@@ -1444,6 +1444,14 @@ class ContextImpl extends Context {
     @Override
     public ComponentName startServiceAsUser(Intent service, UserHandle user) {
         try {
+            if (service.getComponent() == null && service.getPackage() == null) {
+                if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KEY_LIME_PIE) {
+                    IllegalArgumentException ex = new IllegalArgumentException(
+                            "Service Intent must be explicit: " + service);
+                    Log.wtf(TAG, "This will become an error", ex);
+                    //throw ex;
+                }
+            }
             service.prepareToLeaveProcess();
             ComponentName cn = ActivityManagerNative.getDefault().startService(
                 mMainThread.getApplicationThread(), service,
@@ -1468,6 +1476,14 @@ class ContextImpl extends Context {
     @Override
     public boolean stopServiceAsUser(Intent service, UserHandle user) {
         try {
+            if (service.getComponent() == null && service.getPackage() == null) {
+                if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KEY_LIME_PIE) {
+                    IllegalArgumentException ex = new IllegalArgumentException(
+                            "Service Intent must be explicit: " + service);
+                    Log.wtf(TAG, "This will become an error", ex);
+                    //throw ex;
+                }
+            }
             service.prepareToLeaveProcess();
             int res = ActivityManagerNative.getDefault().stopService(
                 mMainThread.getApplicationThread(), service,
@@ -1503,6 +1519,14 @@ class ContextImpl extends Context {
         } else {
             throw new RuntimeException("Not supported in system context");
         }
+        if (service.getComponent() == null && service.getPackage() == null) {
+            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.KEY_LIME_PIE) {
+                IllegalArgumentException ex = new IllegalArgumentException(
+                        "Service Intent must be explicit: " + service);
+                Log.wtf(TAG, "This will become an error", ex);
+                //throw ex;
+            }
+        }
         try {
             IBinder token = getActivityToken();
             if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
index 6fdf3b4..d7d8cdb 100644 (file)
@@ -128,9 +128,7 @@ public final class BluetoothA2dp implements BluetoothProfile {
                             try {
                                 if (mService == null) {
                                     if (VDBG) Log.d(TAG,"Binding service...");
-                                    if (!mContext.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) {
-                                        Log.e(TAG, "Could not bind to Bluetooth A2DP Service");
-                                    }
+                                    doBind();
                                 }
                             } catch (Exception re) {
                                 Log.e(TAG,"",re);
@@ -157,9 +155,18 @@ public final class BluetoothA2dp implements BluetoothProfile {
             }
         }
 
-        if (!context.bindService(new Intent(IBluetoothA2dp.class.getName()), mConnection, 0)) {
-            Log.e(TAG, "Could not bind to Bluetooth A2DP Service");
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothA2dp.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
+            Log.e(TAG, "Could not bind to Bluetooth A2DP Service with " + intent);
+            return false;
         }
+        return true;
     }
 
     /*package*/ void close() {
index 963e9fc..5a5764d 100644 (file)
@@ -241,9 +241,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
                             try {
                                 if (mService == null) {
                                     if (VDBG) Log.d(TAG,"Binding service...");
-                                    if (!mContext.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
-                                        Log.e(TAG, "Could not bind to Bluetooth Headset Service");
-                                    }
+                                    doBind();
                                 }
                             } catch (Exception re) {
                                 Log.e(TAG,"",re);
@@ -270,9 +268,18 @@ public final class BluetoothHeadset implements BluetoothProfile {
             }
         }
 
-        if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) {
-            Log.e(TAG, "Could not bind to Bluetooth Headset Service");
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothHeadset.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
+            Log.e(TAG, "Could not bind to Bluetooth Headset Service with " + intent);
+            return false;
         }
+        return true;
     }
 
     /**
index cb23662..b1a084a 100644 (file)
@@ -117,9 +117,7 @@ public final class BluetoothHealth implements BluetoothProfile {
                             try {
                                 if (mService == null) {
                                     if (VDBG) Log.d(TAG,"Binding service...");
-                                    if (!mContext.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) {
-                                        Log.e(TAG, "Could not bind to Bluetooth Health Service");
-                                    }
+                                    doBind();
                                 }
                             } catch (Exception re) {
                                 Log.e(TAG,"",re);
@@ -483,9 +481,18 @@ public final class BluetoothHealth implements BluetoothProfile {
             }
         }
 
-        if (!context.bindService(new Intent(IBluetoothHealth.class.getName()), mConnection, 0)) {
-            Log.e(TAG, "Could not bind to Bluetooth Health Service");
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothHealth.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
+            Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
+            return false;
         }
+        return true;
     }
 
     /*package*/ void close() {
index db7e424..f9c789c 100644 (file)
@@ -206,9 +206,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
                             try {
                                 if (mService == null) {
                                     if (VDBG) Log.d(TAG,"Binding service...");
-                                    if (!mContext.bindService(new Intent(IBluetoothInputDevice.class.getName()), mConnection, 0)) {
-                                        Log.e(TAG, "Could not bind to Bluetooth HID Service");
-                                    }
+                                    doBind();
                                 }
                             } catch (Exception re) {
                                 Log.e(TAG,"",re);
@@ -237,10 +235,18 @@ public final class BluetoothInputDevice implements BluetoothProfile {
             }
         }
 
-        if (!context.bindService(new Intent(IBluetoothInputDevice.class.getName()),
-                                 mConnection, 0)) {
-            Log.e(TAG, "Could not bind to Bluetooth HID Service");
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothInputDevice.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
+            Log.e(TAG, "Could not bind to Bluetooth HID Service with " + intent);
+            return false;
         }
+        return true;
     }
 
     /*package*/ void close() {
index e25ec86..83d4329 100644 (file)
@@ -137,12 +137,20 @@ public final class BluetoothPan implements BluetoothProfile {
         } catch (RemoteException re) {
             Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
         }
-        Log.d(TAG, "BluetoothPan() call bindService");
-        if (!context.bindService(new Intent(IBluetoothPan.class.getName()),
-                                 mConnection, 0)) {
-            Log.e(TAG, "Could not bind to Bluetooth HID Service");
+        if (VDBG) Log.d(TAG, "BluetoothPan() call bindService");
+        doBind();
+        if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called");
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothPan.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
+            Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
+            return false;
         }
-        Log.d(TAG, "BluetoothPan(), bindService called");
+        return true;
     }
 
     /*package*/ void close() {
@@ -170,11 +178,8 @@ public final class BluetoothPan implements BluetoothProfile {
             //Handle enable request to bind again.
             if (on) {
                 Log.d(TAG, "onBluetoothStateChange(on) call bindService");
-                if (!mContext.bindService(new Intent(IBluetoothPan.class.getName()),
-                                     mConnection, 0)) {
-                    Log.e(TAG, "Could not bind to Bluetooth HID Service");
-                }
-                Log.d(TAG, "BluetoothPan(), bindService called");
+                doBind();
+                if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called");
             } else {
                 if (VDBG) Log.d(TAG,"Unbinding service...");
                 synchronized (mConnection) {
index b5280e5..c42251f 100644 (file)
@@ -129,11 +129,7 @@ public class BluetoothPbap {
                             try {
                                 if (mService == null) {
                                     if (VDBG) Log.d(TAG,"Binding service...");
-                                    if (!mContext.bindService(
-                                                        new Intent(IBluetoothPbap.class.getName()),
-                                                        mConnection, 0)) {
-                                        Log.e(TAG, "Could not bind to Bluetooth PBAP Service");
-                                    }
+                                    doBind();
                                 }
                             } catch (Exception re) {
                                 Log.e(TAG,"",re);
@@ -158,9 +154,18 @@ public class BluetoothPbap {
                 Log.e(TAG,"",e);
             }
         }
-        if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
-            Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothPbap.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
+            Log.e(TAG, "Could not bind to Bluetooth Pbap Service with " + intent);
+            return false;
         }
+        return true;
     }
 
     protected void finalize() throws Throwable {
index 0b1127c..5c37206 100644 (file)
@@ -976,7 +976,7 @@ public abstract class Context {
      * May be null if there are no options.  See {@link android.app.ActivityOptions}
      * for how to build the Bundle supplied here; there are no supported definitions
      * for building it manually.
-     * @param user The UserHandle of the user to start this activity for.
+     * @param userId The UserHandle of the user to start this activity for.
      * @throws ActivityNotFoundException &nbsp;
      * @hide
      */
@@ -1573,9 +1573,11 @@ public abstract class Context {
 
     /**
      * Request that a given application service be started.  The Intent
-     * can either contain the complete class name of a specific service
-     * implementation to start, or an abstract definition through the
-     * action and other fields of the kind of service to start.  If this service
+     * should contain either contain the complete class name of a specific service
+     * implementation to start or a specific package name to target.  If the
+     * Intent is less specified, it will either throw an {@link IllegalArgumentException}
+     * (if the caller targets {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} or later),
+     * or which of multiple matching services it finds and uses will be undefined.  If this service
      * is not already running, it will be instantiated and started (creating a
      * process for it if needed); if it is running then it remains running.
      *
@@ -1601,10 +1603,9 @@ public abstract class Context {
      * <p>This function will throw {@link SecurityException} if you do not
      * have permission to start the given service.
      *
-     * @param service Identifies the service to be started.  The Intent may
-     *      specify either an explicit component name to start, or a logical
-     *      description (action, category, etc) to match an
-     *      {@link IntentFilter} published by a service.  Additional values
+     * @param service Identifies the service to be started.  The Intent must be either
+     *      fully explicit (supplying a component name) or specify a specific package
+     *      name it is targetted to.  Additional values
      *      may be included in the Intent extras to supply arguments along with
      *      this specific start call.
      *
@@ -1634,10 +1635,9 @@ public abstract class Context {
      * <p>This function will throw {@link SecurityException} if you do not
      * have permission to stop the given service.
      *
-     * @param service Description of the service to be stopped.  The Intent may
-     *      specify either an explicit component name to start, or a logical
-     *      description (action, category, etc) to match an
-     *      {@link IntentFilter} published by a service.
+     * @param service Description of the service to be stopped.  The Intent must be either
+     *      fully explicit (supplying a component name) or specify a specific package
+     *      name it is targetted to.
      *
      * @return If there is a service matching the given Intent that is already
      * running, then it is stopped and {@code true} is returned; else {@code false} is returned.
@@ -2296,7 +2296,7 @@ public abstract class Context {
      * camera devices.
      *
      * @see #getSystemService
-     * @see android.hardware.camera.CameraManager
+     * @see android.hardware.camera2.CameraManager
      */
     public static final String CAMERA_SERVICE = "camera";
 
index 30ea3f9..205ca6b 100644 (file)
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.content.pm.ApplicationInfo;
 import android.util.ArraySet;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -44,6 +45,7 @@ import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 
@@ -5034,6 +5036,39 @@ public class Intent implements Parcelable, Cloneable {
     }
 
     /**
+     * Special function for use by the system to resolve service
+     * intents to system apps.  Throws an exception if there are
+     * multiple potential matches to the Intent.  Returns null if
+     * there are no matches.
+     * @hide
+     */
+    public ComponentName resolveSystemService(PackageManager pm, int flags) {
+        if (mComponent != null) {
+            return mComponent;
+        }
+
+        List<ResolveInfo> results = pm.queryIntentServices(this, flags);
+        if (results == null) {
+            return null;
+        }
+        ComponentName comp = null;
+        for (int i=0; i<results.size(); i++) {
+            ResolveInfo ri = results.get(i);
+            if ((ri.serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+                continue;
+            }
+            ComponentName foundComp = new ComponentName(ri.serviceInfo.applicationInfo.packageName,
+                    ri.serviceInfo.name);
+            if (comp != null) {
+                throw new IllegalStateException("Multiple system services handle " + this
+                        + ": " + comp + ", " + foundComp);
+            }
+            comp = foundComp;
+        }
+        return comp;
+    }
+
+    /**
      * Set the general action to be performed.
      *
      * @param action An action name, such as ACTION_VIEW.  Application-specific
@@ -5068,7 +5103,7 @@ public class Intent implements Parcelable, Cloneable {
      *
      * @see #getData
      * @see #setDataAndNormalize
-     * @see android.net.Intent#normalize
+     * @see android.net.Uri#normalizeScheme()
      */
     public Intent setData(Uri data) {
         mData = data;
index 71c3e4a..dd40e35 100644 (file)
@@ -439,6 +439,14 @@ public class Build {
 
         /**
          * Android X.X: Key Lime Pie, another tasty treat.
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li>It is no longer allowed to use implicit intents with
+         * {@link android.content.Context#startService} or
+         * {@link android.content.Context#bindService}.
+         * </ul>
          */
         public static final int KEY_LIME_PIE = CUR_DEVELOPMENT;
     }
index a5cf362..d2d5280 100644 (file)
@@ -643,10 +643,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
                             Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                             mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                             Intent i = new Intent(IBluetooth.class.getName());
-                            if (!mContext.bindServiceAsUser(i, mConnection,
-                                  Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
+                            if (!doBind(i, mConnection,
+                                    Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                                 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-                                Log.e(TAG, "fail to bind to: " + IBluetooth.class.getName());
                             } else {
                                 mBinding = true;
                             }
@@ -1028,10 +1027,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
                 mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
                 mConnection.setGetNameAddressOnly(false);
                 Intent i = new Intent(IBluetooth.class.getName());
-                if (!mContext.bindServiceAsUser(i, mConnection,Context.BIND_AUTO_CREATE,
-                                          UserHandle.CURRENT)) {
+                if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
                     mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
-                    Log.e(TAG, "Fail to bind to: " + IBluetooth.class.getName());
                 } else {
                     mBinding = true;
                 }
@@ -1070,6 +1067,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
         }
     }
 
+    boolean doBind(Intent intent, ServiceConnection conn, int flags, UserHandle user) {
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, conn, flags, user)) {
+            Log.e(TAG, "Fail to bind to: " + intent);
+            return false;
+        }
+        return true;
+    }
+
     private void handleDisable() {
         synchronized(mConnection) {
             // don't need to disable if GetNameAddressOnly is set,
@@ -1122,10 +1129,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
                     if (mContext.getPackageManager().hasSystemFeature(
                                                      PackageManager.FEATURE_BLUETOOTH_LE)) {
                         Intent i = new Intent(IBluetoothGatt.class.getName());
-                        if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE,
-                                                        UserHandle.CURRENT)) {
-                            Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName());
-                        }
+                        doBind(i, mConnection, Context.BIND_AUTO_CREATE, UserHandle.CURRENT);
                     }
                 } else {
                     //If Bluetooth is off, send service down event to proxy objects, and unbind