Lifecycle of ActivityManagerService
Startup
startBootstrapServices(){ [...] traceBeginAndSlog("StartActivityManager"); mActivityManagerService = mSystemServiceManager.startService( ActivityManagerService.Lifecycle.class).getService() }
generic_x86:/ $ ps -A | grep system_server system 2151 2030 1559736 182860 SyS_epoll_wait 0 S system_serve
traceBeginAndSlog("SetWindowManagerService"); mActivityManagerService.setWindowManager(wm);
// 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);
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"); [...] }
Shutdown
@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; }
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!
why does not the ActivityManagerService extends Service and override the onBind method just as the docs describe https://developer.android.com/guide/components/aidl said, but only extends IActivityManager.Stub, so ActivityManagerService is an IBinder implementation, that’s confusing. And can I try to bind the ActivityManagerService manually?
LikeLike
Hy,
The ActivityManagerService is a SystemService that uses a “custom” Binder protocol (defined in IActivityManager.aidl). The Service for third party apps in the end is a wrapper around a standard Binder protocol for non-system services.
So ActivityManagerService doesn’t use the Service API because it has a special communication protocol.
However, every running Activity, Service or ContentProvider is bound to the ActivtiyManagerService via the context.getSystemService(Context.ACTIVITY_SERVICE) (more detailed: the static field IActivityManagerSingelton in the ActivityManager.java holds the corresponding IBinder / IActivityManager.Stub object)
LikeLike