[AOSP] ActivityManager and ActivityManagerService

Lifecycle of ActivityManagerService

Bildschirmfoto vom 2018-03-12 00-57-33
The Android Operating system consists of multiple Services like: AlarmManagerService, InputMethodManagerService, TrustManagerService, WindowManagerService, ServiceManager, PermissionController, SystemServer and also ActivityManagerService (see: https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server).
 

Startup

The lifecycle of ActivityManagerService begins with the following call in SystemServer.

startBootstrapServices(){
 [...] 
 traceBeginAndSlog("StartActivityManager");
 mActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService()
}

source: https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/java/com/android/server/SystemServer.java : 510

Some of the core Services also where instantiated there and therefore belong to that process. By entering the following listed command you’ll see the system_server listed in the process table.

generic_x86:/ $ ps -A | grep system_server 
system 2151 2030 1559736 182860 SyS_epoll_wait 0 S system_serve

The SystemServiceManager is a component of SystemServer that handels  all system services running in SystemServer. See more about SystemServer in future posts.

The SystemServer initializes ActivityManagerService with different values and connects it with other services like WindowManagerService. In general WindowManagerService and ActivityManagerService have a close relationship, because there won’t be any Activity without a Window. In android a Window can hold multiple activities and activities can hold multiple fragments.

traceBeginAndSlog("SetWindowManagerService"); 
mActivityManagerService.setWindowManager(wm);

source: https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/java/com/android/server/SystemServer.java : 845

After ActivityServiceManager and other system services are initialized, the SystemServer tells ActivityServiceManager to allow third party services or applications are allowed to run. The call systemReady(…) takes an callback as parameter, wich get’s called when ActivityManagerService is ready. SystemService starts now, alongside other matters, the system UI.

 // We now tell the activity manager it is okay to run third party
 // code. It will call back into us once it has gotten to the state
 // where third party code can really run (but before it has actually
 // started launching the initial applications), for us to complete our
 // initialization.
 mActivityManagerService.systemReady(() -> {
   Slog.i(TAG, "Making services ready");
   [...]
   startSystemUi(context, windowManagerF);
   [...]
 }, BOOT_TIMINGS_TRACE_LOG);

source: https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/java/com/android/server/SystemServer.java : 1673

By looking into the ActivityManagerService.systemReady(…) function, we recognize that systemReady(…) retrieves the devices serial number in order to provide it to new activities and starts the home activity. Home activity in that case means the activity that launches the launcher, an app that is inteded to show all apps to the user, in order to let him open an app. Except the user didn’t setup the system before by using welcome screen, the home activity doesn’t get system user permissions.

public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
    [...]
    sTheRealBuildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE)).getSerial();
    // Enable home activity for system user, so that the system can always boot. We don't   
    // do this when the system user is not setup since the setup wizard should be the one   
    // to handle home activity in this case.   
    [...]
    if (UserManager.isSplitSystemUser() && Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
        ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
        try {
            AppGlobals.getPackageManager().setComponentEnabledSetting(cName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0, UserHandle.USER_SYSTEM);
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }
    }
    startHomeActivityLocked(currentUserId, "systemReady");
    [...]
}

https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/am/ActivityManagerService.java : 14148

Shutdown

The ActivityManagerService is now started and ready for usage. It will run until the system will be shutdown. The function ActivityManagerService.shutDown(…) celebrates how the shutdown procedure of ActivityManagerService works.

The first step is, to check if the calling user has got the required permissions, in that case the android.Manifest.permission.SHUTDOWN permission. By looking into checkCallingPermission, root user and system_server pid allways have the permission to call that function. If the check was successful the Activity stack get’s notified that we’re going to shutdown the system. After that all the other sub services will be shutdown too.

