13

(I have a remote service with an AIDL interface that is used by several client apps. I would like to add an asynchronous method to the interface for calls that take some time, but I need the solution to be secure, meaning that only my applications can communicate with the service. The client applications are signed with the same signature as the service app. Currently the apps just bind to the service and call a single interface method to perform various operations.

One option is broadcasting an Intent from the service when the operation is complete and using a BroadcastReceiver in the client application, but (Question #1) can this be done in a way that ensures only my apps can receive the Intent? setPackage() seems to do this, but I need to support Gingerbread devices, which seems to rule out that approach according to the answer here: setPackage for intent in gingerbread

So it seems I need to add a second .aidl interface with the callback interface for the service to use, implemented by the client. I have seen examples that use listeners here, but I am not sure what the difference is versus the client just passing in the second interface object as an argument (as used in the IScript / IScriptResult example from this answer: Service call backs to activity in android)

Question #2, what is the benefit of using a listener here vs. a callback method?

Community
  • 1
  • 1
Android QS
  • 416
  • 2
  • 8
  • 17

1 Answers1

28

A callback method/listener is the right thing to do. (As CommonsWare says, it's pretty much the same thing). I would say it's much simpler than fiddling around with BroadcastReceivers, since you're already using aidl.

Something like this:

IAsyncThing.aidl:

package com.my.thingy;

import com.my.thingy.IAsyncThingListener;

interface IAsyncThing {
  void doSomething(IAsyncThingListener listener);
}

IAsyncThingListener.aidl:

package com.my.thingy;

import com.my.thingy.IAsyncThingListener;

interface IAsyncThingListener {
  void onAsyncThingDone(int resultCodeIfYouLike);
}

You can enforce that only your apps can bind to the service by using a signature-level permission on your service (see the note on 'service permissions' here: http://developer.android.com/guide/topics/security/permissions.html). Specifically:

  • Declare a permission in your service's AndroidManifest.xml. Ensure it is signature level.
  • Add that permission in your service tag
  • In all the other apps, use uses-permission to use it.

A couple of other things to bear in mind:

  • In the caller, you'll need to subclass IAsyncThingListener.Stub. Your calling application code may already be subclassing something else, so that means you'd have to use an extra (probably inner) class to receive the completion notification. I mention this only because this might be the answer to question #2 - which I don't fully understand.
  • If the service is potentially in different processes from the caller, each should register for death notification of the other using IBinder.linkToDeath.
Adrian Taylor
  • 4,054
  • 2
  • 24
  • 26
  • What happens if my service calls the callback and the bound application is no longer active / was moved to the background, or was killed by the system? – Android QS Feb 02 '13 at 01:07
  • 5
    That's what the `IBinder.linkToDeath` bit is for. You'll get told if the application is killed. In all other circumstances (e.g. it's in the background) all the IPC communication should still work fine. – Adrian Taylor Feb 05 '13 at 13:48
  • If the app's activity binds to my service and makes an async request, but is no longer active (not on-screen) when the callback is called, the callback function is still received? Wouldn't the private callback stub object be destroyed when the activity is destroyed? – Android QS Feb 13 '13 at 20:33
  • Your activity is not normally destroyed just because it is no longer on screen. Your activity is destroyed only later when the system is short on memory. – Adrian Taylor Feb 13 '13 at 22:28
  • Great answer dude – Samvel Kartashyan Jan 12 '17 at 19:35
  • 2
    Is there a reason for the import line in `IAsyncThingListener.aidl`? – ingh.am Mar 12 '21 at 12:17
  • 1
    @ingh.am Yes, you must import all non-standard types you want to use in the aidl file. It will not compile otherwise. – Robyer Mar 23 '22 at 18:37