diff --git a/core/api/current.txt b/core/api/current.txt index a57499b013e2c..1a8166b90e12f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4614,6 +4614,7 @@ package android.app { method public java.util.List getAppTasks(); method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo(); method @NonNull public java.util.List getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int); + method @NonNull public java.util.List getHistoricalProcessStartReasons(@IntRange(from=0) int); method public int getLargeMemoryClass(); method public int getLauncherLargeIconDensity(); method public int getLauncherLargeIconSize(); @@ -4639,7 +4640,9 @@ package android.app { method @RequiresPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES) public void killBackgroundProcesses(String); method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int); method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle); + method public void removeApplicationStartInfoCompleteListener(); method @Deprecated public void restartPackage(String); + method public void setApplicationStartInfoCompleteListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.ActivityManager.ApplicationStartInfoCompleteListener); method public void setProcessStateSummary(@Nullable byte[]); method public static void setVrThread(int); method public void setWatchHeapLimit(long); @@ -4662,6 +4665,10 @@ package android.app { method public void startActivity(android.content.Context, android.content.Intent, android.os.Bundle); } + public static interface ActivityManager.ApplicationStartInfoCompleteListener { + method public void onApplicationStartInfoComplete(@NonNull android.app.ApplicationStartInfo); + } + public static class ActivityManager.MemoryInfo implements android.os.Parcelable { ctor public ActivityManager.MemoryInfo(); method public int describeContents(); @@ -5221,6 +5228,52 @@ package android.app { field public static final int REASON_USER_STOPPED = 11; // 0xb } + public final class ApplicationStartInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getDefiningUid(); + method @Nullable public android.content.Intent getIntent(); + method public int getLaunchMode(); + method public int getPackageUid(); + method public int getPid(); + method @NonNull public String getProcessName(); + method public int getRealUid(); + method public int getReason(); + method public int getStartType(); + method public int getStartupState(); + method @NonNull public java.util.Map getStartupTimestamps(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int LAUNCH_MODE_SINGLE_INSTANCE = 2; // 0x2 + field public static final int LAUNCH_MODE_SINGLE_INSTANCE_PER_TASK = 4; // 0x4 + field public static final int LAUNCH_MODE_SINGLE_TASK = 3; // 0x3 + field public static final int LAUNCH_MODE_SINGLE_TOP = 1; // 0x1 + field public static final int LAUNCH_MODE_STANDARD = 0; // 0x0 + field public static final int STARTUP_STATE_ERROR = 1; // 0x1 + field public static final int STARTUP_STATE_FIRST_FRAME_DRAWN = 2; // 0x2 + field public static final int STARTUP_STATE_STARTED = 0; // 0x0 + field public static final int START_REASON_ALARM = 0; // 0x0 + field public static final int START_REASON_BACKUP = 1; // 0x1 + field public static final int START_REASON_BOOT_COMPLETE = 2; // 0x2 + field public static final int START_REASON_BROADCAST = 3; // 0x3 + field public static final int START_REASON_CONTENT_PROVIDER = 4; // 0x4 + field public static final int START_REASON_JOB = 5; // 0x5 + field public static final int START_REASON_LAUNCHER = 6; // 0x6 + field public static final int START_REASON_OTHER = 7; // 0x7 + field public static final int START_REASON_PUSH = 8; // 0x8 + field public static final int START_REASON_RESUMED_ACTIVITY = 9; // 0x9 + field public static final int START_REASON_SERVICE = 10; // 0xa + field public static final int START_REASON_START_ACTIVITY = 11; // 0xb + field public static final int START_TIMESTAMP_APPLICATION_ONCREATE = 2; // 0x2 + field public static final int START_TIMESTAMP_BIND_APPLICATION = 3; // 0x3 + field public static final int START_TIMESTAMP_FIRST_FRAME = 4; // 0x4 + field public static final int START_TIMESTAMP_FULLY_DRAWN = 5; // 0x5 + field public static final int START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE = 1; // 0x1 + field public static final int START_TIMESTAMP_LAUNCH = 0; // 0x0 + field public static final int START_TYPE_COLD = 0; // 0x0 + field public static final int START_TYPE_HOT = 2; // 0x2 + field public static final int START_TYPE_WARM = 1; // 0x1 + } + public final class AsyncNotedAppOp implements android.os.Parcelable { method public int describeContents(); method @Nullable public String getAttributionTag(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 57759c645655e..b8041490c9e6b 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -525,6 +525,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL"}) public static int getCurrentUser(); + method @NonNull @RequiresPermission(android.Manifest.permission.DUMP) public java.util.List getExternalHistoricalProcessStartReasons(@NonNull String, @IntRange(from=0) int); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); method @NonNull public java.util.Collection getSupportedLocales(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index f4089167f3d24..42d056c78eba0 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3675,6 +3675,123 @@ public class ActivityManager { } } + /** + * Return a list of {@link ApplicationStartInfo} records containing the information about the + * most recent app startups. + * + *

Note: System stores this historical information in a ring buffer and only + * the most recent records will be returned.

+ * + * @param maxNum The maximum number of results to be returned; a value of 0 + * means to ignore this parameter and return all matching records. If fewer + * records exist, all existing records will be returned. + * + * @return a list of {@link ApplicationStartInfo} records matching the criteria, sorted in + * the order from most recent to least recent. + */ + @NonNull + public List getHistoricalProcessStartReasons( + @IntRange(from = 0) int maxNum) { + try { + ParceledListSlice startInfos = getService() + .getHistoricalProcessStartReasons(null, maxNum, mContext.getUserId()); + return startInfos == null ? Collections.emptyList() : startInfos.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return a list of {@link ApplicationStartInfo} records containing the information about the + * most recent app startups. + * + *

Note: System stores this historical information in a ring buffer and only + * the most recent records will be returned.

+ * + * @param packageName Package name for which app startups to receive. + * @param maxNum The maximum number of results to be returned; a value of 0 + * means to ignore this parameter and return all matching records. If fewer + * records exist, all existing records will be returned. + * + * @return a list of {@link ApplicationStartInfo} records matching the criteria, sorted in + * the order from most recent to least recent. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(Manifest.permission.DUMP) + public List getExternalHistoricalProcessStartReasons( + @NonNull String packageName, @IntRange(from = 0) int maxNum) { + try { + ParceledListSlice startInfos = getService() + .getHistoricalProcessStartReasons(packageName, maxNum, mContext.getUserId()); + return startInfos == null ? Collections.emptyList() : startInfos.getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Callback to receive {@link ApplicationStartInfo} object once recording of startup related + * metrics is complete. + * Use with {@link #setApplicationStartInfoCompleteListener}. + */ + public interface ApplicationStartInfoCompleteListener { + /** {@link ApplicationStartInfo} is complete, no more info will be added. */ + void onApplicationStartInfoComplete(@NonNull ApplicationStartInfo applicationStartInfo); + } + + /** + * Sets a callback to be notified when the {@link ApplicationStartInfo} records of this startup + * are complete. + * + *

Note: callback will not wait for {@link Activity#reportFullyDrawn} to occur. + * Timestamp for fully drawn may be added after callback occurs. Set callback after invoking + * {@link Activity#reportFullyDrawn} if timestamp for fully drawn is required.

+ * + *

Note: if start records have already been retrieved, the callback will be + * invoked immediately on the specified executor with the previously resolved AppStartInfo.

+ * + *

Note: callback is asynchronous and should be made from a background thread. + *

+ * + * @param executor The executor on which the listener should be called. + * @param listener Callback to be called when collection of {@link ApplicationStartInfo} is + * complete. Will replace existing listener if one is already attached. + * + * @throws IllegalArgumentException if executor or listener are null. + */ + public void setApplicationStartInfoCompleteListener(@NonNull final Executor executor, + @NonNull final ApplicationStartInfoCompleteListener listener) { + Preconditions.checkNotNull(executor, "executor cannot be null"); + Preconditions.checkNotNull(listener, "listener cannot be null"); + IApplicationStartInfoCompleteListener callback = + new IApplicationStartInfoCompleteListener.Stub() { + @Override + public void onApplicationStartInfoComplete(ApplicationStartInfo applicationStartInfo) { + executor.execute(() -> + listener.onApplicationStartInfoComplete(applicationStartInfo)); + } + }; + try { + getService().setApplicationStartInfoCompleteListener(callback, mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Removes the callback set by {@link #setApplicationStartInfoCompleteListener} if there is one. + */ + public void removeApplicationStartInfoCompleteListener() { + try { + getService().removeApplicationStartInfoCompleteListener(mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Return a list of {@link ApplicationExitInfo} records containing the reasons for the most * recent app deaths. diff --git a/core/java/android/app/ApplicationStartInfo.aidl b/core/java/android/app/ApplicationStartInfo.aidl new file mode 100644 index 0000000000000..4322c7e69c452 --- /dev/null +++ b/core/java/android/app/ApplicationStartInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +parcelable ApplicationStartInfo; diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java new file mode 100644 index 0000000000000..794c55e85678f --- /dev/null +++ b/core/java/android/app/ApplicationStartInfo.java @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Map; + +/** Provide information related to a processes startup. */ +public final class ApplicationStartInfo implements Parcelable { + + /** + * State indicating process startup has started. Some information is available in + * {@link ApplicationStartInfo} and more will be added. + */ + public static final int STARTUP_STATE_STARTED = 0; + + /** + * State indicating process startup has failed. Startup information in + * {@link ApplicationStartInfo} is incomplete, but no more will be added. + */ + public static final int STARTUP_STATE_ERROR = 1; + + /** + * State indicating process startup has made it to first frame draw. Startup + * information in {@link ApplicationStartInfo} is complete with potential exception + * of fully drawn timestamp which is not guaranteed to be set. + */ + public static final int STARTUP_STATE_FIRST_FRAME_DRAWN = 2; + + /** Process started due to alarm. */ + public static final int START_REASON_ALARM = 0; + + /** Process started to run backup. */ + public static final int START_REASON_BACKUP = 1; + + /** Process started due to boot complete. */ + public static final int START_REASON_BOOT_COMPLETE = 2; + + /** Process started due to broadcast received. */ + public static final int START_REASON_BROADCAST = 3; + + /** Process started due to access of ContentProvider */ + public static final int START_REASON_CONTENT_PROVIDER = 4; + + /** * Process started to run scheduled job. */ + public static final int START_REASON_JOB = 5; + + /** Process started due to click app icon or widget from launcher. */ + public static final int START_REASON_LAUNCHER = 6; + + /** Process started not for any of the listed reasons. */ + public static final int START_REASON_OTHER = 7; + + /** Process started due to push message. */ + public static final int START_REASON_PUSH = 8; + + /** Process started to resume activity. */ + public static final int START_REASON_RESUMED_ACTIVITY = 9; + + /** Process service started. */ + public static final int START_REASON_SERVICE = 10; + + /** Process started due to Activity started for any reason not explicitly listed. */ + public static final int START_REASON_START_ACTIVITY = 11; + + /** Process started from scratch. */ + public static final int START_TYPE_COLD = 0; + + /** Process retained minimally SavedInstanceState. */ + public static final int START_TYPE_WARM = 1; + + /** Process brought back to foreground. */ + public static final int START_TYPE_HOT = 2; + + /** + * Default. The system always creates a new instance of the activity in the target task and + * routes the intent to it. + */ + public static final int LAUNCH_MODE_STANDARD = 0; + + /** + * If an instance of the activity already exists at the top of the target task, the system + * routes the intent to that instance through a call to its onNewIntent() method, rather than + * creating a new instance of the activity. + */ + public static final int LAUNCH_MODE_SINGLE_TOP = 1; + + /** + * The system creates the activity at the root of a new task or locates the activity on an + * existing task with the same affinity. If an instance of the activity already exists and is at + * the root of the task, the system routes the intent to existing instance through a call to its + * onNewIntent() method, rather than creating a new one. + */ + public static final int LAUNCH_MODE_SINGLE_INSTANCE = 2; + + /** + * Same as "singleTask", except that the system doesn't launch any other activities into the + * task holding the instance. The activity is always the single and only member of its task. + */ + public static final int LAUNCH_MODE_SINGLE_TASK = 3; + + /** + * The activity can only be running as the root activity of the task, the first activity that + * created the task, and therefore there will only be one instance of this activity in a task; + * but activity can be instantiated multiple times in different tasks. + */ + public static final int LAUNCH_MODE_SINGLE_INSTANCE_PER_TASK = 4; + + /** Clock monotonic timestamp of launch started. */ + public static final int START_TIMESTAMP_LAUNCH = 0; + + /** Clock monotonic timestamp of finish java classloading. */ + public static final int START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE = 1; + + /** Clock monotonic timestamp of Application onCreate called. */ + public static final int START_TIMESTAMP_APPLICATION_ONCREATE = 2; + + /** Clock monotonic timestamp of bindApplication called. */ + public static final int START_TIMESTAMP_BIND_APPLICATION = 3; + + /** Clock monotonic timestamp of first frame drawn. */ + public static final int START_TIMESTAMP_FIRST_FRAME = 4; + + /** Clock monotonic timestamp of reportFullyDrawn called by application. */ + public static final int START_TIMESTAMP_FULLY_DRAWN = 5; + + /** + * @see #getStartupState + */ + private @StartupState int mStartupState; + + /** + * @see #getPid + */ + private int mPid; + + /** + * @see #getRealUid + */ + private int mRealUid; + + /** + * @see #getPackageUid + */ + private int mPackageUid; + + /** + * @see #getDefiningUid + */ + private int mDefiningUid; + + /** + * @see #getProcessName + */ + private String mProcessName; + + /** + * @see #getReason + */ + private @StartReason int mReason; + + /** + * @see #getStartupTimestamps + */ + private Map<@StartupTimestamp Integer, Long> mStartupTimestampsNs; + + /** + * @see #getStartType + */ + private @StartType int mStartType; + + /** + * @see #getStartIntent + */ + private Intent mStartIntent; + + /** + * @see #getLaunchMode + */ + private @LaunchMode int mLaunchMode; + + /** + * @hide * + */ + @IntDef( + prefix = {"STARTUP_STATE_"}, + value = { + STARTUP_STATE_STARTED, + STARTUP_STATE_ERROR, + STARTUP_STATE_FIRST_FRAME_DRAWN, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StartupState {} + + /** + * @hide * + */ + @IntDef( + prefix = {"START_REASON_"}, + value = { + START_REASON_ALARM, + START_REASON_BACKUP, + START_REASON_BOOT_COMPLETE, + START_REASON_BROADCAST, + START_REASON_CONTENT_PROVIDER, + START_REASON_JOB, + START_REASON_LAUNCHER, + START_REASON_OTHER, + START_REASON_PUSH, + START_REASON_RESUMED_ACTIVITY, + START_REASON_SERVICE, + START_REASON_START_ACTIVITY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StartReason {} + + /** + * @hide * + */ + @IntDef( + prefix = {"START_TYPE_"}, + value = { + START_TYPE_COLD, + START_TYPE_WARM, + START_TYPE_HOT, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StartType {} + + /** + * @hide * + */ + @IntDef( + prefix = {"LAUNCH_MODE_"}, + value = { + LAUNCH_MODE_STANDARD, + LAUNCH_MODE_SINGLE_TOP, + LAUNCH_MODE_SINGLE_INSTANCE, + LAUNCH_MODE_SINGLE_TASK, + LAUNCH_MODE_SINGLE_INSTANCE_PER_TASK, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface LaunchMode {} + + /** + * @hide * + */ + @IntDef( + prefix = {"START_TIMESTAMP_"}, + value = { + START_TIMESTAMP_LAUNCH, + START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE, + START_TIMESTAMP_APPLICATION_ONCREATE, + START_TIMESTAMP_BIND_APPLICATION, + START_TIMESTAMP_FULLY_DRAWN, + }) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) + public @interface StartupTimestamp {} + + /** + * @see #getStartupState + * @hide + */ + public void setStartupState(final @StartupState int startupState) { + mStartupState = startupState; + } + + /** + * @see #getPid + * @hide + */ + public void setPid(final int pid) { + mPid = pid; + } + + /** + * @see #getRealUid + * @hide + */ + public void setRealUid(final int uid) { + mRealUid = uid; + } + + /** + * @see #getPackageUid + * @hide + */ + public void setPackageUid(final int uid) { + mPackageUid = uid; + } + + /** + * @see #getDefiningUid + * @hide + */ + public void setDefiningUid(final int uid) { + mDefiningUid = uid; + } + + /** + * @see #getProcessName + * @hide + */ + public void setProcessName(final String processName) { + mProcessName = intern(processName); + } + + /** + * @see #getReason + * @hide + */ + public void setReason(@StartReason int reason) { + mReason = reason; + } + + /** + * @see #getStartupTimestamps + * @hide + */ + public void addStartupTimestamp(@StartupTimestamp int key, long timestampNs) { + if (mStartupTimestampsNs == null) { + mStartupTimestampsNs = new HashMap<@StartupTimestamp Integer, Long>(); + } + mStartupTimestampsNs.put(key, timestampNs); + } + + /** + * @see #getStartType + * @hide + */ + public void setStartType(@StartType int startType) { + mStartType = startType; + } + + /** + * @see #getStartIntent + * @hide + */ + public void setIntent(Intent startIntent) { + mStartIntent = startIntent; + } + + /** + * @see #getLaunchMode + * @hide + */ + public void setLaunchMode(@LaunchMode int launchMode) { + mLaunchMode = launchMode; + } + + /** + * Current state of startup. + * + * Can be used to determine whether the object will have additional fields added as it may be + * queried before all data is collected. + * + *

Note: field will always be set and available.

+ */ + public @StartupState int getStartupState() { + return mStartupState; + } + + /** + * The process id. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public int getPid() { + return mPid; + } + + /** + * The kernel user identifier of the process, most of the time the system uses this to do access + * control checks. It's typically the uid of the package where the component is running from, + * except the case of isolated process, where this field identifies the kernel user identifier + * that this process is actually running with, while the {@link #getPackageUid} identifies the + * kernel user identifier that is assigned at the package installation time. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public int getRealUid() { + return mRealUid; + } + + /** + * Similar to {@link #getRealUid}, it's the kernel user identifier that is assigned at the + * package installation time. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public int getPackageUid() { + return mPackageUid; + } + + /** + * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and + * {@link #getPackageUid}, if an external service has the {@link + * android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set to + * true and was bound with the flag {@link android.content.Context#BIND_EXTERNAL_SERVICE} + * - in this case, this field here will be the kernel user identifier of the external service + * provider. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public int getDefiningUid() { + return mDefiningUid; + } + + /** + * The actual process name it was running with. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public @NonNull String getProcessName() { + return mProcessName; + } + + /** + * The reason code of what triggered the process's start. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public @StartReason int getReason() { + return mReason; + } + + /** + * Various clock monotonic timestamps in nanoseconds throughout the startup process. + * + *

Note: different timestamps will be available for different values of + * {@link #getStartupState}: + * + * (Subsequent rows contain all timestamps of proceding states.) + * + * For {@link #STARTUP_STATE_STARTED}, timestamp {@link #START_TIMESTAMP_LAUNCH} will be + * available. + * For {@link #STARTUP_STATE_ERROR}, no additional timestamps are guaranteed available. + * For {@link #STARTUP_STATE_FIRST_FRAME_DRAWN}, timestamps + * {@link #START_TIMESTAMP_JAVA_CLASSLOADING_COMPLETE}, {@link #START_TIMESTAMP_APPLICATION_ONCREATE}, + * {@link #START_TIMESTAMP_BIND_APPLICATION}, and {@link #START_TIMESTAMP_FIRST_FRAME} will + * additionally be available. + * + * Timestamp {@link #START_TIMESTAMP_FULLY_DRAWN} is never guaranteed to be available as it is + * dependant on devloper calling {@link Activity#reportFullyDrawn}. + *

+ */ + public @NonNull Map<@StartupTimestamp Integer, Long> getStartupTimestamps() { + if (mStartupTimestampsNs == null) { + mStartupTimestampsNs = new HashMap<@StartupTimestamp Integer, Long>(); + } + return mStartupTimestampsNs; + } + + /** + * The state of the app at startup. + * + *

Note: field will be set for {@link #getStartupState} value + * {@link #STARTUP_STATE_FIRST_FRAME_DRAWN}. Not guaranteed for other states.

+ */ + public @StartType int getStartType() { + return mStartType; + } + + /** + * The intent used to launch the application. + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + @SuppressLint("IntentBuilderName") + @Nullable + public Intent getIntent() { + return mStartIntent; + } + + /** + * An instruction on how the activity should be launched. There are five modes that work in + * conjunction with activity flags in Intent objects to determine what should happen when the + * activity is called upon to handle an intent. + * + * Modes: + * {@link #LAUNCH_MODE_STANDARD} + * {@link #LAUNCH_MODE_SINGLE_TOP} + * {@link #LAUNCH_MODE_SINGLE_INSTANCE} + * {@link #LAUNCH_MODE_SINGLE_TASK} + * {@link #LAUNCH_MODE_SINGLE_INSTANCE_PER_TASK} + * + *

Note: field will be set for any {@link #getStartupState} value.

+ */ + public @LaunchMode int getLaunchMode() { + return mLaunchMode; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(mStartupState); + dest.writeInt(mPid); + dest.writeInt(mRealUid); + dest.writeInt(mPackageUid); + dest.writeInt(mDefiningUid); + dest.writeString(mProcessName); + dest.writeInt(mReason); + dest.writeInt(mStartupTimestampsNs.size()); + for (@StartupTimestamp int key : mStartupTimestampsNs.keySet()) { + dest.writeInt(key); + dest.writeLong(mStartupTimestampsNs.get(key)); + } + dest.writeInt(mStartType); + dest.writeParcelable(mStartIntent, flags); + dest.writeInt(mLaunchMode); + } + + /** @hide */ + public ApplicationStartInfo() {} + + /** @hide */ + public ApplicationStartInfo(ApplicationStartInfo other) { + mStartupState = other.mStartupState; + mPid = other.mPid; + mRealUid = other.mRealUid; + mPackageUid = other.mPackageUid; + mDefiningUid = other.mDefiningUid; + mProcessName = other.mProcessName; + mReason = other.mReason; + mStartupTimestampsNs = other.mStartupTimestampsNs; + mStartType = other.mStartType; + mStartIntent = other.mStartIntent; + mLaunchMode = other.mLaunchMode; + } + + private ApplicationStartInfo(@NonNull Parcel in) { + mStartupState = in.readInt(); + mPid = in.readInt(); + mRealUid = in.readInt(); + mPackageUid = in.readInt(); + mDefiningUid = in.readInt(); + mProcessName = intern(in.readString()); + mReason = in.readInt(); + int starupTimestampCount = in.readInt(); + for (int i = 0; i < starupTimestampCount; i++) { + int key = in.readInt(); + long val = in.readLong(); + addStartupTimestamp(key, val); + } + mStartType = in.readInt(); + mStartIntent = + in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); + mLaunchMode = in.readInt(); + } + + private static String intern(@Nullable String source) { + return source != null ? source.intern() : null; + } + + public @NonNull static final Creator CREATOR = + new Creator() { + @Override + public ApplicationStartInfo createFromParcel(Parcel in) { + return new ApplicationStartInfo(in); + } + + @Override + public ApplicationStartInfo[] newArray(int size) { + return new ApplicationStartInfo[size]; + } + }; +} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index c5c3d3090f12e..a2c4820d5fcba 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -19,10 +19,12 @@ package android.app; import android.app.ActivityManager; import android.app.ActivityManager.PendingIntentInfo; import android.app.ActivityTaskManager; +import android.app.ApplicationStartInfo; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; import android.app.ContentProviderHolder; import android.app.GrantedUriPermission; +import android.app.IApplicationStartInfoCompleteListener; import android.app.IApplicationThread; import android.app.IActivityController; import android.app.IAppTask; @@ -633,6 +635,45 @@ interface IActivityManager { */ void appNotResponding(String reason); + /** + * Return a list of {@link ApplicationStartInfo} records. + * + *

Note: System stores historical information in a ring buffer, older + * records would be overwritten by newer records.

+ * + * @param packageName Optional, an empty value means match all packages belonging to the + * caller's UID. If this package belongs to another UID, you must hold + * {@link android.Manifest.permission#DUMP} in order to retrieve it. + * @param maxNum Optional, the maximum number of results should be returned; A value of 0 + * means to ignore this parameter and return all matching records + * @param userId The userId in the multi-user environment. + * + * @return a list of {@link ApplicationStartInfo} records with the matching criteria, sorted in + * the order from most recent to least recent. + */ + ParceledListSlice getHistoricalProcessStartReasons(String packageName, + int maxNum, int userId); + + + /** + * Sets a callback for {@link ApplicationStartInfo} upon completion of collecting startup data. + * + *

Note: completion of startup is no guaranteed and as such this callback may not occur.

+ * + * @param listener A listener to for the callback upon completion of startup data collection. + * @param userId The userId in the multi-user environment. + */ + void setApplicationStartInfoCompleteListener(IApplicationStartInfoCompleteListener listener, + int userId); + + + /** + * Removes callback for {@link ApplicationStartInfo} upon completion of collecting startup data. + * + * @param userId The userId in the multi-user environment. + */ + void removeApplicationStartInfoCompleteListener(int userId); + /** * Return a list of {@link ApplicationExitInfo} records. * diff --git a/core/java/android/app/IApplicationStartInfoCompleteListener.aidl b/core/java/android/app/IApplicationStartInfoCompleteListener.aidl new file mode 100644 index 0000000000000..0f0d9198234f0 --- /dev/null +++ b/core/java/android/app/IApplicationStartInfoCompleteListener.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.app.ApplicationStartInfo; + +/** + * Notify the client when compilation of app start info is complete. + * Will not be called in case of an error that disrupts startup. + * @param applicationStartInfo the completed ApplicationStartInfo object. + * + * @hide + */ +interface IApplicationStartInfoCompleteListener { + void onApplicationStartInfoComplete(in ApplicationStartInfo applicationStartInfo); +} diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 2aeaa2f704a22..13f327c4bae08 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -260,6 +260,10 @@ final class ActivityManagerConstants extends ContentObserver { */ private static final String KEY_LOW_SWAP_THRESHOLD_PERCENT = "low_swap_threshold_percent"; + /** Default value for mFlagApplicationStartInfoEnabled. Defaults to false. */ + private static final String KEY_DEFAULT_APPLICATION_START_INFO_ENABLED = + "enable_app_start_info"; + /** * Default value for mFlagBackgroundActivityStartsEnabled if not explicitly set in * Settings.Global. This allows it to be set experimentally unless it has been @@ -550,6 +554,9 @@ final class ActivityManagerConstants extends ContentObserver { // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED volatile boolean mFlagActivityStartsLoggingEnabled; + // Indicates whether ApplicationStartInfo is enabled. + volatile boolean mFlagApplicationStartInfoEnabled; + // Indicates whether the background activity starts is enabled. // Controlled by Settings.Global.BACKGROUND_ACTIVITY_STARTS_ENABLED. // If not set explicitly the default is controlled by DeviceConfig. @@ -996,6 +1003,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_MAX_CACHED_PROCESSES: updateMaxCachedProcesses(); break; + case KEY_DEFAULT_APPLICATION_START_INFO_ENABLED: + updateApplicationStartInfoEnabled(); + break; case KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED: updateBackgroundActivityStarts(); break; @@ -1390,6 +1400,14 @@ final class ActivityManagerConstants extends ContentObserver { Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 1) == 1; } + private void updateApplicationStartInfoEnabled() { + mFlagApplicationStartInfoEnabled = + DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_DEFAULT_APPLICATION_START_INFO_ENABLED, + /*defaultValue*/ false); + } + private void updateBackgroundActivityStarts() { mFlagBackgroundActivityStartsEnabled = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1970,6 +1988,10 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(mFgToBgFgsGraceDuration); pw.print(" "); pw.print(KEY_FGS_START_FOREGROUND_TIMEOUT); pw.print("="); pw.println(mFgsStartForegroundTimeoutMs); + pw.print(" "); + pw.print(KEY_DEFAULT_APPLICATION_START_INFO_ENABLED); + pw.print("="); + pw.println(mFlagApplicationStartInfoEnabled); pw.print(" "); pw.print(KEY_DEFAULT_BACKGROUND_ACTIVITY_STARTS_ENABLED); pw.print("="); pw.println(mFlagBackgroundActivityStartsEnabled); pw.print(" "); pw.print(KEY_DEFAULT_BACKGROUND_FGS_STARTS_RESTRICTION_ENABLED); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b7985a5543cd0..ec69306d67831 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -184,6 +184,7 @@ import android.app.AppOpsManager.AttributionFlags; import android.app.AppOpsManagerInternal.CheckOpsDelegate; import android.app.ApplicationErrorReport; import android.app.ApplicationExitInfo; +import android.app.ApplicationStartInfo; import android.app.ApplicationThreadConstants; import android.app.BackgroundStartPrivileges; import android.app.BroadcastOptions; @@ -192,6 +193,7 @@ import android.app.ContentProviderHolder; import android.app.ForegroundServiceDelegationOptions; import android.app.IActivityController; import android.app.IActivityManager; +import android.app.IApplicationStartInfoCompleteListener; import android.app.IApplicationThread; import android.app.IForegroundServiceObserver; import android.app.IInstrumentationWatcher; @@ -9451,6 +9453,37 @@ public class ActivityManagerService extends IActivityManager.Stub return retList; } + @Override + public ParceledListSlice getHistoricalProcessStartReasons( + String packageName, int maxNum, int userId) { + if (!mConstants.mFlagApplicationStartInfoEnabled) { + return new ParceledListSlice( + new ArrayList()); + } + enforceNotIsolatedCaller("getHistoricalProcessStartReasons"); + + final ArrayList results = new ArrayList(); + + return new ParceledListSlice(results); + } + + @Override + public void setApplicationStartInfoCompleteListener( + IApplicationStartInfoCompleteListener listener, int userId) { + if (!mConstants.mFlagApplicationStartInfoEnabled) { + return; + } + enforceNotIsolatedCaller("setApplicationStartInfoCompleteListener"); + } + + @Override + public void removeApplicationStartInfoCompleteListener(int userId) { + if (!mConstants.mFlagApplicationStartInfoEnabled) { + return; + } + enforceNotIsolatedCaller("removeApplicationStartInfoCompleteListener"); + } + @Override public ParceledListSlice getHistoricalProcessExitReasons( String packageName, int pid, int maxNum, int userId) {