| /* |
| * 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.FlaggedApi; |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SuppressLint; |
| import android.content.Intent; |
| import android.content.pm.ApplicationInfo; |
| import android.icu.text.SimpleDateFormat; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.UserHandle; |
| import android.text.TextUtils; |
| import android.util.ArrayMap; |
| import android.util.Xml; |
| import android.util.proto.ProtoInputStream; |
| import android.util.proto.ProtoOutputStream; |
| import android.util.proto.WireTypeMismatchException; |
| |
| import com.android.internal.util.XmlUtils; |
| import com.android.modules.utils.TypedXmlPullParser; |
| import com.android.modules.utils.TypedXmlSerializer; |
| |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.PrintWriter; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.Map; |
| import java.util.Objects; |
| |
| /** |
| * Describes information related to an application process's startup. |
| * |
| * <p> |
| * Many aspects concerning why and how an applications process was started are valuable for apps |
| * both for logging and for potential behavior changes. Reason for process start, start type, |
| * start times, throttling, and other useful diagnostic data can be obtained from |
| * {@link ApplicationStartInfo} records. |
| * </p> |
| */ |
| @FlaggedApi(Flags.FLAG_APP_START_INFO) |
| 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 from launcher recents. */ |
| public static final int START_REASON_LAUNCHER_RECENTS = 7; |
| |
| /** Process started not for any of the listed reasons. */ |
| public static final int START_REASON_OTHER = 8; |
| |
| /** Process started due to push message. */ |
| public static final int START_REASON_PUSH = 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; |
| |
| /** Start type not yet set. */ |
| public static final int START_TYPE_UNSET = 0; |
| |
| /** Process started from scratch. */ |
| public static final int START_TYPE_COLD = 1; |
| |
| /** Process retained minimally SavedInstanceState. */ |
| public static final int START_TYPE_WARM = 2; |
| |
| /** Process brought back to foreground. */ |
| public static final int START_TYPE_HOT = 3; |
| |
| /** |
| * 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; |
| |
| /** The end of the range, beginning with 0, reserved for system timestamps.*/ |
| public static final int START_TIMESTAMP_RESERVED_RANGE_SYSTEM = 20; |
| |
| /** The beginning of the range reserved for developer supplied timestamps.*/ |
| public static final int START_TIMESTAMP_RESERVED_RANGE_DEVELOPER_START = |
| START_TIMESTAMP_RESERVED_RANGE_SYSTEM + 1; |
| |
| /** The end of the range reserved for developer supplied timestamps.*/ |
| public static final int START_TIMESTAMP_RESERVED_RANGE_DEVELOPER = 30; |
| |
| /** Clock monotonic timestamp of launch started. */ |
| public static final int START_TIMESTAMP_LAUNCH = 0; |
| |
| /** Clock monotonic timestamp of process fork. */ |
| public static final int START_TIMESTAMP_FORK = 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; |
| |
| /** Clock monotonic timestamp of initial renderthread frame. */ |
| public static final int START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME = 6; |
| |
| /** Clock monotonic timestamp of surfaceflinger composition complete. */ |
| public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7; |
| |
| /** |
| * @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 #getPackageName |
| */ |
| private String mPackageName; |
| |
| /** |
| * @see #getProcessName |
| */ |
| private String mProcessName; |
| |
| /** |
| * @see #getReason |
| */ |
| private @StartReason int mReason; |
| |
| /** |
| * @see #getStartupTimestamps |
| */ |
| private ArrayMap<Integer, Long> mStartupTimestampsNs; |
| |
| /** |
| * @see #getStartType |
| */ |
| private @StartType int mStartType; |
| |
| /** |
| * @see #getIntent |
| */ |
| private Intent mStartIntent; |
| |
| /** |
| * @see #getLaunchMode |
| */ |
| private @LaunchMode int mLaunchMode; |
| |
| /** |
| * @see #wasForceStopped() |
| */ |
| private boolean mWasForceStopped; |
| |
| /** |
| * @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_LAUNCHER_RECENTS, |
| START_REASON_OTHER, |
| START_REASON_PUSH, |
| START_REASON_SERVICE, |
| START_REASON_START_ACTIVITY, |
| }) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface StartReason {} |
| |
| /** |
| * @hide * |
| */ |
| @IntDef( |
| prefix = {"START_TYPE_"}, |
| value = { |
| START_TYPE_UNSET, |
| 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 {} |
| |
| /** |
| * @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 #getPackageName |
| * @hide |
| */ |
| public void setPackageName(final String packageName) { |
| mPackageName = intern(packageName); |
| } |
| |
| /** |
| * @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(int key, long timestampNs) { |
| if (key < 0 || key > START_TIMESTAMP_RESERVED_RANGE_DEVELOPER) { |
| return; |
| } |
| if (mStartupTimestampsNs == null) { |
| mStartupTimestampsNs = new ArrayMap<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) { |
| if (startIntent != null) { |
| mStartIntent = startIntent.maybeStripForHistory(); |
| } |
| } |
| |
| /** |
| * @see #getLaunchMode |
| * @hide |
| */ |
| public void setLaunchMode(@LaunchMode int launchMode) { |
| mLaunchMode = launchMode; |
| } |
| |
| /** |
| * @see #wasForceStopped() |
| * @param wasForceStopped whether the app had been force-stopped in the past |
| * @hide |
| */ |
| public void setForceStopped(boolean wasForceStopped) { |
| mWasForceStopped = wasForceStopped; |
| } |
| |
| /** |
| * 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. |
| * |
| * <p class="note"> Note: field will always be set and available.</p> |
| */ |
| public @StartupState int getStartupState() { |
| return mStartupState; |
| } |
| |
| /** |
| * The process id. |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| 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. |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| public int getRealUid() { |
| return mRealUid; |
| } |
| |
| /** |
| * Similar to {@link #getRealUid}, it's the kernel user identifier that is assigned at the |
| * package installation time. |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| 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 <code> |
| * true</code> 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. |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| public int getDefiningUid() { |
| return mDefiningUid; |
| } |
| |
| /** |
| * Name of first package running in this process; |
| * |
| * @hide |
| */ |
| public String getPackageName() { |
| return mPackageName; |
| } |
| |
| /** |
| * The actual process name it was running with. |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| public @NonNull String getProcessName() { |
| return mProcessName; |
| } |
| |
| /** |
| * The reason code of what triggered the process's start. |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| public @StartReason int getReason() { |
| return mReason; |
| } |
| |
| /** |
| * Various clock monotonic timestamps in nanoseconds throughout the startup process. |
| * |
| * <p class="note"> 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_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}. |
| * </p> |
| */ |
| public @NonNull Map<Integer, Long> getStartupTimestamps() { |
| if (mStartupTimestampsNs == null) { |
| mStartupTimestampsNs = new ArrayMap<Integer, Long>(); |
| } |
| return mStartupTimestampsNs; |
| } |
| |
| /** |
| * The state of the app at startup. |
| * |
| * <p class="note"> Note: field will be set for {@link #getStartupState} value |
| * {@link #STARTUP_STATE_FIRST_FRAME_DRAWN}. Not guaranteed for other states.</p> |
| */ |
| public @StartType int getStartType() { |
| return mStartType; |
| } |
| |
| /** |
| * The intent used to launch the application. |
| * |
| * <p class="note"> Note: Intent is stripped and does not include extras.</p> |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| @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} |
| * |
| * <p class="note"> Note: field will be set for any {@link #getStartupState} value.</p> |
| */ |
| public @LaunchMode int getLaunchMode() { |
| return mLaunchMode; |
| } |
| |
| /** |
| * Informs whether this is the first process launch for an app since it was |
| * {@link ApplicationInfo#FLAG_STOPPED force-stopped} for some reason. |
| * This allows the app to know if it should re-register for any alarms, jobs and other callbacks |
| * that were cleared when the app was force-stopped. |
| * |
| * @return {@code true} if this is the first process launch of the app after having been |
| * stopped, {@code false} otherwise. |
| */ |
| @FlaggedApi(android.content.pm.Flags.FLAG_STAY_STOPPED) |
| public boolean wasForceStopped() { |
| return mWasForceStopped; |
| } |
| |
| @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(mPackageName); |
| dest.writeString(mProcessName); |
| dest.writeInt(mReason); |
| dest.writeInt(mStartupTimestampsNs == null ? 0 : mStartupTimestampsNs.size()); |
| if (mStartupTimestampsNs != null) { |
| for (int i = 0; i < mStartupTimestampsNs.size(); i++) { |
| dest.writeInt(mStartupTimestampsNs.keyAt(i)); |
| dest.writeLong(mStartupTimestampsNs.valueAt(i)); |
| } |
| } |
| dest.writeInt(mStartType); |
| dest.writeParcelable(mStartIntent, flags); |
| dest.writeInt(mLaunchMode); |
| dest.writeBoolean(mWasForceStopped); |
| } |
| |
| /** @hide */ |
| public ApplicationStartInfo() {} |
| |
| /** @hide */ |
| public ApplicationStartInfo(ApplicationStartInfo other) { |
| mStartupState = other.mStartupState; |
| mPid = other.mPid; |
| mRealUid = other.mRealUid; |
| mPackageUid = other.mPackageUid; |
| mDefiningUid = other.mDefiningUid; |
| mPackageName = other.mPackageName; |
| mProcessName = other.mProcessName; |
| mReason = other.mReason; |
| mStartupTimestampsNs = other.mStartupTimestampsNs; |
| mStartType = other.mStartType; |
| mStartIntent = other.mStartIntent; |
| mLaunchMode = other.mLaunchMode; |
| mWasForceStopped = other.mWasForceStopped; |
| } |
| |
| private ApplicationStartInfo(@NonNull Parcel in) { |
| mStartupState = in.readInt(); |
| mPid = in.readInt(); |
| mRealUid = in.readInt(); |
| mPackageUid = in.readInt(); |
| mDefiningUid = in.readInt(); |
| mPackageName = intern(in.readString()); |
| 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(); |
| mWasForceStopped = in.readBoolean(); |
| } |
| |
| private static String intern(@Nullable String source) { |
| return source != null ? source.intern() : null; |
| } |
| |
| public @NonNull static final Creator<ApplicationStartInfo> CREATOR = |
| new Creator<ApplicationStartInfo>() { |
| @Override |
| public ApplicationStartInfo createFromParcel(Parcel in) { |
| return new ApplicationStartInfo(in); |
| } |
| |
| @Override |
| public ApplicationStartInfo[] newArray(int size) { |
| return new ApplicationStartInfo[size]; |
| } |
| }; |
| |
| private static final String PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS = "timestamps"; |
| private static final String PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP = "timestamp"; |
| private static final String PROTO_SERIALIZER_ATTRIBUTE_KEY = "key"; |
| private static final String PROTO_SERIALIZER_ATTRIBUTE_TS = "ts"; |
| private static final String PROTO_SERIALIZER_ATTRIBUTE_INTENT = "intent"; |
| |
| /** |
| * Write to a protocol buffer output stream. Protocol buffer message definition at {@link |
| * android.app.ApplicationStartInfoProto} |
| * |
| * @param proto Stream to write the ApplicationStartInfo object to. |
| * @param fieldId Field Id of the ApplicationStartInfo as defined in the parent message |
| * @hide |
| */ |
| public void writeToProto(ProtoOutputStream proto, long fieldId) throws IOException { |
| final long token = proto.start(fieldId); |
| proto.write(ApplicationStartInfoProto.PID, mPid); |
| proto.write(ApplicationStartInfoProto.REAL_UID, mRealUid); |
| proto.write(ApplicationStartInfoProto.PACKAGE_UID, mPackageUid); |
| proto.write(ApplicationStartInfoProto.DEFINING_UID, mDefiningUid); |
| proto.write(ApplicationStartInfoProto.PROCESS_NAME, mProcessName); |
| proto.write(ApplicationStartInfoProto.STARTUP_STATE, mStartupState); |
| proto.write(ApplicationStartInfoProto.REASON, mReason); |
| if (mStartupTimestampsNs != null && mStartupTimestampsNs.size() > 0) { |
| ByteArrayOutputStream timestampsBytes = new ByteArrayOutputStream(); |
| ObjectOutputStream timestampsOut = new ObjectOutputStream(timestampsBytes); |
| TypedXmlSerializer serializer = Xml.resolveSerializer(timestampsOut); |
| serializer.startDocument(null, true); |
| serializer.startTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS); |
| for (int i = 0; i < mStartupTimestampsNs.size(); i++) { |
| serializer.startTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP); |
| serializer.attributeInt(null, PROTO_SERIALIZER_ATTRIBUTE_KEY, |
| mStartupTimestampsNs.keyAt(i)); |
| serializer.attributeLong(null, PROTO_SERIALIZER_ATTRIBUTE_TS, |
| mStartupTimestampsNs.valueAt(i)); |
| serializer.endTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP); |
| } |
| serializer.endTag(null, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS); |
| serializer.endDocument(); |
| proto.write(ApplicationStartInfoProto.STARTUP_TIMESTAMPS, |
| timestampsBytes.toByteArray()); |
| timestampsOut.close(); |
| } |
| proto.write(ApplicationStartInfoProto.START_TYPE, mStartType); |
| if (mStartIntent != null) { |
| ByteArrayOutputStream intentBytes = new ByteArrayOutputStream(); |
| ObjectOutputStream intentOut = new ObjectOutputStream(intentBytes); |
| TypedXmlSerializer serializer = Xml.resolveSerializer(intentOut); |
| serializer.startDocument(null, true); |
| serializer.startTag(null, PROTO_SERIALIZER_ATTRIBUTE_INTENT); |
| mStartIntent.saveToXml(serializer); |
| serializer.endTag(null, PROTO_SERIALIZER_ATTRIBUTE_INTENT); |
| serializer.endDocument(); |
| proto.write(ApplicationStartInfoProto.START_INTENT, |
| intentBytes.toByteArray()); |
| intentOut.close(); |
| } |
| proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode); |
| proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped); |
| proto.end(token); |
| } |
| |
| /** |
| * Read from a protocol buffer input stream. Protocol buffer message definition at {@link |
| * android.app.ApplicationStartInfoProto} |
| * |
| * @param proto Stream to read the ApplicationStartInfo object from. |
| * @param fieldId Field Id of the ApplicationStartInfo as defined in the parent message |
| * @hide |
| */ |
| public void readFromProto(ProtoInputStream proto, long fieldId) |
| throws IOException, WireTypeMismatchException, ClassNotFoundException { |
| final long token = proto.start(fieldId); |
| while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { |
| switch (proto.getFieldNumber()) { |
| case (int) ApplicationStartInfoProto.PID: |
| mPid = proto.readInt(ApplicationStartInfoProto.PID); |
| break; |
| case (int) ApplicationStartInfoProto.REAL_UID: |
| mRealUid = proto.readInt(ApplicationStartInfoProto.REAL_UID); |
| break; |
| case (int) ApplicationStartInfoProto.PACKAGE_UID: |
| mPackageUid = proto.readInt(ApplicationStartInfoProto.PACKAGE_UID); |
| break; |
| case (int) ApplicationStartInfoProto.DEFINING_UID: |
| mDefiningUid = proto.readInt(ApplicationStartInfoProto.DEFINING_UID); |
| break; |
| case (int) ApplicationStartInfoProto.PROCESS_NAME: |
| mProcessName = intern(proto.readString(ApplicationStartInfoProto.PROCESS_NAME)); |
| break; |
| case (int) ApplicationStartInfoProto.STARTUP_STATE: |
| mStartupState = proto.readInt(ApplicationStartInfoProto.STARTUP_STATE); |
| break; |
| case (int) ApplicationStartInfoProto.REASON: |
| mReason = proto.readInt(ApplicationStartInfoProto.REASON); |
| break; |
| case (int) ApplicationStartInfoProto.STARTUP_TIMESTAMPS: |
| ByteArrayInputStream timestampsBytes = new ByteArrayInputStream(proto.readBytes( |
| ApplicationStartInfoProto.STARTUP_TIMESTAMPS)); |
| ObjectInputStream timestampsIn = new ObjectInputStream(timestampsBytes); |
| mStartupTimestampsNs = new ArrayMap<Integer, Long>(); |
| try { |
| TypedXmlPullParser parser = Xml.resolvePullParser(timestampsIn); |
| XmlUtils.beginDocument(parser, PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMPS); |
| int depth = parser.getDepth(); |
| while (XmlUtils.nextElementWithin(parser, depth)) { |
| if (PROTO_SERIALIZER_ATTRIBUTE_TIMESTAMP.equals(parser.getName())) { |
| int key = parser.getAttributeInt(null, |
| PROTO_SERIALIZER_ATTRIBUTE_KEY); |
| long ts = parser.getAttributeLong(null, |
| PROTO_SERIALIZER_ATTRIBUTE_TS); |
| mStartupTimestampsNs.put(key, ts); |
| } |
| } |
| } catch (XmlPullParserException e) { |
| // Timestamps lost |
| } |
| timestampsIn.close(); |
| break; |
| case (int) ApplicationStartInfoProto.START_TYPE: |
| mStartType = proto.readInt(ApplicationStartInfoProto.START_TYPE); |
| break; |
| case (int) ApplicationStartInfoProto.START_INTENT: |
| ByteArrayInputStream intentBytes = new ByteArrayInputStream(proto.readBytes( |
| ApplicationStartInfoProto.START_INTENT)); |
| ObjectInputStream intentIn = new ObjectInputStream(intentBytes); |
| try { |
| TypedXmlPullParser parser = Xml.resolvePullParser(intentIn); |
| XmlUtils.beginDocument(parser, PROTO_SERIALIZER_ATTRIBUTE_INTENT); |
| mStartIntent = Intent.restoreFromXml(parser); |
| } catch (XmlPullParserException e) { |
| // Intent lost |
| } |
| intentIn.close(); |
| break; |
| case (int) ApplicationStartInfoProto.LAUNCH_MODE: |
| mLaunchMode = proto.readInt(ApplicationStartInfoProto.LAUNCH_MODE); |
| break; |
| case (int) ApplicationStartInfoProto.WAS_FORCE_STOPPED: |
| mWasForceStopped = proto.readBoolean( |
| ApplicationStartInfoProto.WAS_FORCE_STOPPED); |
| break; |
| } |
| } |
| proto.end(token); |
| } |
| |
| /** @hide */ |
| public void dump(@NonNull PrintWriter pw, @Nullable String prefix, @Nullable String seqSuffix, |
| @NonNull SimpleDateFormat sdf) { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(prefix) |
| .append("ApplicationStartInfo ").append(seqSuffix).append(':') |
| .append('\n') |
| .append(" pid=").append(mPid) |
| .append(" realUid=").append(mRealUid) |
| .append(" packageUid=").append(mPackageUid) |
| .append(" definingUid=").append(mDefiningUid) |
| .append(" user=").append(UserHandle.getUserId(mPackageUid)) |
| .append('\n') |
| .append(" package=").append(mPackageName) |
| .append(" process=").append(mProcessName) |
| .append(" startupState=").append(mStartupState) |
| .append(" reason=").append(reasonToString(mReason)) |
| .append(" startType=").append(startTypeToString(mStartType)) |
| .append(" launchMode=").append(mLaunchMode) |
| .append(" wasForceStopped=").append(mWasForceStopped) |
| .append('\n'); |
| if (mStartIntent != null) { |
| sb.append(" intent=").append(mStartIntent.toString()) |
| .append('\n'); |
| } |
| if (mStartupTimestampsNs != null && mStartupTimestampsNs.size() > 0) { |
| sb.append(" timestamps: "); |
| for (int i = 0; i < mStartupTimestampsNs.size(); i++) { |
| sb.append(mStartupTimestampsNs.keyAt(i)).append("=").append(mStartupTimestampsNs |
| .valueAt(i)).append(" "); |
| } |
| sb.append('\n'); |
| } |
| pw.print(sb.toString()); |
| } |
| |
| private static String reasonToString(@StartReason int reason) { |
| return switch (reason) { |
| case START_REASON_ALARM -> "ALARM"; |
| case START_REASON_BACKUP -> "BACKUP"; |
| case START_REASON_BOOT_COMPLETE -> "BOOT COMPLETE"; |
| case START_REASON_BROADCAST -> "BROADCAST"; |
| case START_REASON_CONTENT_PROVIDER -> "CONTENT PROVIDER"; |
| case START_REASON_JOB -> "JOB"; |
| case START_REASON_LAUNCHER -> "LAUNCHER"; |
| case START_REASON_LAUNCHER_RECENTS -> "LAUNCHER RECENTS"; |
| case START_REASON_OTHER -> "OTHER"; |
| case START_REASON_PUSH -> "PUSH"; |
| case START_REASON_SERVICE -> "SERVICE"; |
| case START_REASON_START_ACTIVITY -> "START ACTIVITY"; |
| default -> ""; |
| }; |
| } |
| |
| private static String startTypeToString(@StartType int startType) { |
| return switch (startType) { |
| case START_TYPE_UNSET -> "UNSET"; |
| case START_TYPE_COLD -> "COLD"; |
| case START_TYPE_WARM -> "WARM"; |
| case START_TYPE_HOT -> "HOT"; |
| default -> ""; |
| }; |
| } |
| |
| /** @hide */ |
| @Override |
| public boolean equals(@Nullable Object other) { |
| if (other == null || !(other instanceof ApplicationStartInfo)) { |
| return false; |
| } |
| final ApplicationStartInfo o = (ApplicationStartInfo) other; |
| return mPid == o.mPid && mRealUid == o.mRealUid && mPackageUid == o.mPackageUid |
| && mDefiningUid == o.mDefiningUid && mReason == o.mReason |
| && mStartupState == o.mStartupState && mStartType == o.mStartType |
| && mLaunchMode == o.mLaunchMode && TextUtils.equals(mProcessName, o.mProcessName) |
| && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState, |
| mStartType, mLaunchMode, mProcessName, |
| mStartupTimestampsNs); |
| } |
| |
| private boolean timestampsEquals(@NonNull ApplicationStartInfo other) { |
| if (mStartupTimestampsNs == null && other.mStartupTimestampsNs == null) { |
| return true; |
| } |
| if (mStartupTimestampsNs == null || other.mStartupTimestampsNs == null) { |
| return false; |
| } |
| return mStartupTimestampsNs.equals(other.mStartupTimestampsNs); |
| } |
| } |