Provide start parameters via setters on ActivityStarter.
A number of arguments are sent to ActivityStarter when starting an
activity. Not all parameters are relevant to each invocation and many
are later modified as the starter prepares to start the activity.
This can lead to callers providing default values for fields they do
not care about.
This change now sets parameters individually on ActivityStarter,
since it is a single use item. Differing behavior, such as waiting
for the start result, are captured in this manner. Aditionally,
clients also now call execute to start the activity.
Bug: 64750076
Test: atest FrameworksServicesTests:ActivityStartControllerTests
Test: atest FrameworksServicesTests:ActivityStarterTests
Change-Id: Ifc88ae6547bc9155527c31709bef1784c6406bda
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5e67396..f5fb93b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4522,9 +4522,18 @@
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
- return mActivityStartController.startActivityMayWait(caller, -1, callingPackage,
- intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");
+ return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
+ .setCaller(caller)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setProfilerInfo(profilerInfo)
+ .setMayWait(bOptions, userId)
+ .execute();
+
}
@Override
@@ -4585,11 +4594,17 @@
// TODO: Switch to user app stacks here.
try {
- int ret = mActivityStartController.startActivityMayWait(null, targetUid,
- targetPackage, intent, resolvedType, null, null, resultTo, resultWho,
- requestCode, startFlags, null, null, null, bOptions, ignoreTargetSecurity,
- userId, null, "startActivityAsCaller");
- return ret;
+ return mActivityStartController.obtainStarter(intent, "startActivityAsCaller")
+ .setCallingUid(targetUid)
+ .setCallingPackage(targetPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setMayWait(bOptions, userId)
+ .setIgnoreTargetSecurity(ignoreTargetSecurity)
+ .execute();
} catch (SecurityException e) {
// XXX need to figure out how to propagate to original app.
// A SecurityException here is generally actually a fault of the original
@@ -4615,9 +4630,18 @@
userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
WaitResult res = new WaitResult();
// TODO: Switch to user app stacks here.
- mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
- resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- profilerInfo, res, null, bOptions, false, userId, null, "startActivityAndWait");
+ mActivityStartController.obtainStarter(intent, "startActivityAndWait")
+ .setCaller(caller)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setMayWait(bOptions, userId)
+ .setProfilerInfo(profilerInfo)
+ .setWaitResult(res)
+ .execute();
return res;
}
@@ -4629,10 +4653,17 @@
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
// TODO: Switch to user app stacks here.
- int ret = mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
- resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null, null,
- config, bOptions, false, userId, null, "startActivityWithConfig");
- return ret;
+ return mActivityStartController.obtainStarter(intent, "startActivityWithConfig")
+ .setCaller(caller)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setGlobalConfiguration(config)
+ .setMayWait(bOptions, userId)
+ .execute();
}
@Override
@@ -4678,9 +4709,16 @@
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ALLOW_FULL_ONLY, "startVoiceActivity", null);
// TODO: Switch to user app stacks here.
- return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
- intent, resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo,
- null, null, bOptions, false, userId, null, "startVoiceActivity");
+ return mActivityStartController.obtainStarter(intent, "startVoiceActivity")
+ .setCallingUid(callingUid)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setVoiceSession(session)
+ .setVoiceInteractor(interactor)
+ .setStartFlags(startFlags)
+ .setProfilerInfo(profilerInfo)
+ .setMayWait(bOptions, userId)
+ .execute();
}
@Override
@@ -4689,9 +4727,13 @@
enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
ALLOW_FULL_ONLY, "startAssistantActivity", null);
- return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
- intent, resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions,
- false, userId, null, "startAssistantActivity");
+
+ return mActivityStartController.obtainStarter(intent, "startAssistantActivity")
+ .setCallingUid(callingUid)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setMayWait(bOptions, userId)
+ .execute();
}
@Override
@@ -4731,9 +4773,12 @@
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(recentsComponent);
intent.putExtras(options);
- return mActivityStartController.startActivityMayWait(null, recentsUid,
- recentsPackage, intent, null, null, null, null, null, 0, 0, null, null,
- null, activityOptions, false, userId, null, "startRecentsActivity");
+
+ return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
+ .setCallingUid(recentsUid)
+ .setCallingPackage(recentsPackage)
+ .setMayWait(activityOptions, userId)
+ .execute();
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -4906,11 +4951,22 @@
}
final long origId = Binder.clearCallingIdentity();
- int res = mActivityStartController.startActivity(r.app.thread, intent,
- null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
- null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
- r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
- false, false, null, null, "startNextMatchingActivity");
+ // TODO(b/64750076): Check if calling pid should really be -1.
+ final int res = mActivityStartController
+ .obtainStarter(intent, "startNextMatchingActivity")
+ .setCaller(r.app.thread)
+ .setResolvedType(r.resolvedType)
+ .setActivityInfo(aInfo)
+ .setResultTo(resultTo != null ? resultTo.appToken : null)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setCallingPid(-1)
+ .setCallingUid(r.launchedFromUid)
+ .setCallingPackage(r.launchedFromPackage)
+ .setRealCallingPid(-1)
+ .setRealCallingUid(r.launchedFromUid)
+ .setActivityOptions(options)
+ .execute();
Binder.restoreCallingIdentity(origId);
r.finishing = wasFinishing;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index af4d3f8..2405890 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3899,12 +3899,19 @@
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
destIntent.getComponent(), 0, srec.userId);
- int res = mService.getActivityStartController().startActivity(
- srec.app.thread, destIntent, null /*ephemeralIntent*/, null, aInfo,
- null /*rInfo*/, null, null, parent.appToken, null, 0, -1,
- parent.launchedFromUid, parent.launchedFromPackage, -1,
- parent.launchedFromUid, 0, null, false, true, null, null,
- "navigateUpTo");
+ // TODO(b/64750076): Check if calling pid should really be -1.
+ final int res = mService.getActivityStartController()
+ .obtainStarter(destIntent, "navigateUpTo")
+ .setCaller(srec.app.thread)
+ .setActivityInfo(aInfo)
+ .setResultTo(parent.appToken)
+ .setCallingPid(-1)
+ .setCallingUid(parent.launchedFromUid)
+ .setCallingPackage(parent.launchedFromPackage)
+ .setRealCallingPid(-1)
+ .setRealCallingUid(parent.launchedFromUid)
+ .setComponentSpecified(true)
+ .execute();
foundParentInTask = res == ActivityManager.START_SUCCESS;
} catch (RemoteException e) {
foundParentInTask = false;
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 317a68f..a97b93c 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -25,8 +25,6 @@
import android.app.ActivityOptions;
import android.app.IApplicationThread;
-import android.app.ProfilerInfo;
-import android.app.WaitResult;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
@@ -34,7 +32,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
import android.os.FactoryTest;
@@ -43,12 +40,10 @@
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
-import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.ActivityStarter.DefaultFactory;
import com.android.server.am.ActivityStarter.Factory;
@@ -72,7 +67,6 @@
private final ActivityManagerService mService;
private final ActivityStackSupervisor mSupervisor;
- private final ActivityStartInterceptor mInterceptor;
/** Last home activity record we attempted to start. */
private ActivityRecord mLastHomeActivityStartRecord;
@@ -115,7 +109,9 @@
private ActivityStarter mLastStarter;
ActivityStartController(ActivityManagerService service) {
- this(service, service.mStackSupervisor, new DefaultFactory());
+ this(service, service.mStackSupervisor,
+ new DefaultFactory(service, service.mStackSupervisor,
+ new ActivityStartInterceptor(service, service.mStackSupervisor)));
}
@VisibleForTesting
@@ -124,38 +120,20 @@
mService = service;
mSupervisor = supervisor;
mHandler = new StartHandler(mService.mHandlerThread.getLooper());
- mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
mFactory = factory;
+ mFactory.setController(this);
}
/**
- * Retrieves a starter to be used for a new start request. The starter will be added to the
- * active starters list.
- *
- * TODO(b/64750076): This should be removed when {@link #obtainStarter} is implemented. At that
- * time, {@link ActivityStarter#execute} will be able to handle cleaning up the starter's
- * internal references.
+ * @return A starter to configure and execute starting an activity. It is valid until after
+ * {@link ActivityStarter#execute} is invoked. At that point, the starter should be
+ * considered invalid and no longer modified or used.
*/
- private ActivityStarter createStarter() {
- mLastStarter = mFactory.getStarter(this, mService, mService.mStackSupervisor, mInterceptor);
- return mLastStarter;
- }
+ ActivityStarter obtainStarter(Intent intent, String reason) {
+ final ActivityStarter starter = mFactory.obtainStarter();
+ mLastStarter = starter;
- /**
- * TODO(b/64750076): Remove once we directly expose starter interface to callers through
- * {@link #obtainStarter}.
- */
- int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
- String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
- String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
- ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
- ActivityRecord[] outActivity, TaskRecord inTask, String reason) {
- return createStarter().startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
- aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
- callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
- options, ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason);
+ return starter.setIntent(intent).setReason(reason);
}
/**
@@ -170,18 +148,12 @@
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
- final ActivityStarter starter = createStarter();
-
- mLastHomeActivityStartResult = starter.startActivityLocked(null /*caller*/, intent,
- null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
- null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
- null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
- null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
- 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
- false /*componentSpecified*/, tmpOutRecord, null /*inTask*/,
- "startHomeActivity: " + reason);
+ mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
+ .setOutActivity(tmpOutRecord)
+ .setCallingUid(0)
+ .setActivityInfo(aInfo)
+ .execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
-
if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
@@ -228,9 +200,10 @@
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(
ri.activityInfo.packageName, ri.activityInfo.name));
- startActivity(null, intent, null /*ephemeralIntent*/, null, ri.activityInfo,
- null /*rInfo*/, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null,
- false, false, null, null, "startSetupActivity");
+ obtainStarter(intent, "startSetupActivity")
+ .setCallingUid(0)
+ .setActivityInfo(ri.activityInfo)
+ .execute();
}
}
}
@@ -246,9 +219,17 @@
null);
// TODO: Switch to user app stacks here.
- return startActivityMayWait(null, uid, callingPackage,
- intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
- null, null, null, bOptions, false, userId, inTask, reason);
+ return obtainStarter(intent, reason)
+ .setCallingUid(uid)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setResultTo(resultTo)
+ .setResultWho(resultWho)
+ .setRequestCode(requestCode)
+ .setStartFlags(startFlags)
+ .setMayWait(bOptions, userId)
+ .setInTask(inTask)
+ .execute();
}
final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
@@ -262,24 +243,6 @@
return ret;
}
- /**
- * TODO(b/64750076): Remove once we directly expose starter interface to callers through
- * {@link #obtainStarter}.
- */
- int startActivityMayWait(IApplicationThread caller, int callingUid,
- String callingPackage, Intent intent, String resolvedType,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- IBinder resultTo, String resultWho, int requestCode, int startFlags,
- ProfilerInfo profilerInfo, WaitResult outResult,
- Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
- TaskRecord inTask, String reason) {
- return createStarter().startActivityMayWait(caller, callingUid, callingPackage, intent,
- resolvedType, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
- startFlags, profilerInfo, outResult, globalConfig, bOptions,
- ignoreTargetSecurity,
- userId, inTask, reason);
- }
-
int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId,
String reason) {
@@ -340,11 +303,23 @@
ActivityOptions options = ActivityOptions.fromBundle(
i == intents.length - 1 ? bOptions : null);
- int res = startActivity(caller, intent, null /*ephemeralIntent*/,
- resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
- callingPid, callingUid, callingPackage,
- realCallingPid, realCallingUid, 0,
- options, false, componentSpecified, outActivity, null, reason);
+
+ final int res = obtainStarter(intent, reason)
+ .setCaller(caller)
+ .setResolvedType(resolvedTypes[i])
+ .setActivityInfo(aInfo)
+ .setResultTo(resultTo)
+ .setRequestCode(-1)
+ .setCallingPid(callingPid)
+ .setCallingUid(callingUid)
+ .setCallingPackage(callingPackage)
+ .setRealCallingPid(realCallingPid)
+ .setRealCallingUid(realCallingUid)
+ .setActivityOptions(options)
+ .setComponentSpecified(componentSpecified)
+ .setOutActivity(outActivity)
+ .execute();
+
if (res < 0) {
return res;
}
@@ -369,10 +344,11 @@
while (!mPendingActivityLaunches.isEmpty()) {
final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
- final ActivityStarter starter = createStarter();
+ final ActivityStarter starter = obtainStarter(null /* intent */,
+ "pendingActivityLaunch");
try {
- starter.startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume,
- null, null, null /*outRecords*/);
+ starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+ resume, null, null, null /* outRecords */);
} catch (Exception e) {
Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3bee4228..b12351b 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -74,7 +74,6 @@
import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -186,6 +185,13 @@
// The reason we were trying to start the last activity
private String mLastStartReason;
+ /*
+ * Request details provided through setter methods. Should be reset after {@link #execute()}
+ * to avoid unnecessarily retaining parameters. Note that the request is ignored when
+ * {@link #startResolvedActivity} is invoked directly.
+ */
+ private Request mRequest = new Request();
+
/**
* An interface that to provide {@link ActivityStarter} instances to the controller. This is
* used by tests to inject their own starter implementations for verification purposes.
@@ -193,27 +199,96 @@
@VisibleForTesting
interface Factory {
/**
+ * Sets the {@link ActivityStartController} to be passed to {@link ActivityStarter}.
+ */
+ void setController(ActivityStartController controller);
+
+ /**
* Generates an {@link ActivityStarter} that is ready to handle a new start request.
* @param controller The {@link ActivityStartController} which the starter who will own
* this instance.
* @return an {@link ActivityStarter}
*/
- ActivityStarter getStarter(ActivityStartController controller,
- ActivityManagerService service, ActivityStackSupervisor supervisor,
- ActivityStartInterceptor interceptor);
+ ActivityStarter obtainStarter();
}
/**
* Default implementation of {@link StarterFactory}.
*/
static class DefaultFactory implements Factory {
- @Override
- public ActivityStarter getStarter(ActivityStartController controller,
- ActivityManagerService service, ActivityStackSupervisor supervisor,
- ActivityStartInterceptor interceptor) {
- // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
- return new ActivityStarter(controller, service, supervisor, interceptor);
+ private ActivityStartController mController;
+ private ActivityManagerService mService;
+ private ActivityStackSupervisor mSupervisor;
+ private ActivityStartInterceptor mInterceptor;
+
+ DefaultFactory(ActivityManagerService service,
+ ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
+ mService = service;
+ mSupervisor = supervisor;
+ mInterceptor = interceptor;
}
+
+ @Override
+ public void setController(ActivityStartController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public ActivityStarter obtainStarter() {
+ // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
+ return new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
+ }
+ }
+
+ /**
+ * Container for capturing initial start request details. This information is NOT reset until
+ * the {@link ActivityStarter} is recycled, allowing for multiple invocations with the same
+ * parameters.
+ *
+ * TODO(b/64750076): Investigate consolidating member variables of {@link ActivityStarter} with
+ * the request object. Note that some member variables are referenced in
+ * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after
+ * execution.
+ */
+ private static class Request {
+ private static final int DEFAULT_CALLING_UID = -1;
+ private static final int DEFAULT_CALLING_PID = 0;
+
+ IApplicationThread caller;
+ Intent intent;
+ Intent ephemeralIntent;
+ String resolvedType;
+ ActivityInfo activityInfo;
+ ResolveInfo resolveInfo;
+ IVoiceInteractionSession voiceSession;
+ IVoiceInteractor voiceInteractor;
+ IBinder resultTo;
+ String resultWho;
+ int requestCode;
+ int callingPid = DEFAULT_CALLING_UID;
+ int callingUid = DEFAULT_CALLING_PID;
+ String callingPackage;
+ int realCallingPid;
+ int realCallingUid;
+ int startFlags;
+ ActivityOptions activityOptions;
+ boolean ignoreTargetSecurity;
+ boolean componentSpecified;
+ ActivityRecord[] outActivity;
+ TaskRecord inTask;
+ String reason;
+ ProfilerInfo profilerInfo;
+ Configuration globalConfig;
+ Bundle waitOptions;
+ int userId;
+ WaitResult waitResult;
+
+ /**
+ * Indicates that we should wait for the result of the start request. This flag is set when
+ * {@link ActivityStarter#setMayWait(Bundle, int)} is called.
+ * {@see ActivityStarter#startActivityMayWait}.
+ */
+ boolean mayWait;
}
ActivityStarter(ActivityStartController controller, ActivityManagerService service,
@@ -224,14 +299,44 @@
mInterceptor = interceptor;
}
- boolean relatedToPackage(String packageName) {
- return (mLastStartActivityRecord[0] != null
- && packageName.equals(mLastStartActivityRecord[0].packageName))
- || (mStartActivity != null
- && packageName.equals(mStartActivity.packageName));
+ ActivityRecord getStartActivity() {
+ return mStartActivity;
}
- int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+ boolean relatedToPackage(String packageName) {
+ return (mLastStartActivityRecord[0] != null
+ && packageName.equals(mLastStartActivityRecord[0].packageName))
+ || (mStartActivity != null && packageName.equals(mStartActivity.packageName));
+ }
+
+ /**
+ * Starts an activity based on the request parameters provided earlier.
+ * @return The starter result.
+ */
+ int execute() {
+ // TODO(b/64750076): Look into passing request directly to these methods to allow
+ // for transactional diffs and preprocessing.
+ if (mRequest.mayWait) {
+ return startActivityMayWait(mRequest.caller, mRequest.callingUid,
+ mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
+ mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+ mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
+ mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
+ mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
+ mRequest.inTask, mRequest.reason);
+ } else {
+ return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
+ mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
+ mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+ mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
+ mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
+ mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
+ mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
+ mRequest.outActivity, mRequest.inTask, mRequest.reason);
+ }
+ }
+
+ private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
@@ -265,7 +370,6 @@
return result != START_ABORTED ? result : START_SUCCESS;
}
- /** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
@@ -548,8 +652,8 @@
mController.doPendingActivityLaunches(false);
- return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
- options, inTask, outActivity);
+ return startResolvedActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
+ true /* doResume */, options, inTask, outActivity);
}
/**
@@ -610,7 +714,7 @@
}
}
- final int startActivityMayWait(IApplicationThread caller, int callingUid,
+ private int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, Intent intent, String resolvedType,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
@@ -754,7 +858,7 @@
}
final ActivityRecord[] outRecord = new ActivityRecord[1];
- int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+ int res = startActivity(caller, intent, ephemeralIntent, resolvedType,
aInfo, rInfo, voiceSession, voiceInteractor,
resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
@@ -820,10 +924,16 @@
}
}
- int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
- IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
- int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
- ActivityRecord[] outActivity) {
+ /**
+ * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
+ * Note that this method is called internally as well as part of {@link #startActivity}.
+ *
+ * @return The start result.
+ */
+ int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+ IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+ int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+ ActivityRecord[] outActivity) {
int result = START_CANCELED;
try {
mService.mWindowManager.deferSurfaceLayout();
@@ -2008,6 +2118,155 @@
(flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
}
+ ActivityStarter setIntent(Intent intent) {
+ mRequest.intent = intent;
+ return this;
+ }
+
+ ActivityStarter setReason(String reason) {
+ mRequest.reason = reason;
+ return this;
+ }
+
+ ActivityStarter setCaller(IApplicationThread caller) {
+ mRequest.caller = caller;
+ return this;
+ }
+
+ ActivityStarter setEphemeralIntent(Intent intent) {
+ mRequest.ephemeralIntent = intent;
+ return this;
+ }
+
+
+ ActivityStarter setResolvedType(String type) {
+ mRequest.resolvedType = type;
+ return this;
+ }
+
+ ActivityStarter setActivityInfo(ActivityInfo info) {
+ mRequest.activityInfo = info;
+ return this;
+ }
+
+ ActivityStarter setResolveInfo(ResolveInfo info) {
+ mRequest.resolveInfo = info;
+ return this;
+ }
+
+ ActivityStarter setVoiceSession(IVoiceInteractionSession voiceSession) {
+ mRequest.voiceSession = voiceSession;
+ return this;
+ }
+
+ ActivityStarter setVoiceInteractor(IVoiceInteractor voiceInteractor) {
+ mRequest.voiceInteractor = voiceInteractor;
+ return this;
+ }
+
+ ActivityStarter setResultTo(IBinder resultTo) {
+ mRequest.resultTo = resultTo;
+ return this;
+ }
+
+ ActivityStarter setResultWho(String resultWho) {
+ mRequest.resultWho = resultWho;
+ return this;
+ }
+
+ ActivityStarter setRequestCode(int requestCode) {
+ mRequest.requestCode = requestCode;
+ return this;
+ }
+
+ ActivityStarter setCallingPid(int pid) {
+ mRequest.callingPid = pid;
+ return this;
+ }
+
+ ActivityStarter setCallingUid(int uid) {
+ mRequest.callingUid = uid;
+ return this;
+ }
+
+ ActivityStarter setCallingPackage(String callingPackage) {
+ mRequest.callingPackage = callingPackage;
+ return this;
+ }
+
+ ActivityStarter setRealCallingPid(int pid) {
+ mRequest.realCallingPid = pid;
+ return this;
+ }
+
+ ActivityStarter setRealCallingUid(int uid) {
+ mRequest.realCallingUid = uid;
+ return this;
+ }
+
+ ActivityStarter setStartFlags(int startFlags) {
+ mRequest.startFlags = startFlags;
+ return this;
+ }
+
+ ActivityStarter setActivityOptions(ActivityOptions options) {
+ mRequest.activityOptions = options;
+ return this;
+ }
+
+ ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
+ mRequest.ignoreTargetSecurity = ignoreTargetSecurity;
+ return this;
+ }
+
+ ActivityStarter setComponentSpecified(boolean componentSpecified) {
+ mRequest.componentSpecified = componentSpecified;
+ return this;
+ }
+
+ ActivityStarter setOutActivity(ActivityRecord[] outActivity) {
+ mRequest.outActivity = outActivity;
+ return this;
+ }
+
+ ActivityStarter setInTask(TaskRecord inTask) {
+ mRequest.inTask = inTask;
+ return this;
+ }
+
+ ActivityStarter setWaitResult(WaitResult result) {
+ mRequest.waitResult = result;
+ return this;
+ }
+
+ ActivityStarter setProfilerInfo(ProfilerInfo info) {
+ mRequest.profilerInfo = info;
+ return this;
+ }
+
+ ActivityStarter setGlobalConfiguration(Configuration config) {
+ mRequest.globalConfig = config;
+ return this;
+ }
+
+ ActivityStarter setWaitOptions(Bundle options) {
+ mRequest.waitOptions = options;
+ return this;
+ }
+
+ ActivityStarter setUserId(int userId) {
+ mRequest.userId = userId;
+ return this;
+ }
+
+ ActivityStarter setMayWait(Bundle options, int userId) {
+ mRequest.mayWait = true;
+ mRequest.waitOptions = options;
+ mRequest.userId = userId;
+
+ return this;
+ }
+
void dump(PrintWriter pw, String prefix) {
prefix = prefix + " ";
pw.print(prefix);
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index e5872c03..f821f6b 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -122,9 +122,14 @@
throw new IllegalArgumentException("Bad app thread " + appThread);
}
}
- return mService.getActivityStartController().startActivityMayWait(appThread, -1,
- callingPackage, intent, resolvedType, null, null, null, null, 0, 0, null, null,
- null, bOptions, false, callingUser, tr, "AppTaskImpl");
+
+ return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
+ .setCaller(appThread)
+ .setCallingPackage(callingPackage)
+ .setResolvedType(resolvedType)
+ .setMayWait(bOptions, callingUser)
+ .setInTask(tr)
+ .execute();
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index 5676510..7160d7f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,31 +19,16 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
-import android.service.voice.IVoiceInteractionSession;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.internal.app.IVoiceInteractor;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.am.ActivityStarter.Factory;
import org.junit.runner.RunWith;
import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -73,59 +58,10 @@
super.setUp();
mService = createActivityManagerService();
mFactory = mock(Factory.class);
- mStarter = mock(ActivityStarter.class);
- doReturn(mStarter).when(mFactory).getStarter(any(), any(), any(), any());
mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
- }
-
- /**
- * Ensures that the starter is correctly invoked on
- * {@link ActivityStartController#startActivity}
- */
- @Test
- @Presubmit
- public void testStartActivity() {
- final Random random = new Random();
-
- final IApplicationThread applicationThread = mock(IApplicationThread.class);
- final Intent intent = mock(Intent.class);
- final Intent ephemeralIntent = mock(Intent.class);
- final String resolvedType = "TestType";
- final ActivityInfo aInfo = mock(ActivityInfo.class);
- final ResolveInfo rInfo = mock(ResolveInfo.class);
- final IVoiceInteractionSession voiceInteractionSession =
- mock(IVoiceInteractionSession.class);
- final IVoiceInteractor voiceInteractor = mock(IVoiceInteractor.class);
- final IBinder resultTo = mock(IBinder.class);
- final String resultWho = "resultWho";
- final int requestCode = random.nextInt();
- final int callingPid = random.nextInt();
- final int callingUid = random.nextInt();
- final String callingPackage = "callingPackage";
- final int realCallingPid = random.nextInt();
- final int realCallingUid = random.nextInt();
- final int startFlags = random.nextInt();
- final ActivityOptions options = mock(ActivityOptions.class);
- final boolean ignoreTargetSecurity = random.nextBoolean();
- final boolean componentSpecified = random.nextBoolean();
- final ActivityRecord[] outActivity = new ActivityRecord[1];
- final TaskRecord inTask = mock(TaskRecord.class);
- final String reason ="reason";
-
- mController.startActivity(applicationThread, intent, ephemeralIntent, resolvedType,
- aInfo, rInfo, voiceInteractionSession, voiceInteractor, resultTo, resultWho,
- requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid,
- startFlags, options, ignoreTargetSecurity, componentSpecified, outActivity, inTask,
- reason);
-
- // The starter should receive a start command with the originally provided parameters
- verify(mStarter, times(1)).startActivityLocked(eq(applicationThread), eq(intent),
- eq(ephemeralIntent), eq(resolvedType), eq(aInfo), eq(rInfo),
- eq(voiceInteractionSession), eq(voiceInteractor), eq(resultTo), eq(resultWho),
- eq(requestCode), eq(callingPid), eq(callingUid), eq(callingPackage),
- eq(realCallingPid), eq(realCallingUid), eq(startFlags), eq(options),
- eq(ignoreTargetSecurity), eq(componentSpecified), eq(outActivity), eq(inTask),
- eq(reason));
+ mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor,
+ mock(ActivityStartInterceptor.class)));
+ doReturn(mStarter).when(mFactory).obtainStarter();
}
/**
@@ -149,7 +85,7 @@
final boolean resume = random.nextBoolean();
mController.doPendingActivityLaunches(resume);
- verify(mStarter, times(1)).startActivity(eq(activity), eq(source), eq(null),
+ verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 471726b..e8194dd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -166,10 +166,9 @@
* Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
* provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
* and the launch flags specified in the intent. The method constructs a call to
- * {@link ActivityStarter#startActivityLocked} based on these preconditions and ensures the
- * result matches the expected. It is important to note that the method also checks side effects
- * of the start, such as ensuring {@link ActivityOptions#abort()} is called in the relevant
- * scenarios.
+ * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
+ * the expected. It is important to note that the method also checks side effects of the start,
+ * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
* @param preconditions A bitmask representing the preconditions for the launch
* @param launchFlags The launch flags to be provided by the launch {@link Intent}.
* @param expectedResult The expected result from the launch.
@@ -254,14 +253,13 @@
final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
? 1 : 0;
- final int result = starter.startActivityLocked(caller, intent,
- null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
- null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
- null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
- null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
- 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
- false /*componentSpecified*/, null /*outActivity*/,
- null /*inTask*/, "testLaunchActivityPermissionDenied");
+ final int result = starter.setCaller(caller)
+ .setIntent(intent)
+ .setActivityInfo(aInfo)
+ .setResultTo(resultTo)
+ .setRequestCode(requestCode)
+ .setReason("testLaunchActivityPermissionDenied")
+ .execute();
// In some cases the expected result internally is different than the published result. We
// must use ActivityStarter#getExternalResult to translate.
@@ -269,15 +267,18 @@
// Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
if (expectedResult != START_SUCCESS) {
+ final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
+ mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
final ActivityOptions options = spy(ActivityOptions.makeBasic());
- final int optionResult = starter.startActivityLocked(caller, intent,
- null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
- null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
- null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
- null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
- 0 /*startFlags*/, options /*options*/, false /*ignoreTargetSecurity*/,
- false /*componentSpecified*/, null /*outActivity*/,
- null /*inTask*/, "testLaunchActivityPermissionDenied");
+
+ final int optionResult = optionStarter.setCaller(caller)
+ .setIntent(intent)
+ .setActivityInfo(aInfo)
+ .setResultTo(resultTo)
+ .setRequestCode(requestCode)
+ .setReason("testLaunchActivityPermissionDenied")
+ .setActivityOptions(options)
+ .execute();
verify(options, times(1)).abort();
}
}