diff options
| author | 2024-04-25 09:31:54 +0000 | |
|---|---|---|
| committer | 2024-04-25 09:31:54 +0000 | |
| commit | b1c835da4e5ae6db1a6328a1192a2a20dc1a6a13 (patch) | |
| tree | c04a242a0c3f5c1e0aa4a28b28bb005a1fcdc2b4 | |
| parent | 860d9e7caa4aee6180db85b0ee7458ccf7abf24b (diff) | |
Update fgs time limit reset logic.
Instead of strictly waiting until the user launches the app after a time
limit is exhaused, infer the app being in the TOP state as a signal for
when the time limit should occur from. This will ensure subsequent
user-initiated fgs starts allow the fgs to run for the entire time
limit.
Also update all timestamps to use the uptime base instead of realtime.
This makes all FGS tracking using the same time base and ensures apps
are not penalized for runtime when the CPU is asleep.
Bug: 330399444
Test: atest CtsFgsTimeoutTestsCases
Change-Id: I288f65b0943ddb1898851bb333f0d4e6a41b2fdb
| -rw-r--r-- | services/core/java/com/android/server/am/ActiveServices.java | 56 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ServiceRecord.java | 45 |
2 files changed, 60 insertions, 41 deletions
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index eec22c92b5dc..3486cd5f4d46 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -3724,6 +3724,33 @@ public final class ActiveServices { return fgsType; } + /** + * @return the constant time limit defined for the given foreground service type. + */ + private long getTimeLimitForFgsType(int foregroundServiceType) { + return switch (foregroundServiceType) { + case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING -> + mAm.mConstants.mMediaProcessingFgsTimeoutDuration; + case ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC -> + mAm.mConstants.mDataSyncFgsTimeoutDuration; + // Add logic for time limits introduced in the future for other fgs types above. + default -> Long.MAX_VALUE; + }; + } + + /** + * @return the next stop time for the given type, based on how long it has already ran for. + * The total runtime is automatically reset 24hrs after the first fgs start of this type + * or if the app has recently been in the TOP state when the app calls startForeground(). + */ + private long getNextFgsStopTime(int fgsType, TimeLimitedFgsInfo fgsInfo) { + final long timeLimit = getTimeLimitForFgsType(fgsType); + if (timeLimit == Long.MAX_VALUE) { + return Long.MAX_VALUE; + } + return fgsInfo.getLastFgsStartTime() + Math.max(0, timeLimit - fgsInfo.getTotalRuntime()); + } + private void maybeUpdateFgsTrackingLocked(ServiceRecord sr, int previousFgsType) { final int previouslyTimeLimitedType = getTimeLimitedFgsType(previousFgsType); if (previouslyTimeLimitedType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE @@ -3755,7 +3782,7 @@ public final class ActiveServices { } traceInstant("FGS start: ", sr); - final long nowRealtime = SystemClock.elapsedRealtime(); + final long nowUptime = SystemClock.uptimeMillis(); // Fetch/create/update the fgs info for the time-limited type. SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid); @@ -3766,10 +3793,10 @@ public final class ActiveServices { final int timeLimitedFgsType = getTimeLimitedFgsType(sr.foregroundServiceType); TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedFgsType); if (fgsTypeInfo == null) { - fgsTypeInfo = sr.createTimeLimitedFgsInfo(nowRealtime); + fgsTypeInfo = sr.createTimeLimitedFgsInfo(nowUptime); fgsInfo.put(timeLimitedFgsType, fgsTypeInfo); } - fgsTypeInfo.setLastFgsStartTime(nowRealtime); + fgsTypeInfo.setLastFgsStartTime(nowUptime); // We'll cancel the previous ANR timer and start a fresh one below. mFGSAnrTimer.cancel(sr); @@ -3777,7 +3804,7 @@ public final class ActiveServices { final Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr); - final long timeoutCallbackTime = sr.getNextFgsStopTime(timeLimitedFgsType, fgsTypeInfo); + final long timeoutCallbackTime = getNextFgsStopTime(timeLimitedFgsType, fgsTypeInfo); if (timeoutCallbackTime == Long.MAX_VALUE) { // This should never happen since we only get to this point if the service record's // foregroundServiceType attribute contains a type that can be timed-out. @@ -3833,6 +3860,20 @@ public final class ActiveServices { mFGSAnrTimer.discard(sr); return; } + + final long lastTopTime = sr.app.mState.getLastTopTime(); + final long constantTimeLimit = getTimeLimitForFgsType(fgsType); + final long nowUptime = SystemClock.uptimeMillis(); + if (constantTimeLimit > (nowUptime - lastTopTime)) { + // The app was in the TOP state after the FGS was started so its time allowance + // should be counted from that time since this is considered a user interaction + mFGSAnrTimer.discard(sr); + final Message msg = mAm.mHandler.obtainMessage( + ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr); + mAm.mHandler.sendMessageAtTime(msg, lastTopTime + constantTimeLimit); + return; + } + Slog.e(TAG_SERVICE, "FGS (" + ServiceInfo.foregroundServiceTypeToLabel(fgsType) + ") timed out: " + sr); mFGSAnrTimer.accept(sr); @@ -3843,14 +3884,13 @@ public final class ActiveServices { final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(fgsType); if (fgsTypeInfo != null) { // Update total runtime for the time-limited fgs type and mark it as timed out. - final long nowRealtime = SystemClock.elapsedRealtime(); fgsTypeInfo.updateTotalRuntime(); - fgsTypeInfo.setTimeLimitExceededAt(nowRealtime); + fgsTypeInfo.setTimeLimitExceededAt(nowUptime); logFGSStateChangeLocked(sr, FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT, - nowRealtime > fgsTypeInfo.getLastFgsStartTime() - ? (int) (nowRealtime - fgsTypeInfo.getLastFgsStartTime()) : 0, + nowUptime > fgsTypeInfo.getLastFgsStartTime() + ? (int) (nowUptime - fgsTypeInfo.getLastFgsStartTime()) : 0, FGS_STOP_REASON_UNKNOWN, FGS_TYPE_POLICY_CHECK_UNKNOWN, FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA, diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 065f3bd6b1e6..68f7cc1cee3c 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -30,9 +30,9 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UptimeMillisLong; import android.app.BackgroundStartPrivileges; import android.app.IApplicationThread; import android.app.Notification; @@ -677,29 +677,29 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN * Data container class to help track certain fgs info for time-restricted types. */ static class TimeLimitedFgsInfo { - @ElapsedRealtimeLong + @UptimeMillisLong private long mFirstFgsStartTime; - @ElapsedRealtimeLong + @UptimeMillisLong private long mLastFgsStartTime; - @ElapsedRealtimeLong + @UptimeMillisLong private long mTimeLimitExceededAt = Long.MIN_VALUE; private long mTotalRuntime = 0; - TimeLimitedFgsInfo(@ElapsedRealtimeLong long startTime) { + TimeLimitedFgsInfo(@UptimeMillisLong long startTime) { mFirstFgsStartTime = startTime; mLastFgsStartTime = startTime; } - @ElapsedRealtimeLong + @UptimeMillisLong public long getFirstFgsStartTime() { return mFirstFgsStartTime; } - public void setLastFgsStartTime(@ElapsedRealtimeLong long startTime) { + public void setLastFgsStartTime(@UptimeMillisLong long startTime) { mLastFgsStartTime = startTime; } - @ElapsedRealtimeLong + @UptimeMillisLong public long getLastFgsStartTime() { return mLastFgsStartTime; } @@ -712,11 +712,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN return mTotalRuntime; } - public void setTimeLimitExceededAt(@ElapsedRealtimeLong long timeLimitExceededAt) { + public void setTimeLimitExceededAt(@UptimeMillisLong long timeLimitExceededAt) { mTimeLimitExceededAt = timeLimitExceededAt; } - @ElapsedRealtimeLong + @UptimeMillisLong public long getTimeLimitExceededAt() { return mTimeLimitExceededAt; } @@ -1858,8 +1858,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN /** * Called when a time-limited FGS starts. */ - public TimeLimitedFgsInfo createTimeLimitedFgsInfo(@ElapsedRealtimeLong long nowRealtime) { - return new TimeLimitedFgsInfo(nowRealtime); + public TimeLimitedFgsInfo createTimeLimitedFgsInfo(@UptimeMillisLong long nowUptime) { + return new TimeLimitedFgsInfo(nowUptime); } /** @@ -1872,27 +1872,6 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN != ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; } - /** - * @return the next stop time for the given type, based on how long it has already ran for. - * The total runtime is automatically reset 24hrs after the first fgs start of this type - * or if the app has recently been in the TOP state when the app calls startForeground(). - */ - long getNextFgsStopTime(int fgsType, TimeLimitedFgsInfo fgsInfo) { - final long timeLimit; - switch (ams.mServices.getTimeLimitedFgsType(fgsType)) { - case ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING: - timeLimit = ams.mConstants.mMediaProcessingFgsTimeoutDuration; - break; - case ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC: - timeLimit = ams.mConstants.mDataSyncFgsTimeoutDuration; - break; - // Add logic for time limits introduced in the future for other fgs types above. - default: - return Long.MAX_VALUE; - } - return fgsInfo.mLastFgsStartTime + Math.max(0, timeLimit - fgsInfo.mTotalRuntime); - } - private boolean isAppAlive() { if (app == null) { return false; |