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) { + } }