17

I have a remote service, which external applications can bind to. There are situations where I may wish to decline the binding. According to the documentation,

Return the communication channel to the service. May return null if clients can not bind to the service.

@Override
public IBinder onBind(final Intent intent) {
    return null;
}

Returning null does indeed not return an IBinder object and therefore prevents the connection, however the calling application does not correctly receive this 'information'.

boolean bound = context.bindService(intent, serviceConnection, flagsHere);

Whether returning null or not from the Service, this always returns true?

According to the documentation,

Returns - If you have successfully bound to the service, true is returned; false is returned if the connection is not made so you will not receive the service object

I had assumed that returning null from onBind would have caused bindService to return false. Assumptions are never a good idea...

Returning null does however prevent the ServiceConnection from being instantiated invoked, but a consequence of this would be no option to check if the binder is in fact null in onServiceConnected.

So, my question - How does an application 'know' if the binding request has been denied?

Additionally, if I decide on the fly that a request to onRebind (having previously returned true from onUnbind) should be declined, I seem to be unable to override the behaviour to prevent this:

@Override
public void onRebind(final Intent intent) {

    if (shouldAllowRebind(intent)) {
        super.onRebind(intent);
    } else {
        // ?
    }
}

I hope someone can shed some light for me. Thanks in advance.

Onik
  • 19,396
  • 14
  • 68
  • 91
brandall
  • 6,094
  • 4
  • 49
  • 103
  • 4
    The return value from `bindService()` has been an issue for quite some time. "How does an application 'know' if the binding request has been denied?" -- presumably, its `ServiceConnection` never gets called with `onServiceConnected()`. – CommonsWare Mar 21 '16 at 16:58
  • Thanks @CommonsWare I was hoping you'd spot this question! As the `bindService()` has returned true, the app is left in 'limbo' expecting the `ServiceConnection` to call `onServiceConnected()` As it doesn't (due to misbehaviour), would you suggest a monitor thread that waits a few seconds and then checks if a `boolean` identifier has signalled the method was called? Otherwise, mark the context used as null and hope GC will tidy it up? I'm concerned that Android will believe the app is bound to the Service and hold it in memory – brandall Mar 21 '16 at 17:05
  • "would you suggest a monitor thread that waits a few seconds and then checks if a boolean identifier has signalled the method was called?" -- personally, I would look to solve the overall problem in some other way (e.g., rejecting individual API calls to the binder) instead of trying to reject the binding. Given this implementation, using something to implement a timeout (e.g., `postDelayed()` on a `View`) may be your best option. "Otherwise, mark the context used as null and hope GC will tidy it up?" -- sorry, but I didn't understand this part. – CommonsWare Mar 21 '16 at 17:08
  • @CommonsWare Thanks. Rejecting prior to the binding request will not be possible in all circumstances, I won't go into why. I'm concerned that Android will believe the app is bound to the Service and hold it in memory - I only hold a weak reference to the context used - I need to understand if that context remains tied to the service. I'll investigate. I may be over-thinking that part. – brandall Mar 21 '16 at 17:14

1 Answers1

7

You probably have to create a workaround. I see two options here:

  • Return a Binder without any functionality if the request should be denied. The client then has to check if the wanted functionality is there.
  • Always return the same Binder, but let every method throw an Exception (e.g. SecurityException) if the call is not permitted. (This was also suggested by @CommonsWare in the comments)

I would personnaly prefer the second approach as it is more flexible. (e.g. allows per-call permit/deny, solves the problem of denying stuff after a rebind, etc.)

F43nd1r
  • 7,690
  • 3
  • 24
  • 62
  • Thanks for your answer - could you reference in the code where `bindService does not create the Service syncronously` please? For `Are you sure your onServiceConnected isn't called?` 100% did this not happen for you? I'll correct the wording in my question, I think `invoked` would have been better terminology that `instantiated`? – brandall Mar 29 '16 at 21:14
  • The easiest way to show this is if you look at the origin of the onBind call: it comes from a Handler. As you probably know Handlers are mostly used for asyncronous (interprocess) communication. http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/app/ActivityThread.java#1391 – F43nd1r Mar 29 '16 at 21:32
  • The first paragraph of your answer is totally incorrect. If you add simple breakpoints, you'll see that `bindService` is waiting on the response of `onBind` and prior to this `onStartCommand` is called regardless of whether the service was running or not. I'm not sure what you mean in your comment - That the System will use IPC to communicate the response of `onBind`? – brandall Mar 29 '16 at 22:11
  • Breakpoints are not a reliable way to determine the order of calls in different threads, as threads may be executed in parallel (with multicores and virtual parallelization). But that doesn't influence the result anyway. Whether you call it intended behaviour or a workaround won't change your code. – F43nd1r Mar 29 '16 at 22:27
  • Pause the thread in question. I don't think you've tested your answer. – brandall Mar 29 '16 at 22:33
  • I'll remove everything that is not the solution, as it seems to bother you. – F43nd1r Mar 29 '16 at 22:34
  • If you've posted a solution that is incorrect, you have to expect it to be challenged. This isn't personal, it's Stackoverflow. – brandall Mar 29 '16 at 22:39
  • I didn't think of it on a personal level. I just felt that this discussion is pointless as it is only targeting the reason for your problem, not the solution. So I removed everything that might be incorrect to create a straightforward answer, which might help not only you, but also other users with a similar problem. – F43nd1r Mar 29 '16 at 22:43
  • Can you remove the reference to `probably with instanceof` please - It's only ever a BinderProxy so that won't work, but I'd like to reference your idea of `Return a Binder without any functionality if the request should be denied. The client then has to check if the wanted functionality is there` in a workaround I'm going to post and it would be better if I didn't have to correct you? I won't be accepting your or my post as the answer, as both will be a way to handle this, rather than an actual solution or explanation. I thank you for this suggestion though. – brandall Apr 13 '16 at 23:01