diff --git a/api/current.txt b/api/current.txt index dec8c50338abb..ef47651656181 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10081,6 +10081,7 @@ package android.content { public abstract interface ServiceConnection { method public default void onBindingDied(android.content.ComponentName); + method public default void onNullBinding(android.content.ComponentName); method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder); method public abstract void onServiceDisconnected(android.content.ComponentName); } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index de6230cf825aa..ebd101494588e 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1646,9 +1646,12 @@ public final class LoadedApk { if (dead) { mConnection.onBindingDied(name); } - // If there is a new service, it is now connected. + // If there is a new viable service, it is now connected. if (service != null) { mConnection.onServiceConnected(name, service); + } else { + // The binding machinery worked, but the remote returned null from onBind(). + mConnection.onNullBinding(name); } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ca8120895d342..9800ab0041b74 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2801,10 +2801,17 @@ public abstract class Context { * example, if this Context is an Activity that is stopped, the service will * not be required to continue running until the Activity is resumed. * - *
This function will throw {@link SecurityException} if you do not + *
If the service does not support binding, it may return {@code null} from + * its {@link android.app.Service#onBind(Intent) onBind()} method. If it does, then + * the ServiceConnection's + * {@link ServiceConnection#onNullBinding(ComponentName) onNullBinding()} method + * will be invoked instead of + * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}. + * + *
This method will throw {@link SecurityException} if the calling app does not * have permission to bind to the given service. * - *
Note: this method can not be called from a
+ * Note: this method cannot be called from a
* {@link BroadcastReceiver} component. A pattern you can use to
* communicate from a BroadcastReceiver to a Service is to call
* {@link #startService} with the arguments containing the command to be
@@ -2827,8 +2834,8 @@ public abstract class Context {
* {@link #BIND_WAIVE_PRIORITY}.
* @return If you have successfully bound to the service, {@code true} is returned;
* {@code false} is returned if the connection is not made so you will not
- * receive the service object. However, you should still call
- * {@link #unbindService} to release the connection.
+ * receive the service object. You should still call {@link #unbindService}
+ * to release the connection even if this method returned {@code false}.
*
* @throws SecurityException If the caller does not have permission to access the service
* or the service can not be found.
diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java
index 6ff4900212ffc..c16dbbe33aab9 100644
--- a/core/java/android/content/ServiceConnection.java
+++ b/core/java/android/content/ServiceConnection.java
@@ -63,4 +63,21 @@ public interface ServiceConnection {
*/
default void onBindingDied(ComponentName name) {
}
+
+ /**
+ * Called when the service being bound has returned {@code null} from its
+ * {@link android.app.Service#onBind(Intent) onBind()} method. This indicates
+ * that the attempting service binding represented by this ServiceConnection
+ * will never become usable.
+ *
+ * The app which requested the binding must still call
+ * {@link Context#unbindService(ServiceConnection)} to release the tracking
+ * resources associated with this ServiceConnection even if this callback was
+ * invoked following {@link Context#bindService Context.bindService() bindService()}.
+ *
+ * @param name The concrete component name of the service whose binding
+ * has been rejected by the Service implementation.
+ */
+ default void onNullBinding(ComponentName name) {
+ }
}