@Override
public boolean shutdown(int timeout) {
    if (checkCallingPermission(android.Manifest.permission.SHUTDOWN) != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("Requires permission " + android.Manifest.permission.SHUTDOWN);
    }
    boolean timedout = false;
    synchronized (this) {
        mShuttingDown = true;
        mStackSupervisor.prepareForShutdownLocked();
        updateEventDispatchingLocked();
        timedout = mStackSupervisor.shutdownLocked(timeout);
    }
    mAppOpsService.shutdown();
    if (mUsageStatsService != null) {
        mUsageStatsService.prepareShutdown();
    }
    mBatteryStatsService.shutdown();
    synchronized (this) {
        mProcessStats.shutdownLocked();
        notifyTaskPersisterLocked(null, true);
    }
    return timedout;
}

Communication

The ActivityManager

The ActivityManagerService can be reached by “client” processes by using the ActivityManger. This class represents the ActivityManagerService and provides an interface for communicate with that service. For transmit data from ActivityManger to ActivityManagerService, the Binder interface is used. The Binder interface will be explained in a future post. While looking into the ActivityManger you get a beatiful overview of all existing events of ActivityManagerService. But remember, not all functions you’ll see in ActivityManager are visible for App developers using Android SDK. Some of them are hidden api, so you’ll have to use Reflection to call them. An other position to get an overview over all events is the IActivityManager.java source file, generated by aidl (Android Interface Definition Language) by using IActivityManager.aidl. Please look at: https://android.googlesource.com/platform/frameworks/base/+/c80f952/core/java/android/app/IActivityManager.java

At the end of the file, you’ll see a lot of integers like:

START_ACTIVITY_TRANSACTION

the value of them define the event code, that must be send via the Binder interface, next to the event parameters. Let’s do an experiment here!

Send Activity finish

We’re going to send an FINISH_ACTIVITY_TRANSACTION event to ActivityManagerService and hope he finishes our activity, or at least gives us a positive return value. My code is usually writte in Kotlin because it’s the new Android way of writing code!

First, we define data Parcel and reply Parcel. A Parcel is an object that is able to be transmitted using Binder. By using writeInt(…), writeString(…) or something like that, the given value get’s serialized and stored into the Parcel.

While data is used to transfer data to our target Service, reply is used to receive the result. Then we’re using reflection in order to obtain the ActivityManagerService Binder interface from ServiceManager. ServiceManager holds all the representatives of Android’s system services. Then we get our Activity Binder token from our Activity instance by using Reflection. The mToken field holds the Binder token wich is used to identify our Activity in every Service. Now we initilize the Parcel’s and write the needed content into them.

The Parcel.writeInterfaceToken(…) is used by ActivityManagerService to consider that the sended event is targeted to him. The Service will check if the inserted string is equal to his package name.  Then we write the Activity’s Binder token and some values like result code and result intent. I suggest our Activity wasn’t started by using startActivityForResult(…) so we don’t have any results. Now we’re able to send our event via amBinder.transact(…) with the FINISH_ACTIVITY_TRANSACTION as event type.  By reading the first integer of our reply Parcel, we’re able to check if the transaction was successful. Take in mind that a negative value means our transaction failed.

fun finishActivity() {
    var reply: Parcel? = null
    var data: Parcel? = null
    val amBinder = Class.forName("android.os.ServiceManager").getMethod("getService", String::class.java).invoke(null, "activity") as IBinder
    val tokenField = Activity::class.java.getDeclaredField("mToken")
    tokenField.isAccessible = true
    val token = tokenField.get(getActivity()) as IBinder
    reply = Parcel.obtain()
    data = Parcel.obtain()
    data!!.writeInterfaceToken(ActivityManagerService.descriptor)
    data!!.writeStrongBinder(token)
    data!!.writeInt(0)
    data!!.writeInt(0)
    NativeInterface.onNativeLog("transact: " + data + " with token: " + token)
    amBinder.transact(ActivityManagerService.FINISH_ACTIVITY_TRANSACTION, data, reply, 0)
    var resValue: Int = reply!!.readInt()
    NativeInterface.onNativeLog("received: " + resValue)
    if (resValue < 0) {
        NativeInterface.onNativeLog("Transaction failed")
    } else {
        NativeInterface.onNativeLog("Transaction successful")
    }
}

