summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Varun Shah <varunshah@google.com> 2024-04-25 09:31:54 +0000
committer Varun Shah <varunshah@google.com> 2024-04-25 09:31:54 +0000
commitb1c835da4e5ae6db1a6328a1192a2a20dc1a6a13 (patch)
treec04a242a0c3f5c1e0aa4a28b28bb005a1fcdc2b4
parent860d9e7caa4aee6180db85b0ee7458ccf7abf24b (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.java56
-rw-r--r--services/core/java/com/android/server/am/ServiceRecord.java45
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;