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

Commit 5ed56aac authored by Yisroel Forta's avatar Yisroel Forta
Browse files

Cleanup startinfo old records

Modify persist records to also remove any records older than 14 days.

Test: run new test
Bug: 384539178
Flag: android.app.app_start_info_cleanup_old_records
Change-Id: Iea2d534200000c4ab3b4000fb7133fad5a7a97a4
parent 3590e925
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -170,3 +170,13 @@ flag {
    description: "Holdback study for jank_perceptible_narrow"
    bug: "304837972"
}

flag {
     namespace: "system_performance"
     name: "app_start_info_cleanup_old_records"
     description: "Cleanup old records to reduce size of in memory store."
     bug: "384539178"
     metadata {
         purpose: PURPOSE_BUGFIX
     }
}
+40 −13
Original line number Diff line number Diff line
@@ -98,6 +98,9 @@ public final class AppStartInfoTracker {

    @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;

    @VisibleForTesting
    static final long APP_START_INFO_HISTORY_LENGTH_MS = TimeUnit.DAYS.toMillis(14);

    /**
     * The max number of records that can be present in {@link mInProgressRecords}.
     *
@@ -120,9 +123,13 @@ public final class AppStartInfoTracker {
     * Monotonic clock which does not reset on reboot.
     *
     * Time for offset is persisted along with records, see {@link #persistProcessStartInfo}.
     * This does not follow the recommendation of {@link MonotonicClock} to persist on shutdown as
     * it's ok in this case to lose any time change past the last persist as records added since
     * then will be lost as well and the purpose of this clock is to keep records in order.
     * This does not currently follow the recommendation of {@link MonotonicClock} to persist on
     * shutdown as it's ok in this case to lose any time change past the last persist as records
     * added since then will be lost as well. Since this time is used for cleanup as well, the
     * potential old offset may result in the cleanup window being extended slightly beyond the
     * targeted 14 days.
     *
     * TODO: b/402794215 - Persist on shutdown once persist performance is sufficiently improved.
     */
    @VisibleForTesting MonotonicClock mMonotonicClock = null;

@@ -296,7 +303,7 @@ public final class AppStartInfoTracker {
            if (!mEnabled) {
                return;
            }
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
            start.setIntent(intent);
            start.setStartType(ApplicationStartInfo.START_TYPE_UNSET);
@@ -454,7 +461,7 @@ public final class AppStartInfoTracker {
            if (!mEnabled) {
                return;
            }
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
            addBaseFieldsFromProcessRecord(start, app);
            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
            start.addStartupTimestamp(
@@ -484,7 +491,7 @@ public final class AppStartInfoTracker {
            if (!mEnabled) {
                return;
            }
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
            addBaseFieldsFromProcessRecord(start, app);
            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
            start.addStartupTimestamp(
@@ -511,7 +518,7 @@ public final class AppStartInfoTracker {
            if (!mEnabled) {
                return;
            }
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
            addBaseFieldsFromProcessRecord(start, app);
            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
            start.addStartupTimestamp(
@@ -533,7 +540,7 @@ public final class AppStartInfoTracker {
            if (!mEnabled) {
                return;
            }
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
            ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
            addBaseFieldsFromProcessRecord(start, app);
            start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
            start.addStartupTimestamp(
@@ -1098,7 +1105,7 @@ public final class AppStartInfoTracker {
                    mLastAppStartInfoPersistTimestamp = now;
                }
            }
            proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTime());
            proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTimeMs());
            if (succeeded) {
                proto.flush();
                af.finishWrite(out);
@@ -1219,7 +1226,11 @@ public final class AppStartInfoTracker {
        }
    }

    private long getMonotonicTime() {
    /**
     * Monotonic time that doesn't change with reboot or device time change for ordering records.
     */
    @VisibleForTesting
    public long getMonotonicTimeMs() {
        if (mMonotonicClock == null) {
            // This should never happen. Return 0 to not interfere with past or future records.
            return 0;
@@ -1439,10 +1450,26 @@ public final class AppStartInfoTracker {
            long token = proto.start(fieldId);
            proto.write(AppsStartInfoProto.Package.User.UID, mUid);
            int size = mInfos.size();
            if (android.app.Flags.appStartInfoCleanupOldRecords()) {
                long removeOlderThan = getMonotonicTimeMs() - APP_START_INFO_HISTORY_LENGTH_MS;
                // Iterate backwards so we can remove old records as we go.
                for (int i = size - 1; i >= 0; i--) {
                    if (mInfos.get(i).getMonoticCreationTimeMs() < removeOlderThan) {
                        // Remove the record.
                        mInfos.remove(i);
                    } else {
                        mInfos.get(i).writeToProto(
                                proto, AppsStartInfoProto.Package.User.APP_START_INFO,
                                byteArrayOutputStream, objectOutputStream, typedXmlSerializer);
                    }
                }
            } else {
                for (int i = 0; i < size; i++) {
                mInfos.get(i).writeToProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO,
                    mInfos.get(i).writeToProto(
                            proto, AppsStartInfoProto.Package.User.APP_START_INFO,
                            byteArrayOutputStream, objectOutputStream, typedXmlSerializer);
                }
            }
            proto.write(AppsStartInfoProto.Package.User.MONITORING_ENABLED, mMonitoringModeEnabled);
            proto.end(token);
        }
+61 −0
Original line number Diff line number Diff line
@@ -680,6 +680,67 @@ public class ApplicationStartInfoTest {
                ApplicationStartInfo.START_TIMESTAMP_FORK));
    }

    /**
     * Test that cleanup old records works as expected, removing records that are older than the max
     * retention length.
     */
    @Test
    @EnableFlags(android.app.Flags.FLAG_APP_START_INFO_CLEANUP_OLD_RECORDS)
    public void testOldRecordsCleanup() throws Exception {
        // Use a different start timestamp for each record so we can identify which was removed.
        // This timestamp is not used for ordering and has no impact on removal.
        final long startTimeRecord1 = 123L;
        final long startTimeRecord2 = 456L;
        final long startTimeRecord3 = 789L;

        // Create a process record to use with all starts.
        ProcessRecord app = makeProcessRecord(
                APP_1_PID_1,                     // pid
                APP_1_UID,                       // uid
                APP_1_UID,                       // packageUid
                null,                            // definingUid
                APP_1_PROCESS_NAME,              // processName
                APP_1_PACKAGE_NAME);             // packageName

        // Set monotonic time to 1, and then trigger a start info record.
        doReturn(1L).when(mAppStartInfoTracker).getMonotonicTimeMs();
        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app,
                buildIntent(COMPONENT), false /* isAlarm */);

        // Set monotonic time to 2, and then trigger another start info record.
        doReturn(2L).when(mAppStartInfoTracker).getMonotonicTimeMs();
        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app,
                buildIntent(COMPONENT), false /* isAlarm */);

        // Set monotonic time to 3, then trigger another start info record.
        doReturn(3L).when(mAppStartInfoTracker).getMonotonicTimeMs();
        mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord3, app,
                buildIntent(COMPONENT), false /* isAlarm */);

        // Verify that all 3 records were added successfully.
        ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
        assertEquals(3, list.size());
        assertEquals(startTimeRecord3, list.get(0).getStartupTimestamps().get(0).longValue());
        assertEquals(startTimeRecord2, list.get(1).getStartupTimestamps().get(0).longValue());
        assertEquals(startTimeRecord1, list.get(2).getStartupTimestamps().get(0).longValue());

        // Set monotonic time to max history length + 3 so that the older 2 records will be removed
        // but that newest 1 will remain.
        doReturn(AppStartInfoTracker.APP_START_INFO_HISTORY_LENGTH_MS + 3L)
                .when(mAppStartInfoTracker).getMonotonicTimeMs();

        // Trigger a persist which will trigger the cleanup of old records.
        mAppStartInfoTracker.persistProcessStartInfo();

        // Now verify that the records older than max were removed, and that the records not older
        // remain.
        list.clear();
        mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
        assertEquals(1, list.size());
        assertEquals(startTimeRecord3, list.get(0).getStartupTimestamps().get(0).longValue());
    }

    private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
        try {
            Field field = clazz.getDeclaredField(fieldName);