Loading core/java/android/app/ApplicationStartInfo.java +30 −4 Original line number Diff line number Diff line Loading @@ -209,6 +209,11 @@ public final class ApplicationStartInfo implements Parcelable { /** Clock monotonic timestamp of surfaceflinger composition complete. */ public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7; /** * @see #getMonoticCreationTimeMs */ private long mMonoticCreationTimeMs; /** * @see #getStartupState */ Loading Loading @@ -486,6 +491,15 @@ public final class ApplicationStartInfo implements Parcelable { return mStartupState; } /** * Monotonic elapsed time persisted across reboots. * * @hide */ public long getMonoticCreationTimeMs() { return mMonoticCreationTimeMs; } /** * The process id. * Loading Loading @@ -669,7 +683,9 @@ public final class ApplicationStartInfo implements Parcelable { } /** @hide */ public ApplicationStartInfo() {} public ApplicationStartInfo(long monotonicCreationTimeMs) { mMonoticCreationTimeMs = monotonicCreationTimeMs; } /** @hide */ public ApplicationStartInfo(ApplicationStartInfo other) { Loading @@ -686,6 +702,7 @@ public final class ApplicationStartInfo implements Parcelable { mStartIntent = other.mStartIntent; mLaunchMode = other.mLaunchMode; mWasForceStopped = other.mWasForceStopped; mMonoticCreationTimeMs = other.mMonoticCreationTimeMs; } private ApplicationStartInfo(@NonNull Parcel in) { Loading @@ -708,6 +725,7 @@ public final class ApplicationStartInfo implements Parcelable { in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); mLaunchMode = in.readInt(); mWasForceStopped = in.readBoolean(); mMonoticCreationTimeMs = in.readLong(); } private static String intern(@Nullable String source) { Loading Loading @@ -786,6 +804,7 @@ public final class ApplicationStartInfo implements Parcelable { } proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode); proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped); proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonoticCreationTimeMs); proto.end(token); } Loading Loading @@ -869,6 +888,10 @@ public final class ApplicationStartInfo implements Parcelable { mWasForceStopped = proto.readBoolean( ApplicationStartInfoProto.WAS_FORCE_STOPPED); break; case (int) ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS: mMonoticCreationTimeMs = proto.readLong( ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS); break; } } proto.end(token); Loading @@ -881,6 +904,8 @@ public final class ApplicationStartInfo implements Parcelable { sb.append(prefix) .append("ApplicationStartInfo ").append(seqSuffix).append(':') .append('\n') .append(" monotonicCreationTimeMs=").append(mMonoticCreationTimeMs) .append('\n') .append(" pid=").append(mPid) .append(" realUid=").append(mRealUid) .append(" packageUid=").append(mPackageUid) Loading Loading @@ -949,14 +974,15 @@ public final class ApplicationStartInfo implements Parcelable { && 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; && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs; } @Override public int hashCode() { return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState, mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs); mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs, mMonoticCreationTimeMs); } private boolean timestampsEquals(@NonNull ApplicationStartInfo other) { Loading core/proto/android/app/appstartinfo.proto +1 −0 Original line number Diff line number Diff line Loading @@ -40,4 +40,5 @@ message ApplicationStartInfoProto { optional bytes start_intent = 10; optional AppStartLaunchMode launch_mode = 11; optional bool was_force_stopped = 12; optional int64 monotonic_creation_time_ms = 13; } core/proto/android/server/activitymanagerservice.proto +3 −1 Original line number Diff line number Diff line Loading @@ -1045,7 +1045,7 @@ message AppsExitInfoProto { repeated Package packages = 2; } // sync with com.android.server.am.am.ProcessList.java // LINT.IfChange message AppsStartInfoProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; Loading @@ -1064,4 +1064,6 @@ message AppsStartInfoProto { repeated User users = 2; } repeated Package packages = 2; optional int64 monotonic_time = 3; } // LINT.ThenChange(/services/core/java/com/android/server/am/AppStartInfoTracker.java) services/core/java/com/android/server/am/AppStartInfoTracker.java +45 −18 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.am; import static android.app.ApplicationStartInfo.START_TIMESTAMP_LAUNCH; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; Loading Loading @@ -51,6 +50,8 @@ import android.util.proto.WireTypeMismatchException; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; import com.android.internal.os.Clock; import com.android.internal.os.MonotonicClock; import com.android.server.IoThread; import com.android.server.ServiceThread; import com.android.server.SystemServiceManager; Loading Loading @@ -107,6 +108,16 @@ public final class AppStartInfoTracker { @VisibleForTesting boolean mEnabled = false; /** * 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. */ @VisibleForTesting MonotonicClock mMonotonicClock = null; /** Initialized in {@link #init} and read-only after that. */ @VisibleForTesting ActivityManagerService mService; Loading Loading @@ -203,6 +214,15 @@ public final class AppStartInfoTracker { IoThread.getHandler().post(() -> { loadExistingProcessStartInfo(); }); if (mMonotonicClock == null) { // This should only happen if there are no persisted records, or if the records were // persisted by a version without the monotonic clock. Either way, create a new clock // with no offset. In the case of records with no monotonic time the value will default // to 0 and all new records will correctly end up in front of them. mMonotonicClock = new MonotonicClock(Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK); } } /** Loading Loading @@ -264,7 +284,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.setIntent(intent); start.setStartType(ApplicationStartInfo.START_TYPE_UNSET); Loading Loading @@ -396,7 +416,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading @@ -422,7 +442,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading @@ -444,7 +464,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading @@ -461,7 +481,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading Loading @@ -632,7 +652,8 @@ public final class AppStartInfoTracker { Collections.sort( list, (a, b) -> Long.compare(getStartTimestamp(b), getStartTimestamp(a))); Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); int size = list.size(); if (maxNum > 0) { size = Math.min(size, maxNum); Loading Loading @@ -898,6 +919,10 @@ public final class AppStartInfoTracker { case (int) AppsStartInfoProto.PACKAGES: loadPackagesFromProto(proto, next); break; case (int) AppsStartInfoProto.MONOTONIC_TIME: long monotonicTime = proto.readLong(AppsStartInfoProto.MONOTONIC_TIME); mMonotonicClock = new MonotonicClock(monotonicTime, Clock.SYSTEM_CLOCK); break; } } } catch (IOException | IllegalArgumentException | WireTypeMismatchException Loading Loading @@ -979,6 +1004,7 @@ public final class AppStartInfoTracker { mLastAppStartInfoPersistTimestamp = now; } } proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTime()); if (succeeded) { proto.flush(); af.finishWrite(out); Loading Loading @@ -1099,13 +1125,12 @@ 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; private long getMonotonicTime() { if (mMonotonicClock == null) { // This should never happen. Return 0 to not interfere with past or future records. return 0; } return startInfo.getStartupTimestamps().get(START_TIMESTAMP_LAUNCH); return mMonotonicClock.monotonicTime(); } /** A container class of (@link android.app.ApplicationStartInfo) */ Loading Loading @@ -1143,7 +1168,7 @@ public final class AppStartInfoTracker { // Sort records so we can remove the least recent ones. Collections.sort(mInfos, (a, b) -> Long.compare(getStartTimestamp(b), getStartTimestamp(a))); Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); // Remove records and trim list object back to size. mInfos.subList(0, mInfos.size() - getMaxCapacity()).clear(); Loading @@ -1165,8 +1190,8 @@ public final class AppStartInfoTracker { long oldestTimeStamp = Long.MAX_VALUE; for (int i = 0; i < size; i++) { ApplicationStartInfo startInfo = mInfos.get(i); if (getStartTimestamp(startInfo) < oldestTimeStamp) { oldestTimeStamp = getStartTimestamp(startInfo); if (startInfo.getMonoticCreationTimeMs() < oldestTimeStamp) { oldestTimeStamp = startInfo.getMonoticCreationTimeMs(); oldestIndex = i; } } Loading @@ -1176,7 +1201,7 @@ public final class AppStartInfoTracker { } mInfos.add(info); Collections.sort(mInfos, (a, b) -> Long.compare(getStartTimestamp(b), getStartTimestamp(a))); Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); } /** Loading Loading @@ -1337,7 +1362,9 @@ public final class AppStartInfoTracker { mUid = proto.readInt(AppsStartInfoProto.Package.User.UID); break; case (int) AppsStartInfoProto.Package.User.APP_START_INFO: ApplicationStartInfo info = new ApplicationStartInfo(); // Create record with monotonic time 0 in case the persisted record does not // have a create time. ApplicationStartInfo info = new ApplicationStartInfo(0); info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO); mInfos.add(info); break; Loading services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java +84 −4 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerService.Injector; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; Loading @@ -42,6 +43,8 @@ import android.os.Process; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; import com.android.internal.os.Clock; import com.android.internal.os.MonotonicClock; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.appop.AppOpsService; Loading Loading @@ -121,11 +124,18 @@ public class ApplicationStartInfoTest { LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); mAppStartInfoTracker.mMonotonicClock = new MonotonicClock( Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK); mAppStartInfoTracker.clearProcessStartInfo(true); mAppStartInfoTracker.mAppStartInfoLoaded.set(true); mAppStartInfoTracker.mAppStartInfoHistoryListSize = mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE; doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean()); mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(), AppStartInfoTracker.APP_START_STORE_DIR); mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir, AppStartInfoTracker.APP_START_INFO_FILE); } @After Loading @@ -135,11 +145,8 @@ public class ApplicationStartInfoTest { @Test public void testApplicationStartInfo() throws Exception { mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(), AppStartInfoTracker.APP_START_STORE_DIR); // Make sure we can write to the file. assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir)); mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir, AppStartInfoTracker.APP_START_INFO_FILE); final long appStartTimestampIntentStarted = 1000000; final long appStartTimestampActivityLaunchFinished = 2000000; Loading Loading @@ -482,6 +489,79 @@ public class ApplicationStartInfoTest { verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS); } /** * Test to make sure that records are returned in correct order, from most recently added at * index 0 to least recently added at index size - 1. */ @Test public void testHistoricalRecordsOrdering() throws Exception { // Clear old records mAppStartInfoTracker.clearProcessStartInfo(false); // Add some records with timestamps 0 decreasing as clock increases. 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 mAppStartInfoTracker.handleProcessBroadcastStart(3, app, buildIntent(COMPONENT), false /* isAlarm */); mAppStartInfoTracker.handleProcessBroadcastStart(2, app, buildIntent(COMPONENT), false /* isAlarm */); mAppStartInfoTracker.handleProcessBroadcastStart(1, app, buildIntent(COMPONENT), false /* isAlarm */); // Get records ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>(); mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); // Confirm that records are in correct order, with index 0 representing the most recently // added record and index size - 1 representing the least recently added one. assertEquals(3, list.size()); assertEquals(1L, list.get(0).getStartupTimestamps().get(0).longValue()); assertEquals(2L, list.get(1).getStartupTimestamps().get(0).longValue()); assertEquals(3L, list.get(2).getStartupTimestamps().get(0).longValue()); } /** * Test to make sure that persist and restore correctly maintains the state of the monotonic * clock. */ @Test public void testPersistAndRestoreMonotonicClock() { // Make sure we can write to the file. assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir)); // No need to persist records for this test, clear any that may be there. mAppStartInfoTracker.clearProcessStartInfo(false); // Set clock with an arbitrary 5 minute offset, just needs to be longer than it would take // for code to run. mAppStartInfoTracker.mMonotonicClock = new MonotonicClock(5 * 60 * 1000, Clock.SYSTEM_CLOCK); // Record the current time. long originalMonotonicTime = mAppStartInfoTracker.mMonotonicClock.monotonicTime(); // Now persist the process start info. Records were cleared above so this should just // persist the monotonic time. mAppStartInfoTracker.persistProcessStartInfo(); // Null out the clock to make sure its set on load. mAppStartInfoTracker.mMonotonicClock = null; assertNull(mAppStartInfoTracker.mMonotonicClock); // Now load from disk. mAppStartInfoTracker.loadExistingProcessStartInfo(); // Confirm clock has been set and that its current time is greater than the previous one. assertNotNull(mAppStartInfoTracker.mMonotonicClock); assertTrue(mAppStartInfoTracker.mMonotonicClock.monotonicTime() > originalMonotonicTime); } private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { try { Field field = clazz.getDeclaredField(fieldName); Loading Loading
core/java/android/app/ApplicationStartInfo.java +30 −4 Original line number Diff line number Diff line Loading @@ -209,6 +209,11 @@ public final class ApplicationStartInfo implements Parcelable { /** Clock monotonic timestamp of surfaceflinger composition complete. */ public static final int START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE = 7; /** * @see #getMonoticCreationTimeMs */ private long mMonoticCreationTimeMs; /** * @see #getStartupState */ Loading Loading @@ -486,6 +491,15 @@ public final class ApplicationStartInfo implements Parcelable { return mStartupState; } /** * Monotonic elapsed time persisted across reboots. * * @hide */ public long getMonoticCreationTimeMs() { return mMonoticCreationTimeMs; } /** * The process id. * Loading Loading @@ -669,7 +683,9 @@ public final class ApplicationStartInfo implements Parcelable { } /** @hide */ public ApplicationStartInfo() {} public ApplicationStartInfo(long monotonicCreationTimeMs) { mMonoticCreationTimeMs = monotonicCreationTimeMs; } /** @hide */ public ApplicationStartInfo(ApplicationStartInfo other) { Loading @@ -686,6 +702,7 @@ public final class ApplicationStartInfo implements Parcelable { mStartIntent = other.mStartIntent; mLaunchMode = other.mLaunchMode; mWasForceStopped = other.mWasForceStopped; mMonoticCreationTimeMs = other.mMonoticCreationTimeMs; } private ApplicationStartInfo(@NonNull Parcel in) { Loading @@ -708,6 +725,7 @@ public final class ApplicationStartInfo implements Parcelable { in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); mLaunchMode = in.readInt(); mWasForceStopped = in.readBoolean(); mMonoticCreationTimeMs = in.readLong(); } private static String intern(@Nullable String source) { Loading Loading @@ -786,6 +804,7 @@ public final class ApplicationStartInfo implements Parcelable { } proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode); proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped); proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonoticCreationTimeMs); proto.end(token); } Loading Loading @@ -869,6 +888,10 @@ public final class ApplicationStartInfo implements Parcelable { mWasForceStopped = proto.readBoolean( ApplicationStartInfoProto.WAS_FORCE_STOPPED); break; case (int) ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS: mMonoticCreationTimeMs = proto.readLong( ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS); break; } } proto.end(token); Loading @@ -881,6 +904,8 @@ public final class ApplicationStartInfo implements Parcelable { sb.append(prefix) .append("ApplicationStartInfo ").append(seqSuffix).append(':') .append('\n') .append(" monotonicCreationTimeMs=").append(mMonoticCreationTimeMs) .append('\n') .append(" pid=").append(mPid) .append(" realUid=").append(mRealUid) .append(" packageUid=").append(mPackageUid) Loading Loading @@ -949,14 +974,15 @@ public final class ApplicationStartInfo implements Parcelable { && 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; && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs; } @Override public int hashCode() { return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState, mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs); mStartType, mLaunchMode, mProcessName, mStartupTimestampsNs, mMonoticCreationTimeMs); } private boolean timestampsEquals(@NonNull ApplicationStartInfo other) { Loading
core/proto/android/app/appstartinfo.proto +1 −0 Original line number Diff line number Diff line Loading @@ -40,4 +40,5 @@ message ApplicationStartInfoProto { optional bytes start_intent = 10; optional AppStartLaunchMode launch_mode = 11; optional bool was_force_stopped = 12; optional int64 monotonic_creation_time_ms = 13; }
core/proto/android/server/activitymanagerservice.proto +3 −1 Original line number Diff line number Diff line Loading @@ -1045,7 +1045,7 @@ message AppsExitInfoProto { repeated Package packages = 2; } // sync with com.android.server.am.am.ProcessList.java // LINT.IfChange message AppsStartInfoProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; Loading @@ -1064,4 +1064,6 @@ message AppsStartInfoProto { repeated User users = 2; } repeated Package packages = 2; optional int64 monotonic_time = 3; } // LINT.ThenChange(/services/core/java/com/android/server/am/AppStartInfoTracker.java)
services/core/java/com/android/server/am/AppStartInfoTracker.java +45 −18 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.am; import static android.app.ApplicationStartInfo.START_TIMESTAMP_LAUNCH; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; Loading Loading @@ -51,6 +50,8 @@ import android.util.proto.WireTypeMismatchException; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; import com.android.internal.os.Clock; import com.android.internal.os.MonotonicClock; import com.android.server.IoThread; import com.android.server.ServiceThread; import com.android.server.SystemServiceManager; Loading Loading @@ -107,6 +108,16 @@ public final class AppStartInfoTracker { @VisibleForTesting boolean mEnabled = false; /** * 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. */ @VisibleForTesting MonotonicClock mMonotonicClock = null; /** Initialized in {@link #init} and read-only after that. */ @VisibleForTesting ActivityManagerService mService; Loading Loading @@ -203,6 +214,15 @@ public final class AppStartInfoTracker { IoThread.getHandler().post(() -> { loadExistingProcessStartInfo(); }); if (mMonotonicClock == null) { // This should only happen if there are no persisted records, or if the records were // persisted by a version without the monotonic clock. Either way, create a new clock // with no offset. In the case of records with no monotonic time the value will default // to 0 and all new records will correctly end up in front of them. mMonotonicClock = new MonotonicClock(Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK); } } /** Loading Loading @@ -264,7 +284,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.setIntent(intent); start.setStartType(ApplicationStartInfo.START_TYPE_UNSET); Loading Loading @@ -396,7 +416,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading @@ -422,7 +442,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading @@ -444,7 +464,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading @@ -461,7 +481,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } ApplicationStartInfo start = new ApplicationStartInfo(); ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( Loading Loading @@ -632,7 +652,8 @@ public final class AppStartInfoTracker { Collections.sort( list, (a, b) -> Long.compare(getStartTimestamp(b), getStartTimestamp(a))); Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); int size = list.size(); if (maxNum > 0) { size = Math.min(size, maxNum); Loading Loading @@ -898,6 +919,10 @@ public final class AppStartInfoTracker { case (int) AppsStartInfoProto.PACKAGES: loadPackagesFromProto(proto, next); break; case (int) AppsStartInfoProto.MONOTONIC_TIME: long monotonicTime = proto.readLong(AppsStartInfoProto.MONOTONIC_TIME); mMonotonicClock = new MonotonicClock(monotonicTime, Clock.SYSTEM_CLOCK); break; } } } catch (IOException | IllegalArgumentException | WireTypeMismatchException Loading Loading @@ -979,6 +1004,7 @@ public final class AppStartInfoTracker { mLastAppStartInfoPersistTimestamp = now; } } proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTime()); if (succeeded) { proto.flush(); af.finishWrite(out); Loading Loading @@ -1099,13 +1125,12 @@ 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; private long getMonotonicTime() { if (mMonotonicClock == null) { // This should never happen. Return 0 to not interfere with past or future records. return 0; } return startInfo.getStartupTimestamps().get(START_TIMESTAMP_LAUNCH); return mMonotonicClock.monotonicTime(); } /** A container class of (@link android.app.ApplicationStartInfo) */ Loading Loading @@ -1143,7 +1168,7 @@ public final class AppStartInfoTracker { // Sort records so we can remove the least recent ones. Collections.sort(mInfos, (a, b) -> Long.compare(getStartTimestamp(b), getStartTimestamp(a))); Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); // Remove records and trim list object back to size. mInfos.subList(0, mInfos.size() - getMaxCapacity()).clear(); Loading @@ -1165,8 +1190,8 @@ public final class AppStartInfoTracker { long oldestTimeStamp = Long.MAX_VALUE; for (int i = 0; i < size; i++) { ApplicationStartInfo startInfo = mInfos.get(i); if (getStartTimestamp(startInfo) < oldestTimeStamp) { oldestTimeStamp = getStartTimestamp(startInfo); if (startInfo.getMonoticCreationTimeMs() < oldestTimeStamp) { oldestTimeStamp = startInfo.getMonoticCreationTimeMs(); oldestIndex = i; } } Loading @@ -1176,7 +1201,7 @@ public final class AppStartInfoTracker { } mInfos.add(info); Collections.sort(mInfos, (a, b) -> Long.compare(getStartTimestamp(b), getStartTimestamp(a))); Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); } /** Loading Loading @@ -1337,7 +1362,9 @@ public final class AppStartInfoTracker { mUid = proto.readInt(AppsStartInfoProto.Package.User.UID); break; case (int) AppsStartInfoProto.Package.User.APP_START_INFO: ApplicationStartInfo info = new ApplicationStartInfo(); // Create record with monotonic time 0 in case the persisted record does not // have a create time. ApplicationStartInfo info = new ApplicationStartInfo(0); info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO); mInfos.add(info); break; Loading
services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java +84 −4 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.am.ActivityManagerService.Injector; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; Loading @@ -42,6 +43,8 @@ import android.os.Process; import android.platform.test.annotations.Presubmit; import android.text.TextUtils; import com.android.internal.os.Clock; import com.android.internal.os.MonotonicClock; import com.android.server.LocalServices; import com.android.server.ServiceThread; import com.android.server.appop.AppOpsService; Loading Loading @@ -121,11 +124,18 @@ public class ApplicationStartInfoTest { LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); mAppStartInfoTracker.mMonotonicClock = new MonotonicClock( Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK); mAppStartInfoTracker.clearProcessStartInfo(true); mAppStartInfoTracker.mAppStartInfoLoaded.set(true); mAppStartInfoTracker.mAppStartInfoHistoryListSize = mAppStartInfoTracker.APP_START_INFO_HISTORY_LIST_SIZE; doNothing().when(mAppStartInfoTracker).schedulePersistProcessStartInfo(anyBoolean()); mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(), AppStartInfoTracker.APP_START_STORE_DIR); mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir, AppStartInfoTracker.APP_START_INFO_FILE); } @After Loading @@ -135,11 +145,8 @@ public class ApplicationStartInfoTest { @Test public void testApplicationStartInfo() throws Exception { mAppStartInfoTracker.mProcStartStoreDir = new File(mContext.getFilesDir(), AppStartInfoTracker.APP_START_STORE_DIR); // Make sure we can write to the file. assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir)); mAppStartInfoTracker.mProcStartInfoFile = new File(mAppStartInfoTracker.mProcStartStoreDir, AppStartInfoTracker.APP_START_INFO_FILE); final long appStartTimestampIntentStarted = 1000000; final long appStartTimestampActivityLaunchFinished = 2000000; Loading Loading @@ -482,6 +489,79 @@ public class ApplicationStartInfoTest { verifyInProgressRecordsSize(AppStartInfoTracker.MAX_IN_PROGRESS_RECORDS); } /** * Test to make sure that records are returned in correct order, from most recently added at * index 0 to least recently added at index size - 1. */ @Test public void testHistoricalRecordsOrdering() throws Exception { // Clear old records mAppStartInfoTracker.clearProcessStartInfo(false); // Add some records with timestamps 0 decreasing as clock increases. 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 mAppStartInfoTracker.handleProcessBroadcastStart(3, app, buildIntent(COMPONENT), false /* isAlarm */); mAppStartInfoTracker.handleProcessBroadcastStart(2, app, buildIntent(COMPONENT), false /* isAlarm */); mAppStartInfoTracker.handleProcessBroadcastStart(1, app, buildIntent(COMPONENT), false /* isAlarm */); // Get records ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>(); mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); // Confirm that records are in correct order, with index 0 representing the most recently // added record and index size - 1 representing the least recently added one. assertEquals(3, list.size()); assertEquals(1L, list.get(0).getStartupTimestamps().get(0).longValue()); assertEquals(2L, list.get(1).getStartupTimestamps().get(0).longValue()); assertEquals(3L, list.get(2).getStartupTimestamps().get(0).longValue()); } /** * Test to make sure that persist and restore correctly maintains the state of the monotonic * clock. */ @Test public void testPersistAndRestoreMonotonicClock() { // Make sure we can write to the file. assertTrue(FileUtils.createDir(mAppStartInfoTracker.mProcStartStoreDir)); // No need to persist records for this test, clear any that may be there. mAppStartInfoTracker.clearProcessStartInfo(false); // Set clock with an arbitrary 5 minute offset, just needs to be longer than it would take // for code to run. mAppStartInfoTracker.mMonotonicClock = new MonotonicClock(5 * 60 * 1000, Clock.SYSTEM_CLOCK); // Record the current time. long originalMonotonicTime = mAppStartInfoTracker.mMonotonicClock.monotonicTime(); // Now persist the process start info. Records were cleared above so this should just // persist the monotonic time. mAppStartInfoTracker.persistProcessStartInfo(); // Null out the clock to make sure its set on load. mAppStartInfoTracker.mMonotonicClock = null; assertNull(mAppStartInfoTracker.mMonotonicClock); // Now load from disk. mAppStartInfoTracker.loadExistingProcessStartInfo(); // Confirm clock has been set and that its current time is greater than the previous one. assertNotNull(mAppStartInfoTracker.mMonotonicClock); assertTrue(mAppStartInfoTracker.mMonotonicClock.monotonicTime() > originalMonotonicTime); } private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { try { Field field = clazz.getDeclaredField(fieldName); Loading