939 lines
34 KiB
Java
939 lines
34 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
}
|