Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c58b6e84 authored by Yisroel Forta's avatar Yisroel Forta
Browse files

AppStartInfo Unit tests

Add unit tests covering AppStartInfoTracker

Test: build/flash/ atest ApplicationStartInfoTest
Bug: 287152936
Change-Id: I80e2abf2273008489abd5bcc5405bc5bcc4f2100
parent e90f0d5a
Loading
Loading
Loading
Loading
+103 −2
Original line number Diff line number Diff line
@@ -26,11 +26,19 @@ 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;
@@ -40,6 +48,7 @@ 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.
@@ -214,6 +223,11 @@ public final class ApplicationStartInfo implements Parcelable {
     */
    private int mDefiningUid;

    /**
     * @see #getPackageName
     */
    private String mPackageName;

    /**
     * @see #getProcessName
     */
@@ -348,6 +362,14 @@ public final class ApplicationStartInfo implements Parcelable {
        mDefiningUid = uid;
    }

    /**
     * @see #getPackageName
     * @hide
     */
    public void setPackageName(final String packageName) {
        mPackageName = intern(packageName);
    }

    /**
     * @see #getProcessName
     * @hide
@@ -460,6 +482,15 @@ public final class ApplicationStartInfo implements Parcelable {
        return mDefiningUid;
    }

    /**
     * Name of first package running in this process;
     *
     * @hide
     */
    public String getPackageName() {
        return mPackageName;
    }

    /**
     * The actual process name it was running with.
     *
@@ -555,6 +586,7 @@ public final class ApplicationStartInfo implements Parcelable {
        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());
@@ -579,6 +611,7 @@ public final class ApplicationStartInfo implements Parcelable {
        mRealUid = other.mRealUid;
        mPackageUid = other.mPackageUid;
        mDefiningUid = other.mDefiningUid;
        mPackageName = other.mPackageName;
        mProcessName = other.mProcessName;
        mReason = other.mReason;
        mStartupTimestampsNs = other.mStartupTimestampsNs;
@@ -593,6 +626,7 @@ public final class ApplicationStartInfo implements Parcelable {
        mRealUid = in.readInt();
        mPackageUid = in.readInt();
        mDefiningUid = in.readInt();
        mPackageName = intern(in.readString());
        mProcessName = intern(in.readString());
        mReason = in.readInt();
        int starupTimestampCount = in.readInt();
@@ -624,6 +658,11 @@ public final class ApplicationStartInfo implements Parcelable {
                }
            };

    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";

    /**
     * Write to a protocol buffer output stream. Protocol buffer message definition at {@link
     * android.app.ApplicationStartInfoProto}
@@ -644,9 +683,22 @@ public final class ApplicationStartInfo implements Parcelable {
        if (mStartupTimestampsNs != null && mStartupTimestampsNs.size() > 0) {
            ByteArrayOutputStream timestampsBytes = new ByteArrayOutputStream();
            ObjectOutputStream timestampsOut = new ObjectOutputStream(timestampsBytes);
            timestampsOut.writeObject(mStartupTimestampsNs);
            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) {
@@ -697,7 +749,24 @@ public final class ApplicationStartInfo implements Parcelable {
                    ByteArrayInputStream timestampsBytes = new ByteArrayInputStream(proto.readBytes(
                            ApplicationStartInfoProto.STARTUP_TIMESTAMPS));
                    ObjectInputStream timestampsIn = new ObjectInputStream(timestampsBytes);
                    mStartupTimestampsNs = (ArrayMap<Integer, Long>) timestampsIn.readObject();
                    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);
@@ -734,6 +803,7 @@ public final class ApplicationStartInfo implements Parcelable {
                .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))
@@ -782,4 +852,35 @@ public final class ApplicationStartInfo implements Parcelable {
            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);
    }

    @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);
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -5400,13 +5400,13 @@ public final class ActiveServices {
                return msg;
            }
            mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r,
                    hostingRecord, true);
                    true);
            if (isolated) {
                r.isolationHostProc = app;
            }
        } else {
            mAm.mProcessList.getAppStartInfoTracker().handleProcessServiceStart(startTimeNs, app, r,
                    hostingRecord, false);
                    false);
        }

        if (r.fgRequired) {
+27 −15
Original line number Diff line number Diff line
@@ -81,18 +81,18 @@ public final class AppStartInfoTracker {
    private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
    private static final int FOREACH_ACTION_STOP_ITERATION = 2;

    private static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
    @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;

    @VisibleForTesting static final String APP_START_STORE_DIR = "procstartstore";

    @VisibleForTesting static final String APP_START_INFO_FILE = "procstartinfo";

    private final Object mLock = new Object();
    @VisibleForTesting final Object mLock = new Object();

    private boolean mEnabled = false;
    @VisibleForTesting boolean mEnabled = false;

    /** Initialized in {@link #init} and read-only after that. */
    private ActivityManagerService mService;
    @VisibleForTesting ActivityManagerService mService;

    /** Initialized in {@link #init} and read-only after that. */
    private Handler mHandler;
@@ -112,7 +112,7 @@ public final class AppStartInfoTracker {
     *
     * <p>Initialized in {@link #init} and read-only after that. No lock is needed.
     */
    private int mAppStartInfoHistoryListSize;
    @VisibleForTesting int mAppStartInfoHistoryListSize;

    @GuardedBy("mLock")
    private final ProcessMap<AppStartInfoContainer> mData;
@@ -146,7 +146,8 @@ public final class AppStartInfoTracker {
     * Key is timestamp of launch from {@link #ActivityMetricsLaunchObserver}.
     */
    @GuardedBy("mLock")
    private ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();
    @VisibleForTesting
    final ArrayMap<Long, ApplicationStartInfo> mInProgRecords = new ArrayMap<>();

    AppStartInfoTracker() {
        mCallbacks = new SparseArray<>();
@@ -229,7 +230,7 @@ public final class AppStartInfoTracker {
                ApplicationStartInfo info = mInProgRecords.get(id);
                info.setStartType((int) temperature);
                addBaseFieldsFromProcessRecord(info, app);
                addStartInfoLocked(info);
                mInProgRecords.put(id, addStartInfoLocked(info));
            } else {
                mInProgRecords.remove(id);
            }
@@ -262,6 +263,7 @@ public final class AppStartInfoTracker {
            ApplicationStartInfo info = mInProgRecords.get(id);
            info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN);
            info.setLaunchMode(launchMode);
            checkCompletenessAndCallback(info);
        }
    }

@@ -281,7 +283,7 @@ public final class AppStartInfoTracker {
    }

    public void handleProcessServiceStart(long startTimeNs, ProcessRecord app,
                ServiceRecord serviceRecord, HostingRecord hostingRecord, boolean cold) {
                ServiceRecord serviceRecord, boolean cold) {
        synchronized (mLock) {
            if (!mEnabled) {
                return;
@@ -297,7 +299,9 @@ public final class AppStartInfoTracker {
                    && serviceRecord.permission.contains("android.permission.BIND_JOB_SERVICE")
                    ? ApplicationStartInfo.START_REASON_JOB
                    : ApplicationStartInfo.START_REASON_SERVICE);
            if (serviceRecord.intent != null) {
                start.setIntent(serviceRecord.intent.getIntent());
            }
            addStartInfoLocked(start);
        }
    }
@@ -378,6 +382,7 @@ public final class AppStartInfoTracker {
        start.setPackageUid(app.info.uid);
        start.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid);
        start.setProcessName(app.processName);
        start.setPackageName(app.info.packageName);
    }

    void reportApplicationOnCreateTimeNanos(ProcessRecord app, long timeNs) {
@@ -419,12 +424,12 @@ public final class AppStartInfoTracker {
    }

    private void addTimestampToStart(ProcessRecord app, long timeNs, int key) {
        addTimestampToStart(app.processName, app.uid, timeNs, key);
        addTimestampToStart(app.info.packageName, app.uid, timeNs, key);
    }

    private void addTimestampToStart(String processName, int uid, long timeNs, int key) {
    private void addTimestampToStart(String packageName, int uid, long timeNs, int key) {
        synchronized (mLock) {
            AppStartInfoContainer container = mData.get(processName, uid);
            AppStartInfoContainer container = mData.get(packageName, uid);
            if (container == null) {
                // Record was not created, discard new data.
                return;
@@ -443,11 +448,11 @@ public final class AppStartInfoTracker {

        final ApplicationStartInfo info = new ApplicationStartInfo(raw);

        AppStartInfoContainer container = mData.get(raw.getProcessName(), raw.getRealUid());
        AppStartInfoContainer container = mData.get(raw.getPackageName(), raw.getRealUid());
        if (container == null) {
            container = new AppStartInfoContainer(mAppStartInfoHistoryListSize);
            container.mUid = info.getRealUid();
            mData.put(raw.getProcessName(), raw.getRealUid(), container);
            mData.put(raw.getPackageName(), raw.getRealUid(), container);
        }
        container.addStartInfoLocked(info);

@@ -486,6 +491,9 @@ public final class AppStartInfoTracker {
        if (!mEnabled) {
            return;
        }
        if (maxNum == 0) {
            maxNum = APP_START_INFO_HISTORY_LIST_SIZE;
        }
        final long identity = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
@@ -891,6 +899,7 @@ public final class AppStartInfoTracker {
                mProcStartInfoFile.delete();
            }
            mData.getMap().clear();
            mInProgRecords.clear();
        }
    }

@@ -960,6 +969,10 @@ public final class AppStartInfoTracker {

    /** Convenience method to obtain timestamp of beginning of start.*/
    private static long getStartTimestamp(ApplicationStartInfo startInfo) {
        if (startInfo.getStartupTimestamps() == null
                    || !startInfo.getStartupTimestamps().containsKey(START_TIMESTAMP_LAUNCH)) {
            return -1;
        }
        return startInfo.getStartupTimestamps().get(START_TIMESTAMP_LAUNCH);
    }

@@ -997,7 +1010,6 @@ public final class AppStartInfoTracker {
                if (oldestIndex >= 0) {
                    mInfos.remove(oldestIndex);
                }
                mInfos.remove(0);
            }
            mInfos.add(info);
            Collections.sort(mInfos, (a, b) ->
+587 −0

File added.

Preview size limit exceeded, changes collapsed.