Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android remote service callbacks

(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?

like image 415
Android QS Avatar asked Jan 29 '13 21:01

Android QS


1 Answers

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.
like image 84
Adrian Taylor Avatar answered Nov 15 '22 17:11

Adrian Taylor