import android.os.SystemClock;
import android.os.UserHandle;
import android.util.EventLog;
+import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
long mStartVisibleTime;
long mEndTime;
int mNumActive;
+
+ // Temp output of foregroundAppShownEnoughLocked
+ long mHideTime;
}
/**
}
}
+ boolean foregroundAppShownEnoughLocked(ActiveForegroundApp aa, long nowElapsed) {
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Shown enough: pkg=" + aa.mPackageName + ", uid="
+ + aa.mUid);
+ boolean canRemove = false;
+ aa.mHideTime = Long.MAX_VALUE;
+ if (aa.mShownWhileTop) {
+ // If the app was ever at the top of the screen while the foreground
+ // service was running, then we can always just immediately remove it.
+ canRemove = true;
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - shown while on top");
+ } else if (mScreenOn || aa.mShownWhileScreenOn) {
+ final long minTime = aa.mStartVisibleTime
+ + (aa.mStartTime != aa.mStartVisibleTime
+ ? mAm.mConstants.FGSERVICE_SCREEN_ON_AFTER_TIME
+ : mAm.mConstants.FGSERVICE_MIN_SHOWN_TIME);
+ if (nowElapsed >= minTime) {
+ // If shown while the screen is on, and it has been shown for
+ // at least the minimum show time, then we can now remove it.
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - shown long enough with screen on");
+ canRemove = true;
+ } else {
+ // This is when we will be okay to stop telling the user.
+ long reportTime = nowElapsed + mAm.mConstants.FGSERVICE_MIN_REPORT_TIME;
+ aa.mHideTime = reportTime > minTime ? reportTime : minTime;
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "NO -- wait " + (aa.mHideTime-nowElapsed)
+ + " with screen on");
+ }
+ } else {
+ final long minTime = aa.mEndTime
+ + mAm.mConstants.FGSERVICE_SCREEN_ON_BEFORE_TIME;
+ if (nowElapsed >= minTime) {
+ // If the foreground service has only run while the screen is
+ // off, but it has been gone now for long enough that we won't
+ // care to tell the user about it when the screen comes back on,
+ // then we can remove it now.
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - gone long enough with screen off");
+ canRemove = true;
+ } else {
+ // This is when we won't care about this old fg service.
+ aa.mHideTime = minTime;
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "NO -- wait " + (aa.mHideTime-nowElapsed)
+ + " with screen off");
+ }
+ }
+ return canRemove;
+ }
+
void updateForegroundApps(ServiceMap smap) {
// This is called from the handler without the lock held.
ArrayList<ActiveForegroundApp> active = null;
synchronized (mAm) {
final long now = SystemClock.elapsedRealtime();
- final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
long nextUpdateTime = Long.MAX_VALUE;
if (smap != null) {
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Updating foreground apps for user "
+ + smap.mUserId);
for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) {
ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i);
- if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) {
- if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
- // Check to see if this should still be displayed... we continue
- // until it has been shown for at least the timeout duration.
- if (nowPlusMin >= aa.mStartVisibleTime) {
- // All over!
- smap.mActiveForegroundApps.removeAt(i);
- smap.mActiveForegroundAppsChanged = true;
- continue;
- } else {
- long hideTime = aa.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME;
- if (hideTime < nextUpdateTime) {
- nextUpdateTime = hideTime;
- }
- }
- } else {
+ if (aa.mEndTime != 0) {
+ boolean canRemove = foregroundAppShownEnoughLocked(aa, now);
+ if (canRemove) {
// This was up for longer than the timeout, so just remove immediately.
smap.mActiveForegroundApps.removeAt(i);
smap.mActiveForegroundAppsChanged = true;
continue;
}
+ if (aa.mHideTime < nextUpdateTime) {
+ nextUpdateTime = aa.mHideTime;
+ }
}
if (!aa.mAppOnTop) {
if (active == null) {
active = new ArrayList<>();
}
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Adding active: pkg="
+ + aa.mPackageName + ", uid=" + aa.mUid);
active.add(aa);
}
}
smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
if (nextUpdateTime < Long.MAX_VALUE) {
- Message msg = smap.obtainMessage();
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Next update time in: "
+ + (nextUpdateTime-now));
+ Message msg = smap.obtainMessage(ServiceMap.MSG_UPDATE_FOREGROUND_APPS);
smap.sendMessageAtTime(msg, nextUpdateTime
+ SystemClock.uptimeMillis() - SystemClock.elapsedRealtime());
}
active.mNumActive--;
if (active.mNumActive <= 0) {
active.mEndTime = SystemClock.elapsedRealtime();
- if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) {
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Ended running of service");
+ if (foregroundAppShownEnoughLocked(active, active.mEndTime)) {
// Have been active for long enough that we will remove it immediately.
smap.mActiveForegroundApps.remove(r.packageName);
smap.mActiveForegroundAppsChanged = true;
requestUpdateActiveForegroundAppsLocked(smap, 0);
- } else {
- requestUpdateActiveForegroundAppsLocked(smap, active.mStartVisibleTime
- + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME);
+ } else if (active.mHideTime < Long.MAX_VALUE){
+ requestUpdateActiveForegroundAppsLocked(smap, active.mHideTime);
}
}
}
// services that were started while the screen was off.
if (screenOn) {
final long nowElapsed = SystemClock.elapsedRealtime();
+ if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Screen turned on");
for (int i = mServiceMap.size()-1; i >= 0; i--) {
ServiceMap smap = mServiceMap.valueAt(i);
+ long nextUpdateTime = Long.MAX_VALUE;
boolean changed = false;
for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) {
ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j);
- if (!active.mShownWhileScreenOn) {
- changed = true;
- active.mShownWhileScreenOn = mScreenOn;
- active.mStartVisibleTime = nowElapsed;
- if (active.mEndTime != 0) {
- active.mEndTime = nowElapsed;
+ if (active.mEndTime == 0) {
+ if (!active.mShownWhileScreenOn) {
+ active.mShownWhileScreenOn = true;
+ active.mStartVisibleTime = nowElapsed;
+ }
+ } else {
+ if (!active.mShownWhileScreenOn
+ && active.mStartVisibleTime == active.mStartTime) {
+ // If this was never shown while the screen was on, then we will
+ // count the time it started being visible as now, to tell the user
+ // about it now that they have a screen to look at.
+ active.mEndTime = active.mStartVisibleTime = nowElapsed;
+ }
+ if (foregroundAppShownEnoughLocked(active, nowElapsed)) {
+ // Have been active for long enough that we will remove it
+ // immediately.
+ smap.mActiveForegroundApps.remove(active.mPackageName);
+ smap.mActiveForegroundAppsChanged = true;
+ changed = true;
+ } else {
+ if (active.mHideTime < nextUpdateTime) {
+ nextUpdateTime = active.mHideTime;
+ }
}
}
}
if (changed) {
- requestUpdateActiveForegroundAppsLocked(smap,
- nowElapsed + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME);
- } else if (smap.mActiveForegroundApps.size() > 0) {
- // Just being paranoid.
+ // Need to immediately update.
requestUpdateActiveForegroundAppsLocked(smap, 0);
+ } else if (nextUpdateTime < Long.MAX_VALUE) {
+ requestUpdateActiveForegroundAppsLocked(smap, nextUpdateTime);
}
}
}
return true;
}
- // Is someone still bound to us keepign us running?
+ // Is someone still bound to us keeping us running?
if (!knowConn) {
hasConn = r.hasAutoCreateConnections();
}
pw.println();
}
}
+ if (smap.hasMessagesOrCallbacks()) {
+ if (needSep) {
+ pw.println();
+ }
+ printedAnything = true;
+ needSep = true;
+ pw.print(" Handler - user ");
+ pw.print(user);
+ pw.println(":");
+ smap.dumpMine(new PrintWriterPrinter(pw), " ");
+ }
}
}
// Key names stored in the settings value.
private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes";
private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time";
- private static final String KEY_FOREGROUND_SERVICE_UI_MIN_TIME
- = "foreground_service_ui_min_time";
+ private static final String KEY_FGSERVICE_MIN_SHOWN_TIME
+ = "fgservice_min_shown_time";
+ private static final String KEY_FGSERVICE_MIN_REPORT_TIME
+ = "fgservice_min_report_time";
+ private static final String KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME
+ = "fgservice_screen_on_before_time";
+ private static final String KEY_FGSERVICE_SCREEN_ON_AFTER_TIME
+ = "fgservice_screen_on_after_time";
private static final String KEY_CONTENT_PROVIDER_RETAIN_TIME = "content_provider_retain_time";
private static final String KEY_GC_TIMEOUT = "gc_timeout";
private static final String KEY_GC_MIN_INTERVAL = "gc_min_interval";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
- private static final long DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME = 30*1000;
+ private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
+ private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
+ private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
+ private static final long DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME = 5*1000;
private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
private static final long DEFAULT_GC_TIMEOUT = 5*1000;
private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000;
// before we start restricting what it can do.
public long BACKGROUND_SETTLE_TIME = DEFAULT_BACKGROUND_SETTLE_TIME;
- // The minimum time a foreground service will be shown as running in the notification UI.
- public long FOREGROUND_SERVICE_UI_MIN_TIME = DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME;
+ // The minimum time we allow a foreground service to run with a notification and the
+ // screen on without otherwise telling the user about it. (If it runs for less than this,
+ // it will still be reported to the user as a running app for at least this amount of time.)
+ public long FGSERVICE_MIN_SHOWN_TIME = DEFAULT_FGSERVICE_MIN_SHOWN_TIME;
+
+ // If a foreground service is shown for less than FGSERVICE_MIN_SHOWN_TIME, we will display
+ // the background app running notification about it for at least this amount of time (if it
+ // is larger than the remaining shown time).
+ public long FGSERVICE_MIN_REPORT_TIME = DEFAULT_FGSERVICE_MIN_REPORT_TIME;
+
+ // The minimum amount of time the foreground service needs to have remain being shown
+ // before the screen goes on for us to consider it not worth showing to the user. That is
+ // if an app has a foreground service that stops itself this amount of time or more before
+ // the user turns on the screen, we will just let it go without the user being told about it.
+ public long FGSERVICE_SCREEN_ON_BEFORE_TIME = DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME;
+
+ // The minimum amount of time a foreground service should remain reported to the user if
+ // it is stopped when the screen turns on. This is the time from when the screen turns
+ // on until we will stop reporting it.
+ public long FGSERVICE_SCREEN_ON_AFTER_TIME = DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME;
// How long we will retain processes hosting content providers in the "last activity"
// state before allowing them to drop down to the regular cached LRU list. This is
DEFAULT_MAX_CACHED_PROCESSES);
BACKGROUND_SETTLE_TIME = mParser.getLong(KEY_BACKGROUND_SETTLE_TIME,
DEFAULT_BACKGROUND_SETTLE_TIME);
- FOREGROUND_SERVICE_UI_MIN_TIME = mParser.getLong(KEY_FOREGROUND_SERVICE_UI_MIN_TIME,
- DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME);
+ FGSERVICE_MIN_SHOWN_TIME = mParser.getLong(KEY_FGSERVICE_MIN_SHOWN_TIME,
+ DEFAULT_FGSERVICE_MIN_SHOWN_TIME);
+ FGSERVICE_MIN_REPORT_TIME = mParser.getLong(KEY_FGSERVICE_MIN_REPORT_TIME,
+ DEFAULT_FGSERVICE_MIN_REPORT_TIME);
+ FGSERVICE_SCREEN_ON_BEFORE_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME,
+ DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME);
+ FGSERVICE_SCREEN_ON_AFTER_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME,
+ DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME);
CONTENT_PROVIDER_RETAIN_TIME = mParser.getLong(KEY_CONTENT_PROVIDER_RETAIN_TIME,
DEFAULT_CONTENT_PROVIDER_RETAIN_TIME);
GC_TIMEOUT = mParser.getLong(KEY_GC_TIMEOUT,
pw.println(MAX_CACHED_PROCESSES);
pw.print(" "); pw.print(KEY_BACKGROUND_SETTLE_TIME); pw.print("=");
pw.println(BACKGROUND_SETTLE_TIME);
- pw.print(" "); pw.print(KEY_FOREGROUND_SERVICE_UI_MIN_TIME); pw.print("=");
- pw.println(FOREGROUND_SERVICE_UI_MIN_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_MIN_SHOWN_TIME); pw.print("=");
+ pw.println(FGSERVICE_MIN_SHOWN_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_MIN_REPORT_TIME); pw.print("=");
+ pw.println(FGSERVICE_MIN_REPORT_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME); pw.print("=");
+ pw.println(FGSERVICE_SCREEN_ON_BEFORE_TIME);
+ pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME); pw.print("=");
+ pw.println(FGSERVICE_SCREEN_ON_AFTER_TIME);
pw.print(" "); pw.print(KEY_CONTENT_PROVIDER_RETAIN_TIME); pw.print("=");
pw.println(CONTENT_PROVIDER_RETAIN_TIME);
pw.print(" "); pw.print(KEY_GC_TIMEOUT); pw.print("=");