After execute that code I got the following results on API 27 emulator:

03-11 23:51:00.683 12198-12225/com.saroteck.exploittester D/native: transact: android.os.Parcel@9a23f11 with token: android.os.BinderProxy@e0daee9
03-11 23:51:00.689 1616-2739/system_process W/ActivityManager: Force removing ActivityRecord{d3c1d31 u0 com.saroteck.exploittester/.MainActivity t57}: app died, no saved state
03-11 23:51:00.689 12246-12246/? I/cr_ChildProcessService: Destroying ChildProcessService pid=12246
03-11 23:51:00.702 1616-2739/system_process I/WindowManager: Failed to capture screenshot of Token{8638c16 ActivityRecord{d3c1d31 u0 com.saroteck.exploittester/.MainActivity t57 f}} appWin=Window{ff00d1f u0 Zeroday Kitchen} drawState=4
03-11 23:51:00.706 1616-2739/system_process W/ActivityManager: Crash of app com.saroteck.exploittester running instrumentation ComponentInfo{com.saroteck.exploittester.test/android.support.test.runner.AndroidJUnitRunner}
03-11 23:51:00.706 1616-2739/system_process I/ActivityManager: Force stopping com.saroteck.exploittester appid=10081 user=0: finished inst

You see, our app was finished imedately. The result value wasn’t parced because our activity was already removed befour our process was able to execute that line of code.

I guess you ask yourself, how to identify the content to write into our Parcel? To answer this question, take a look into the next ActivityManagerNative.java that defines the client side of aidl. The function finishActivity(…) shows us how to write the transaction to ActivityManagerService.

public boolean finishActivity(IBinder token, int resultCode, Intent resultData)
        throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(token);
    data.writeInt(resultCode);
    if (resultData != null) {
        data.writeInt(1);
        resultData.writeToParcel(data, 0);
    } else {
        data.writeInt(0);
    }
    mRemote.transact(FINISH_ACTIVITY_TRANSACTION, data, reply, 0);
    reply.readException();
    boolean res = reply.readInt() != 0;
    data.recycle();
    reply.recycle();
    return res;
}

source: https://android.googlesource.com/platform/frameworks/base/+/a45746e/core/java/android/app/ActivityManagerNative.java

Most recent events

Start Activity

Starting activity’s will be done by calling the startActivity(…) the Binder interface of ActivityManagerService provides different versions of it. Some are able to start an activity as different user (the user id is also an app identifier on android) or able to start multiple activities simultaneously.

Finish Activity

This event finishes the current activity identified by the given Binder token.

ACTIVITY_PAUSED_TRANSACTION

This event will be send to ActivityManagerService to set the Activtiy state to pause.

ACTIVITY_STOPPED_TRANSACTION

This event will be send to ActivityManagerService to set the Activtiy state to stopped.

HANG_TRANSACTION

This event will be send to inform ActivtiyManagerService that our application is hanging. The ActivityManagerService then, displays an dialog out of an other process in order to let the user decide to wait or to exit the application.

Conclusion

There are a lot of other Events take a look at ActivityManagerNative or IActivityManager.java.

The ActivityManagerService handels the lifecycle of all Activities running on the Operating system. It is one of the most important System Services of Android. If you wan’t a more deep diving article, feel free to comment!

4 Comments

  1. *downloads android exploit. Opens. Is eventually presented with privacy policy. Sees weblink.*
    πŸ€” *Click*….*quickly scans*….
    😯 *Reading more attentively*πŸ•…πŸ€¨πŸ•œ…🧐..πŸ•πŸ•‘………😐…πŸ•.😢….πŸ˜Άβ€πŸŒ«οΈ…..🀯*🧠level up!!* πŸ’ͺ 😎

Comments are closed.