Understanding the Activity Launch Flow in Android Framework
When you call startActivity() from an Activity, a complex chain of framwork components comes into play to handle the transition to a new screen. This article examines how the launch request propagates through the system and what happens at each stage.
Entry Point in Activity
The public startActivity(Intent) method delegates to an internal implementation with optional parameters:
public class Activity extends ContextThemeWrapper {
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
startActivityForResult(intent, -1, options);
}
public void startActivityForResult(Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this,
mMainThread.getApplicationThread(),
mToken,
this,
intent,
requestCode,
options);
if (requestCode >= 0) {
mStartedActivity = true;
}
} else {
mParent.startActivityFromChild(this, intent, requestCode, options);
}
}
}
The call eventually reaches the Instrumentation class, which serves as the central coordinator for application operations.
Instrumentation Layer
The Instrumentation.execStartActivity() method performs several important tasks before delegating to the system service:
public class Instrumentation {
public ActivityResult execStartActivity(
Context who,
IBinder contextThread,
IBinder token,
Activity target,
Intent intent,
int requestCode,
Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
// Check activity monitors (for testing purposes)
if (mActivityMonitors != null) {
synchronized (mSync) {
for (ActivityMonitor am : mActivityMonitors) {
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
}
}
}
}
try {
intent.migrateExtraStreamToClipData(who);
intent.prepareToLeaveProcess(who);
int result = ActivityTaskManager.getService().startActivity(
whoThread,
who.getOpPackageName(),
who.getAttributionTag(),
intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token,
target != null ? target.mEmbeddedID : null,
requestCode,
0,
null,
options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
}
The critical line is the call to ActivityTaskManager.getService().startActivity(), which transitions the request from the application process to the system server.
ActivityTaskManagerService
The service-side handling begins with the entry method that validates and routes the request:
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public final int startActivity(IApplicationThread caller,
String callingPackage,
String callingFeatureId,
Intent intent,
String resolvedType,
IBinder resultTo,
String resultWho,
int requestCode,
int startFlags,
ProfilerInfo profilerInfo,
Bundle bOptions) {
return startActivityAsUser(caller, callingPackage, callingFeatureId,
intent, resolvedType, resultTo, resultWho, requestCode,
startFlags, profilerInfo, bOptions,
UserHandle.getCallingUserId());
}
private int startActivityAsUser(IApplicationThread caller,
String callingPackage,
@Nullable String callingFeatureId,
Intent intent,
String resolvedType,
IBinder resultTo,
String resultWho,
int requestCode,
int startFlags,
ProfilerInfo profilerInfo,
Bundle bOptions,
int userId,
boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivityAsUser");
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setCallingFeatureId(callingFeatureId)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setActivityOptions(bOptions)
.setUserId(userId)
.execute();
}
}
The builder patern is used here to construct the start request with all necessary parameters before executing it.
ActivityStarter Execution
The ActivityStarter class handles the actual logic of resolving and launching activities:
class ActivityStarter {
int execute() {
try {
final LaunchingState launchingState;
synchronized (mService.mGlobalLock) {
final ActivityRecord caller = ActivityRecord.forTokenLocked(mRequest.resultTo);
if (mRequest.activityInfo == null) {
mRequest.resolveActivity(mSupervisor);
}
}
int result;
synchronized (mService.mGlobalLock) {
result = executeRequest(mRequest);
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(
launchingState, result,
mStartActivity == mLastStartActivityRecord,
mLastStartActivityRecord,
originalOptions);
return getExternalResult(result);
}
} finally {
onExecutionComplete();
}
}
}
The executeRequest() method performs extensive validation and preparation before the actual launch:
private int executeRequest(Request request) {
// Validate caller and extract process information
WindowProcessController callerApp = null;
if (caller != null) {
callerApp = mService.getProcessController(caller);
if (callerApp != null) {
callingPid = callerApp.getPid();
callingUid = callerApp.mInfo.uid;
}
}
// Check for permission issues
if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
err = ActivityManager.START_INTENT_NOT_RESOLVED;
}
if (err == ActivityManager.START_SUCCESS && aInfo == null) {
err = ActivityManager.START_CLASS_NOT_FOUND;
}
// Permission and security checks
boolean abort = !mSupervisor.checkStartAnyActivityPermission(
intent, aInfo, resultWho, requestCode, callingPid,
callingUid, callingPackage, callingFeatureId,
request.ignoreTargetSecurity, inTask != null, callerApp,
resultRecord, resultRootTask);
// Handle runtime permission review if needed
if (aInfo != null && mService.getPackageManagerInternalLocked()
.isPermissionsReviewRequired(aInfo.packageName, userId)) {
// Redirect to permission review activity
intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
}
// Create ActivityRecord for the target
final ActivityRecord r = new ActivityRecord.Builder(mService)
.setCaller(callerApp)
.setLaunchedFromPid(callingPid)
.setLaunchedFromUid(callingUid)
.setIntent(intent)
.setResolvedType(resolvedType)
.setActivityInfo(aInfo)
.setConfiguration(mService.getGlobalConfiguration())
.setResultTo(resultRecord)
.build();
return startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, true, checkedOptions,
inTask, inTaskFragment, restrictedBgActivity, intentGrants);
}
Task Selection and Management
The startActivityUnchecked() method handles task affinity and launch mode resolution:
private int startActivityUnchecked(final ActivityRecord r,
ActivityRecord sourceRecord,
int startFlags,
boolean doResume,
ActivityOptions options,
Task inTask) {
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord);
computeLaunchingTaskFlags();
mIntent.setFlags(mLaunchFlags);
final Task reusedTask = getReusableTask();
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
if (targetTask != null && targetTask.getTopNonFinishingActivity() != null) {
startResult = recycleTask(targetTask, targetTaskTop, reusedTask);
if (startResult != START_SUCCESS) {
return startResult;
}
}
if (newTask) {
setNewTask(taskToAffiliate);
}
if (mTargetRootTask == null) {
mTargetRootTask = getLaunchRootTask(mStartActivity, mLaunchFlags, targetTask);
}
return START_SUCCESS;
}
Window Container Orchestration
After the task is determined, the RootWindowContainer coordinates resuming activities:
class RootWindowContainer {
boolean resumeFocusedTasksTopActivities(
Task targetRootTask,
ActivityRecord target,
ActivityOptions targetOptions,
boolean deferPause) {
if (!mTaskSupervisor.readyToResume()) {
return false;
}
boolean result = false;
if (targetRootTask.isTopRootTaskInDisplayArea()
|| getTopDisplayFocusedRootTask() == targetRootTask) {
result = targetRootTask.resumeTopActivityUncheckedLocked(
target, targetOptions, deferPause);
}
// Handle other displays
for (DisplayContent display : getDisplays()) {
display.forAllRootTasks(rootTask -> {
if (rootTask == targetRootTask) {
return;
}
if (rootTask.isFocusableAndVisible()) {
topRunningActivity.makeActiveIfNeeded(target);
}
});
}
return result;
}
}
Task and Activity Resume
The Task class manages the resume sequence for activities within its stack:
class Task {
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev,
ActivityOptions options,
boolean deferPause) {
if (mInResumeTopActivity) {
return false;
}
boolean someActivityResumed = false;
try {
mInResumeTopActivity = true;
if (isLeafTask() && isFocusableAndVisible()) {
someActivityResumed = resumeTopActivityInnerLocked(prev, options, deferPause);
} else {
for (Task child : getChildTasks()) {
if (child.isTopActivityFocusable()) {
someActivityResumed |= child.resumeTopActivityUncheckedLocked(
prev, options, deferPause);
}
}
}
} finally {
mInResumeTopActivity = false;
}
return someActivityResumed;
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev,
ActivityOptions options,
boolean deferPause) {
final ActivityRecord topActivity = topRunningActivity(true);
if (topActivity == null) {
return resumeNextFocusableActivityWhenRootTaskIsEmpty(prev, options);
}
final TaskFragment topFragment = topActivity.getTaskFragment();
resumed[0] = topFragment.resumeTopActivity(prev, options, deferPause);
return resumed[0];
}
}
Activity Lifecycle Coordination
The TaskFragment.resumeTopActivity() method orchestrates the pause/resume sequence:
class TaskFragment {
final boolean resumeTopActivity(ActivityRecord prev,
ActivityOptions options,
boolean deferPause) {
ActivityRecord next = topRunningActivity(true);
if (next == null || !next.canResumeByCompat()) {
return false;
}
// Skip if already resumed
if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
executeAppTransition(options);
return false;
}
// Wait for current pause to complete
if (!mRootWindowContainer.allPausedActivitiesComplete()) {
return false;
}
boolean pausing = !deferPause && taskDisplayArea.pauseBackTasks(next);
if (mResumedActivity != null) {
pausing |= startPausing(mTaskSupervisor.mUserLeaving,
false, next, "resumeTopActivity");
}
if (pausing) {
// Update process priority while waiting
if (next.attachedToProcess()) {
next.app.updateProcessInfo(true, true, false, false);
} else if (!next.isProcessRunning()) {
final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
mAtmService.startProcessAsync(next, false, isTop,
isTop ? "pre-top-activity" : "pre-activity");
}
return true;
}
// Activity already attached and running
if (next.attachedToProcess()) {
next.setState(RESUMED, "resumeTopActivity");
// Send transaction to client
final ClientTransaction transaction = ClientTransaction.obtain(
next.app.getThread(), next.appToken);
transaction.addCallback(LaunchActivityItem.obtain(
new Intent(next.intent),
System.identityHashCode(next),
next.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(),
next.compat,
task.voiceInteractor,
proc.getReportedProcState(),
next.getSavedState(),
next.getPersistentSavedState(),
results, newIntents, next.takeOptions(),
isTransitionForward,
proc.createProfilerInfoIfNeeded(),
next.assistToken,
activityClientController));
final ActivityLifecycleItem lifecycleItem =
andResume ?
ResumeActivityItem.obtain(isTransitionForward) :
PauseActivityItem.obtain();
transaction.setLifecycleStateRequest(lifecycleItem);
mService.getLifecycleManager().scheduleTransaction(transaction);
return true;
}
// Need to start the activity (process may not exist yet)
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
}
mTaskSupervisor.startSpecificActivity(next, true, true);
return true;
}
}
Process Launch and Activity Binding
When the target process doesn't exist, ActivityTaskSupervisor.startSpecificActivity() intiiates the process:
public class ActivityTaskSupervisor {
void startSpecificActivity(ActivityRecord r,
boolean andResume,
boolean checkConfig) {
final WindowProcessController wpc =
mService.getProcessController(r.processName,
r.info.applicationInfo.uid);
if (wpc != null && wpc.hasThread()) {
realStartActivityLocked(r, wpc, andResume, checkConfig);
return;
}
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
final boolean isTop = andResume && r.isTopRunningActivity();
mService.startProcessAsync(r, false, isTop,
isTop ? "top-activity" : "activity");
}
}
Once the process is established, realStartActivityLocked() sends the launch transaction to the client:
boolean realStartActivityLocked(ActivityRecord r,
WindowProcessController proc,
boolean andResume,
boolean checkConfig)
throws RemoteException {
if (!mRootWindowContainer.allPausedActivitiesComplete()) {
return false;
}
final Task task = r.getTask();
beginDeferResume();
proc.pauseConfigurationDispatch();
try {
r.startFreezingScreenLocked(proc, 0);
r.setProcess(proc);
// Create activity launch transaction
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(
new Intent(r.intent),
System.identityHashCode(r),
r.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(),
r.compat,
r.getFilteredReferrer(r.launchedFromPackage),
task.voiceInteractor,
proc.getReportedProcState(),
r.getSavedState(),
r.getPersistentSavedState(),
results, newIntents, r.takeOptions(),
isTransitionForward,
proc.createProfilerInfoIfNeeded(),
r.assistToken,
activityClientController,
r.createFixedRotationAdjustmentsIfNeeded(),
r.shareableActivityToken,
r.getLaunchedFromBubble()));
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(isTransitionForward);
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
} finally {
endDeferResume();
proc.resumeConfigurationDispatch();
}
if (andResume && readyToResume()) {
rootTask.minimalResumeActivityLocked(r);
}
return true;
}
Component Chain Overview
The activity launch mechanism involves these key components:
| Component | Responsibility |
|---|---|
| Activity | Public API entry point |
| Instrumentation | Intercepts and forwards calls to system |
| ActivityTaskManagerService | Entry point in system server |
| ActivityStartController | Factory for ActivityStarter |
| ActivityStarter | Resolves intent and validates launch |
| RootWindowContainer | Manages display-level activity scheduling |
| Task | Manages activity stack and resume ordering |
| TaskFragment | Coordinates pause/resume sequence |
| ActivityTaskSupervisor | Oversees activity lifecycle and process management |
Summary
The startActivity call traverses multiple layers: from the application through Instrumentation, across IPC to ActivityTaskManagerService, through ActivityStarter for resolution and validation, then into the window management subsystem with RootWindowContainer and Task, before finally reaching TaskFragment which coordinates the actual pause/resume lifecycle transition. The final launch instruction is packaged as a ClientTransaction and sent to the target application's ActivityThread for execution.