TL;DR
- Android services run in the background and handle tasks like media playback or Bluetooth connections
- Most are low risk, but AIDL services can expose system-level functionality
- AIDL services are common in OEM and system apps, rarely found in regular user-installed apps
- They can lack proper permission checks, making them a valuable attack surface
- Use the service list and service call commands to enumerate and interact with services
- Without source code, reverse engineering or brute-forcing calls can reveal functionality
- Look for methods that skip permission checks or allow unintended access
- Always check for these during app reviews, especially on vendor builds of Android
What are Android services?
Android splits its communications into multiple components, some of which we’ve written about before. One of these components is service, which is a generic component that runs in a background thread. It can be communicated with by the local program through common IPC mechanisms.
Foreground, background, and bound services explained
Along with user interactive tasks, Android needs a method of running things in the background. There are multiple methods of doing this, but the most common method is the service.
This is a task that can run asynchronously in the foreground or the background that could feed back to the user via a notification or a toast / snackbar. For example, if a media application is looking for newly added media, it will use a background service to enumerate the common directories. If an app is playing media, then it will likely use a foreground service to handle the media playing.
Tasks may also be bound, where a component makes a persistent connection to the service. We commonly see bound services in apps to manage hardware connections to third party devices through a facility such as Bluetooth.
Why services can be risky
Services are run in the same thread and process as the rest of the app. This means that a service could prevent an app from terminating or could cause the app to stop responding if processing gets complicated.
How services interact with other apps
Services may also be exported in the manifest so that other apps can use them. Since Android 5.0, Android will only send explicit intents to services to minimise the impact from service spoofing.
If a service has been exported, we can use the am command (or an instance of ActivityManager) to send an intent to the service using the start-service or start-foreground-service subcommands. These follow the same patterns as other components, such as activities or broadcast receivers.
Like other components, the dumpsys command dumps data out of running services, including ActivityManager, using the activity services sub command.

AIDL services and security risks
Usually you won’t find anything interesting in services, except for what are rarely in installed apps, but are quite common in OEM and system provided apps, something known as AIDL services.
AIDL, or Android Interface Definition Language, is named after the process for defining the programming interface. It provides a common mechanism for creating an interface and handles some of the complexity so that data can be passed and returned using the Parcel serialisation standard.
The other important thing is that AIDL does not take account of security – the service must perform authorisation checks itself, leading to opportunities to perform actions you shouldn’t normally be allowed to!
A common use of AIDL services is to provide system level calls that can be run by other processes. Several OEMs also provide their specific features through the use of AIDL services.
Finding and interacting with AIDL services
Listing the active AIDLs on an Android device is simple, from the command line, the service command can be used to list services and send messages to them. For example, the service list command will show all running AIDLs:

Individual calls are numbered from 1 to the maximum that have been implemented. You can brute force these with the service call command, increasing the call number by one until you get a Not a data message error. Normally you wouldn’t have access to the header files or the source code, so you have to guess what parameters a call will take.

The alternative is decompiling the application to looks for the calls. It can be fun to find the application that owns the AIDL as you can call them what you want and not all developers follow a convention.
The dumpsys activity services command generally won’t help here unfortunately as these services may not be controlled by AccountManager.
Example: Reverse engineering the mtkconnmetrics service
Let’s follow an example of reverse engineering an AIDL service, as I’m using a phone with a MediaTek MCU, then I’ll pick one of the MediaTek OEM apps: search_engine_service. The first step is to identify what process owns the service.
The service list command will give the name and (usually) the package that owns that service. In this case the service is called mtkconnmetrics and its interface is com.mediatek.net.connectivity.IMtkIpConnectivityMetrics. It has a service ID of 145, this is assigned in the order the service is started and should not be assumed to be consistent across reboots.

A lot of AIDL services will have a capital I as part of their name (com.mediatek.net.connectivity.IMtkIpConnectivityMetrics), this stands for “interface” and is a convention, although not a requirement.
A lot of the system services are part of AOSP and have public source code, it can be interesting to view some of the AOSP code to get an understanding of how this code is written. The compiled version of the code looks significantly differently to the AIDL template, so it’s worth having a look at a decompiled implementation.
Finding what provides that service can be a real pain. It doesn’t come under the purview of ActivityManager or PackageManager, so tools like dumpsys or pm query-services may not be helpful. It could also be started from init as the phone boots. As I know that this is a system service, I looked in /system/framework, where I made a guess it would be in mediatek-services.jar.
ServiceManager does create an AIDL service, called manager, which offers calls to look at some detail around services, including getService() and getDeclaredInstances(); but these offer little help in locating something.
Decompiling mediatek-services.jar can allow us to find which calls are possible. When an AIDL service is compiled it extends android.os.IInterface, which can be found in com.mediatek.net.connectivity.IMtkIpConnectivity:

Decoding services calls and permissions
When a service call is sent, it will be redirected to the onTransact() method with the service code. onTransact() is normally one large switch statement, which should call the right method to perform the action.
There are some special code items which are reserved by ServiceManager to provide meta information about the service, although only one of these is usually implemented, that is 1598968902, which is 0x5F4E5446 in hex, or “_NTF” as an ASCII string.

The switch statements will usually have a paired class (in this case com.mediatek.net.connectivity.MtkIpConnectivity).
The other special codes can be found in the documentation for android.os.IBinder, which does include an easter egg for the code LIKE_TRANSACTION:

And for TWEET_TRANSACTION:

Anyway, after the management of the special codes, there’s another switch statement that handles the actual calls, with each number equating a method call. For example, if we take case 5:

This gives us a lot of information – because they’ve used an alias for the call number, we can see the name of the method. We can also see the parameters and their type, allowing us to assume a function prototype of updateCtaAppStatus(int, boolean).
Internally it calls another method called updateCtaAppStatus() with the passed parameters. Searching through jadx can find the implementation of this method in com.mediatek.net.connectivity.MtkIpConnectivity.Metrics.Impl:

Practical takeaways for security testing
Now we’re here we can look for methods that allow us to perform tasks or read data. As it’s up to the method to enforce permissions, it should have a call to check the permission. Here the isPermissionAllowed() method checks to see whether the calling UID is 1000, which is the system user.
Checking the support code, shows that no permissions are checked for startMonitorProcessWithUid() and stopMonitorProcessWithUid() (calls 8 and 10), these both are passed an integer. Digging into the code, it looks like it sends NetLink messages to the Netfilter kernel module, unfortunately the messages are static so there’s no potential to manipulate them. They do log the message to the system log (logcat) so that we can demonstrate receiving the message.
Let’s start by “pinging” the service using the standard PING code highlighted above:

This returns a parcel, which is a way of serialising object data into a binary lump. In this this case we can see a UTF-16 string which can be translated to com.mediatek.net.connectivity.IMtkIPConnectivityMetrics which is the basic class.
Let’s send a message to the startMonitorProcessWithUid() method. The i32 is defining the type of parameter as a 32-bit integer with a value of 1.

As the code logs this in the system log we can see what it does when we call this:

Final thoughts
This is only meant to be a quick overview of what an AIDL service is, what it does and how it can be communicated with.
I’ll reiterate that AIDL services tend to be services that are operating system centric. It would be unusual for a standard app to spawn an AIDL service. This makes it important as an attack vector for checking OEMs versions of Android.
As part of a standard app review, checking for services (including AIDL services) should be performed. This includes ensuring the permissions are appropriately checked and other components cannot be manipulated unexpectedly.