Services are long-running background processes
provided by Android to allow for background tasks such as playing music
and running a game server. They can be started with an Intent and
optionally communicated with over a Binder interface via a call to
Context’s bindService() method. (This is a slight oversimplification,
but by using bindService(), you can eventually get a binder channel to
talk with a Service.) Services are similar to BroadcastReceivers and
Activities in that you can start them independently of their
IntentFilters by specifying a Component (if they are exported).
Services can also be secured by adding a permission check to their
<service> tag in the AndroidManifest.xml. The long-lasting
connections provided by bindService() create a fast IPC channel based
on a Binder interface (see Binder Interfaces section). Binder
interfaces can check permissions on their caller, allowing them to
enforce more than one permission at a time or different permissions on
different requests. Services therefore provide lots of ways to make
sure the caller is trusted, similar to Activities, BroadcastReceivers,
and Binder interfaces.
Calling a Service is
slightly trickier. This hardly matters for scheduling MP3s to play, but
if you need to make sensitive calls into a Service, such as storing
passwords or private messages, you’ll need to validate that the Service
you’re connect to is the correct one and not some hostile program that
shouldn’t have access to the information you provide. (An old attack on
many IPC mechanisms is to “name-squat” on the expected IPC channel or
name. Attackers listen on a port, name, and so on that trusted programs
use to talk. Clients therefore end up talking to the wrong server.) If
you know the exact component you are trying to connect to, you can
specify that explicitly in the Intent you use to connect.
Alternatively, you can verify it against the name provided to your
SeviceConnection’s onServiceConnected (ComponentName name, IBinder
service) implementation. That isn’t very dynamic, though, and doesn’t
let users choose to replace the service provider.
To
dynamically allow users to add replacement services and then authorize
them by means of checking for the permission they declared and were
granted by the user, you can use the component name’s package as a way
to validate the permission. The package name is also available to your
ServiceConnection’s onServiceConnected(ComponentName name, IBinder
binder) method. You receive the name of the implementing component when
you receive the onServiceConnected() callback, and this name is
associated with the application’s rights. This is perhaps harder to
explain than to do, it and comes down to only a single line of code:
res = getPackageManager().checkPermission(permToCheck,
name.getPackageName());
Compare the result
of the checkPermission() call shown here with the constants
PackageManager.PERMISSION_GRANTED and PackageManager.
PERMISSION_DENIED. As documented, the returned value is an integer, not
a boolean.