summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRiddle Hsu <riddlehsu@google.com>2019-05-17 23:10:16 -0600
committerChris Wailes <chriswailes@google.com>2020-03-03 16:36:46 -0800
commit364f2500e536c61a4acb1ac7129a2ef348b684ff (patch)
tree52b2a19f68347a8f7f2bde90a23e1fbd30ab4615
parent980e0e1286e6eb289147e89cb0d47140e5de1f53 (diff)
downloadbase-364f2500e536c61a4acb1ac7129a2ef348b684ff.tar.gz
Start process of next activity with top priority in advance
In common cases, to resume the next activity we need to wait for the current one to be paused. Since starting a process for activity is asynchronous, if we already know the process of next activity has not started yet, we can start the process earlier so the time waiting for the pause to complete can be saved. Also if the launching activity is going to be the top app, we can set the top schedule group right after its process is started so the start time before actually launching the activity can be improved. Although before the current activity is paused, the next top activity may still change and results some empty processes. That should not be a common case and the process is still useful when going back the stack, and the empty background processes are easier to be reclaimed. Bug: 123043091 Test: AppLaunchTest Test: Launch calculator from launcher, the event log am_proc_start will show "pre-top-activity". Test: Cold launch a top activity, the system log should not show "not expected top priority". Test: Use startActivities to start serveral activities in a sequence. Check "adb shell cat /proc/$pid/task/$pid/cgroup" for each process. Only the last one has top-app, others are background. Change-Id: I9601b66e7cc0855fd7c2b573ded31fcf8d0711ae Merged-In: I9601b66e7cc0855fd7c2b573ded31fcf8d0711ae
-rw-r--r--core/java/android/app/ActivityManagerInternal.java3
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/ZygoteProcess.java12
-rw-r--r--core/java/com/android/internal/os/Zygote.java20
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java7
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java2
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp44
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/HostingRecord.java23
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java39
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityStack.java13
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java16
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java18
15 files changed, 171 insertions, 58 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 1725db046a95..9b59ab453acf 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -29,7 +29,6 @@ import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.TransactionTooLargeException;
-import android.view.RemoteAnimationAdapter;
import java.util.ArrayList;
import java.util.List;
@@ -300,7 +299,7 @@ public abstract class ActivityManagerInternal {
/** Starts a given process. */
public abstract void startProcess(String processName, ApplicationInfo info,
- boolean knownToBeDead, String hostingType, ComponentName hostingName);
+ boolean knownToBeDead, boolean isTop, String hostingType, ComponentName hostingName);
/** Starts up the starting activity process for debugging if needed.
* This function needs to be called synchronously from WindowManager context so the caller
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 10b4e5ddcd55..1c0f0f42a5dc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -525,6 +525,7 @@ public class Process {
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param isTopApp whether the process starts for high priority application.
* @param disabledCompatChanges null-ok list of disabled compat changes for the process being
* started.
* @param zygoteArgs Additional arguments to supply to the zygote process.
@@ -545,12 +546,13 @@ public class Process {
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable String[] zygoteArgs) {
return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- /*useUsapPool=*/ true, disabledCompatChanges, zygoteArgs);
+ /*useUsapPool=*/ true, isTopApp, disabledCompatChanges, zygoteArgs);
}
/** @hide */
@@ -571,7 +573,7 @@ public class Process {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, packageName,
- /*useUsapPool=*/ false, disabledCompatChanges, zygoteArgs);
+ /*useUsapPool=*/ false, /*isTopApp=*/ false, disabledCompatChanges, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 016823e1a07d..32df0529a41c 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -309,6 +309,7 @@ public class ZygoteProcess {
* @param disabledCompatChanges null-ok list of disabled compat changes for the process being
* started.
* @param zygoteArgs Additional arguments to supply to the zygote process.
+ * @param isTopApp Whether the process starts for high priority application.
*
* @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
@@ -325,6 +326,7 @@ public class ZygoteProcess {
@Nullable String invokeWith,
@Nullable String packageName,
boolean useUsapPool,
+ boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable String[] zygoteArgs) {
// TODO (chriswailes): Is there a better place to check this value?
@@ -336,7 +338,7 @@ public class ZygoteProcess {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
- packageName, useUsapPool, disabledCompatChanges, zygoteArgs);
+ packageName, useUsapPool, isTopApp, disabledCompatChanges, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -537,6 +539,7 @@ public class ZygoteProcess {
* @param startChildZygote Start a sub-zygote. This creates a new zygote process
* that has its state cloned from this zygote process.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param isTopApp Whether the process starts for high priority application.
* @param disabledCompatChanges a list of disabled compat changes for the process being started.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
@@ -556,6 +559,7 @@ public class ZygoteProcess {
boolean startChildZygote,
@Nullable String packageName,
boolean useUsapPool,
+ boolean isTopApp,
@Nullable long[] disabledCompatChanges,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
@@ -628,6 +632,10 @@ public class ZygoteProcess {
argsForZygote.add("--package-name=" + packageName);
}
+ if (isTopApp) {
+ argsForZygote.add(Zygote.START_AS_TOP_APP_ARG);
+ }
+
if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
final StringBuilder sb = new StringBuilder();
sb.append("--disabled-compat-changes=");
@@ -1190,7 +1198,7 @@ public class ZygoteProcess {
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
true /* startChildZygote */, null /* packageName */,
- false /* useUsapPool */,
+ false /* useUsapPool */, false /* isTopApp */,
null /* disabledCompatChanges */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 5f196a0e4c1c..d047415aacfb 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -164,6 +164,9 @@ public final class Zygote {
/** Number of bytes sent to the Zygote over USAP pipes or the pool event FD */
public static final int USAP_MANAGEMENT_MESSAGE_BYTES = 8;
+ /** Make the new process have top application priority. */
+ public static final String START_AS_TOP_APP_ARG = "--is-top-app";
+
/**
* An extraArg passed when a zygote process is forking a child-zygote, specifying a name
* in the abstract socket namespace. This socket name is what the new child zygote
@@ -266,18 +269,20 @@ public final class Zygote {
* new zygote process.
* @param instructionSet null-ok the instruction set to use.
* @param appDataDir null-ok the data directory of the app.
+ * @param isTopApp true if the process is for top (high priority) application.
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ boolean isTopApp) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, startChildZygote, instructionSet, appDataDir);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp);
if (pid == 0) {
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -293,7 +298,7 @@ public final class Zygote {
private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
- String appDataDir);
+ String appDataDir, boolean isTopApp);
/**
* Specialize an unspecialized app process. The current VM must have been started
@@ -315,12 +320,13 @@ public final class Zygote {
* new zygote process.
* @param instructionSet null-ok The instruction set to use.
* @param appDataDir null-ok The data directory of the app.
+ * @param isTopApp True if the process is for top (high priority) application.
*/
public static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName,
- boolean startChildZygote, String instructionSet, String appDataDir) {
+ boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp) {
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
- niceName, startChildZygote, instructionSet, appDataDir);
+ niceName, startChildZygote, instructionSet, appDataDir, isTopApp);
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -339,7 +345,7 @@ public final class Zygote {
private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
- boolean startChildZygote, String instructionSet, String appDataDir);
+ boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp);
/**
* Called to do any initialization before starting an application.
@@ -662,7 +668,7 @@ public final class Zygote {
specializeAppProcess(args.mUid, args.mGid, args.mGids,
args.mRuntimeFlags, rlimits, args.mMountExternal,
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
- args.mInstructionSet, args.mAppDataDir);
+ args.mInstructionSet, args.mAppDataDir, args.mIsTopApp);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 3915ba273dfa..4442cf0c8208 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -210,6 +210,11 @@ class ZygoteArguments {
int mHiddenApiAccessStatslogSampleRate = -1;
/**
+ * @see Zygote#START_AS_TOP_APP_ARG
+ */
+ boolean mIsTopApp;
+
+ /**
* A set of disabled app compatibility changes for the running app. From
* --disabled-compat-changes.
*/
@@ -422,6 +427,8 @@ class ZygoteArguments {
mUsapPoolStatusSpecified = true;
mUsapPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1));
expectRuntimeArgs = false;
+ } else if (arg.startsWith(Zygote.START_AS_TOP_APP_ARG)) {
+ mIsTopApp = true;
} else if (arg.startsWith("--disabled-compat-changes=")) {
if (mDisabledCompatChanges != null) {
throw new IllegalArgumentException("Duplicate arg specified");
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 24ea59ae6ac0..8f1f4cc1077c 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -257,7 +257,7 @@ class ZygoteConnection {
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
- parsedArgs.mInstructionSet, parsedArgs.mAppDataDir);
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp);
try {
if (pid == 0) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ea58cbd99179..0ce61de270f8 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -663,11 +663,25 @@ static void SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inh
}
}
-static void SetSchedulerPolicy(fail_fn_t fail_fn) {
- errno = -set_sched_policy(0, SP_DEFAULT);
+static void SetSchedulerPolicy(fail_fn_t fail_fn, bool is_top_app) {
+ SchedPolicy policy = is_top_app ? SP_TOP_APP : SP_DEFAULT;
+
+ if (is_top_app && cpusets_enabled()) {
+ errno = -set_cpuset_policy(0, policy);
+ if (errno != 0) {
+ fail_fn(CREATE_ERROR("set_cpuset_policy(0, %d) failed: %s", policy, strerror(errno)));
+ }
+ }
+
+ errno = -set_sched_policy(0, policy);
if (errno != 0) {
- fail_fn(CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno)));
+ fail_fn(CREATE_ERROR("set_sched_policy(0, %d) failed: %s", policy, strerror(errno)));
}
+
+ // We are going to lose the permission to set scheduler policy during the specialization, so make
+ // sure that we don't cache the fd of cgroup path that may cause sepolicy violation by writing
+ // value to the cached fd directly when creating new thread.
+ DropTaskProfilesResourceCaching();
}
static int UnmountTree(const char* path) {
@@ -1055,7 +1069,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jint mount_external, jstring managed_se_info,
jstring managed_nice_name, bool is_system_server,
bool is_child_zygote, jstring managed_instruction_set,
- jstring managed_app_data_dir) {
+ jstring managed_app_data_dir, bool is_top_app) {
const char* process_name = is_system_server ? "system_server" : "zygote";
auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1118,6 +1132,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// privileged syscalls used below still need to be accessible in app process.
SetUpSeccompFilter(uid, is_child_zygote);
+ // Must be called before losing the permission to set scheduler policy.
+ SetSchedulerPolicy(fail_fn, is_top_app);
+
if (setresuid(uid, uid, uid) == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
}
@@ -1176,8 +1193,6 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
- SetSchedulerPolicy(fail_fn);
-
__android_log_close();
stats_log_close();
@@ -1446,7 +1461,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
- jstring instruction_set, jstring app_data_dir) {
+ jstring instruction_set, jstring app_data_dir, jboolean is_top_app) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -1482,7 +1497,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
- is_child_zygote == JNI_TRUE, instruction_set, app_data_dir);
+ is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
+ is_top_app == JNI_TRUE);
}
return pid;
}
@@ -1514,7 +1530,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
- false, nullptr, nullptr);
+ false, nullptr, nullptr, /* is_top_app= */ false);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -1637,18 +1653,20 @@ static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
* @param is_child_zygote If the process is to become a WebViewZygote
* @param instruction_set The instruction set expected/requested by the new application
* @param app_data_dir Path to the application's data directory
+ * @param is_top_app If the process is for top (high priority) application
*/
static void com_android_internal_os_Zygote_nativeSpecializeAppProcess(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
- jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir) {
+ jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, nice_name, false,
- is_child_zygote == JNI_TRUE, instruction_set, app_data_dir);
+ is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
+ is_top_app == JNI_TRUE);
}
/**
@@ -1845,7 +1863,7 @@ static jint com_android_internal_os_Zygote_nativeParseSigChld(JNIEnv* env, jclas
static const JNINativeMethod gMethods[] = {
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Z)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
@@ -1858,7 +1876,7 @@ static const JNINativeMethod gMethods[] = {
{ "nativeForkUsap", "(II[IZ)I",
(void *) com_android_internal_os_Zygote_nativeForkUsap },
{ "nativeSpecializeAppProcess",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;)V",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Z)V",
(void *) com_android_internal_os_Zygote_nativeSpecializeAppProcess },
{ "nativeInitNativeState", "(Z)V",
(void *) com_android_internal_os_Zygote_nativeInitNativeState },
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 53d23842ec79..f19dc7a11eaa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4875,7 +4875,7 @@ public class ActivityManagerService extends IActivityManager.Stub
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
- app.setCurrentSchedulingGroup(app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT);
+ mOomAdjuster.setAttachingSchedGroupLocked(app);
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, 0, false);
app.hasShownUi = false;
@@ -18473,16 +18473,19 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public void startProcess(String processName, ApplicationInfo info,
- boolean knownToBeDead, String hostingType, ComponentName hostingName) {
+ public void startProcess(String processName, ApplicationInfo info, boolean knownToBeDead,
+ boolean isTop, String hostingType, ComponentName hostingName) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "startProcess:"
+ processName);
}
synchronized (ActivityManagerService.this) {
+ // If the process is known as top app, set a hint so when the process is
+ // started, the top priority can be applied immediately to avoid cpu being
+ // preempted by other processes before attaching the process of top app.
startProcessLocked(processName, info, knownToBeDead, 0 /* intentFlags */,
- new HostingRecord(hostingType, hostingName),
+ new HostingRecord(hostingType, hostingName, isTop),
false /* allowWhileBooting */, false /* isolated */,
true /* keepIfLarge */);
}
diff --git a/services/core/java/com/android/server/am/HostingRecord.java b/services/core/java/com/android/server/am/HostingRecord.java
index 784dde1e98df..6bb5def26b9d 100644
--- a/services/core/java/com/android/server/am/HostingRecord.java
+++ b/services/core/java/com/android/server/am/HostingRecord.java
@@ -41,6 +41,8 @@ import android.content.ComponentName;
* {@link android.content.Context#BIND_EXTERNAL_SERVICE} service. In that case, the packageName
* and uid in the ApplicationInfo will be set to those of the caller, not of the defining package.
*
+ * {@code mIsTopApp} will be passed to {@link android.os.Process#start}. So Zygote will initialize
+ * the process with high priority.
*/
public final class HostingRecord {
@@ -53,15 +55,22 @@ public final class HostingRecord {
private final int mHostingZygote;
private final String mDefiningPackageName;
private final int mDefiningUid;
+ private final boolean mIsTopApp;
public HostingRecord(String hostingType) {
- this(hostingType, null, REGULAR_ZYGOTE, null, -1);
+ this(hostingType, null /* hostingName */, REGULAR_ZYGOTE, null /* definingPackageName */,
+ -1 /* mDefiningUid */, false /* isTopApp */);
}
public HostingRecord(String hostingType, ComponentName hostingName) {
this(hostingType, hostingName, REGULAR_ZYGOTE);
}
+ public HostingRecord(String hostingType, ComponentName hostingName, boolean isTopApp) {
+ this(hostingType, hostingName.toShortString(), REGULAR_ZYGOTE,
+ null /* definingPackageName */, -1 /* mDefiningUid */, isTopApp /* isTopApp */);
+ }
+
public HostingRecord(String hostingType, String hostingName) {
this(hostingType, hostingName, REGULAR_ZYGOTE);
}
@@ -71,16 +80,18 @@ public final class HostingRecord {
}
private HostingRecord(String hostingType, String hostingName, int hostingZygote) {
- this(hostingType, hostingName, hostingZygote, null, -1);
+ this(hostingType, hostingName, hostingZygote, null /* definingPackageName */,
+ -1 /* mDefiningUid */, false /* isTopApp */);
}
private HostingRecord(String hostingType, String hostingName, int hostingZygote,
- String definingPackageName, int definingUid) {
+ String definingPackageName, int definingUid, boolean isTopApp) {
mHostingType = hostingType;
mHostingName = hostingName;
mHostingZygote = hostingZygote;
mDefiningPackageName = definingPackageName;
mDefiningUid = definingUid;
+ mIsTopApp = isTopApp;
}
public String getType() {
@@ -91,6 +102,10 @@ public final class HostingRecord {
return mHostingName;
}
+ public boolean isTopApp() {
+ return mIsTopApp;
+ }
+
/**
* Returns the UID of the package defining the component we want to start. Only valid
* when {@link #usesAppZygote()} returns true.
@@ -130,7 +145,7 @@ public final class HostingRecord {
public static HostingRecord byAppZygote(ComponentName hostingName, String definingPackageName,
int definingUid) {
return new HostingRecord("", hostingName.toShortString(), APP_ZYGOTE,
- definingPackageName, definingUid);
+ definingPackageName, definingUid, false /* isTopApp */);
}
/**
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index dbf8acf74dbd..80e22e529ba9 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -178,9 +178,12 @@ public final class OomAdjuster {
adjusterThread.start();
Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP);
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup");
final int pid = msg.arg1;
final int group = msg.arg2;
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "setProcessGroup "
+ + msg.obj + " to " + group);
+ }
try {
setProcessGroup(pid, group);
} catch (Exception e) {
@@ -1758,7 +1761,7 @@ public final class OomAdjuster {
break;
}
mProcessGroupHandler.sendMessage(mProcessGroupHandler.obtainMessage(
- 0 /* unused */, app.pid, processGroup));
+ 0 /* unused */, app.pid, processGroup, app.processName));
try {
if (curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
@@ -1949,6 +1952,38 @@ public final class OomAdjuster {
return success;
}
+ @GuardedBy("mService")
+ void setAttachingSchedGroupLocked(ProcessRecord app) {
+ int initialSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ // If the process has been marked as foreground via Zygote.START_FLAG_USE_TOP_APP_PRIORITY,
+ // then verify that the top priority is actually is applied.
+ if (app.hasForegroundActivities()) {
+ String fallbackReason = null;
+ try {
+ // The priority must be the same as how does {@link #applyOomAdjLocked} set for
+ // {@link ProcessList.SCHED_GROUP_TOP_APP}. We don't check render thread because it
+ // is not ready when attaching.
+ if (Process.getProcessGroup(app.pid) == THREAD_GROUP_TOP_APP) {
+ app.getWindowProcessController().onTopProcChanged();
+ setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);
+ } else {
+ fallbackReason = "not expected top priority";
+ }
+ } catch (Exception e) {
+ fallbackReason = e.toString();
+ }
+ if (fallbackReason == null) {
+ initialSchedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+ } else {
+ // The real scheduling group will depend on if there is any component of the process
+ // did something during attaching.
+ Slog.w(TAG, "Fallback pre-set sched group to default: " + fallbackReason);
+ }
+ }
+
+ app.setCurrentSchedulingGroup(app.setSchedGroup = initialSchedGroup);
+ }
+
// ONLY used for unit testing in OomAdjusterTests.java
@VisibleForTesting
void maybeUpdateUsageStats(ProcessRecord app, long nowElapsed) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 8520cb7c30b8..3996ae2172b4 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -51,7 +51,6 @@ import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppProtoEnums;
import android.app.IApplicationThread;
-import android.app.IUidObserver;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.ComponentName;
@@ -1899,6 +1898,14 @@ public final class ProcessList {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkSlow(startTime, "startProcess: asking zygote to start proc");
+ final boolean isTopApp = hostingRecord.isTopApp();
+ if (isTopApp) {
+ // Use has-foreground-activities as a temporary hint so the current scheduling
+ // group won't be lost when the process is attaching. The actual state will be
+ // refreshed when computing oom-adj.
+ app.setHasForegroundActivities(true);
+ }
+
final Process.ProcessStartResult startResult;
if (hostingRecord.usesWebviewZygote()) {
startResult = startWebView(entryPoint,
@@ -1913,13 +1920,13 @@ public final class ProcessList {
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
- /*useUsapPool=*/ false, app.mDisabledCompatChanges,
+ /*useUsapPool=*/ false, isTopApp, app.mDisabledCompatChanges,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, invokeWith, app.info.packageName,
+ app.info.dataDir, invokeWith, app.info.packageName, isTopApp,
app.mDisabledCompatChanges,
new String[]{PROC_START_SEQ_IDENT + app.startSeq});
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 1d8f059a7e75..500cc38f8b66 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3551,7 +3551,7 @@ final class ActivityRecord extends ConfigurationContainer {
mStackSupervisor.scheduleRestartTimeout(this);
}
- private boolean isProcessRunning() {
+ boolean isProcessRunning() {
WindowProcessController proc = app;
if (proc == null) {
proc = mAtmService.mProcessNames.get(processName, info.applicationInfo.uid);
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 242139cd2da7..848971d3b044 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2743,7 +2743,7 @@ class ActivityStack extends ConfigurationContainer {
final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
&& !lastResumedCanPip;
- boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
+ boolean pausing = display.pauseBackStacks(userLeaving, next, false);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
@@ -2759,6 +2759,13 @@ class ActivityStack extends ConfigurationContainer {
if (next.attachedToProcess()) {
next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
true /* activityChange */, false /* updateOomAdj */);
+ } else if (!next.isProcessRunning()) {
+ // Since the start-process is asynchronous, if we already know the process of next
+ // activity isn't running, we can start the process earlier to save the time to wait
+ // for the current activity to be paused.
+ final boolean isTop = this == display.getFocusedStack();
+ mService.startProcessAsync(next, false /* knownToBeDead */, isTop,
+ isTop ? "pre-top-activity" : "pre-activity");
}
if (lastResumed != null) {
lastResumed.setWillCloseOrEnterPip(true);
@@ -2827,7 +2834,7 @@ class ActivityStack extends ConfigurationContainer {
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
boolean anim = true;
- final DisplayContent dc = getDisplay().mDisplayContent;
+ final DisplayContent dc = display.mDisplayContent;
if (prev != null) {
if (prev.finishing) {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
@@ -2984,7 +2991,7 @@ class ActivityStack extends ConfigurationContainer {
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.getReportedProcState(),
- getDisplay().mDisplayContent.isNextTransitionForward()));
+ dc.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 8f573c0e7234..69bd84b1267b 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -995,20 +995,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
r.notifyUnknownVisibilityLaunched();
}
- try {
- if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
- + r.processName);
- }
- // Post message to start process to avoid possible deadlock of calling into AMS with the
- // ATMS lock held.
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
- r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
- mService.mH.sendMessage(msg);
- } finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
- }
+ final boolean isTop = andResume && r.isTopRunningActivity();
+ mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");
}
boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 223205377bb9..c0fd24c39908 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5653,6 +5653,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mH.sendMessage(m);
}
+ void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
+ String hostingType) {
+ try {
+ if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+ + activity.processName);
+ }
+ // Post message to start process to avoid possible deadlock of calling into AMS with the
+ // ATMS lock held.
+ final Message m = PooledLambda.obtainMessage(ActivityManagerInternal::startProcess,
+ mAmInternal, activity.processName, activity.info.applicationInfo, knownToBeDead,
+ isTop, hostingType, activity.intent.getComponent());
+ mH.sendMessage(m);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+ }
+
void setBooting(boolean booting) {
mAmInternal.setBooting(booting);
}