OSDN Git Service

Work on issue #36891897: Need to ensure foreground services...
authorDianne Hackborn <hackbod@google.com>
Thu, 15 Jun 2017 00:30:15 +0000 (17:30 -0700)
committerDianne Hackborn <hackbod@google.com>
Thu, 15 Jun 2017 00:33:41 +0000 (17:33 -0700)
...can't hide themselves

Tune the policies for when we tell about apps running in the
background after their services have stopped.

- If it ran while the screen was on, the time we require for it
to be running is much shorter (a couple seconds) as well as the
time we tell about it having run (with another tunable for the
minimum time we tell about this).

- If it has only run while the screen is off and stops a sufficient
amount of time before the screen goes on (currently a second) then
we will not show anything when the screen goes on.

- If it stops when the screen turns on, we will make sure the user
sees about it for a short period of time (currently 5 seconds).

Also includes some improved debug output about handler message
queues.

Test: manual

Change-Id: Iab438410d7182b2dfe4f9c1cce7069b26b34834c

core/java/android/os/Handler.java
core/java/android/os/Looper.java
core/java/android/os/MessageQueue.java
core/java/android/provider/Settings.java
services/core/java/com/android/server/am/ActiveServices.java
services/core/java/com/android/server/am/ActivityManagerConstants.java
services/core/java/com/android/server/am/ActivityManagerDebugConfig.java

index 8678d95..b69a23a 100644 (file)
@@ -696,6 +696,14 @@ public class Handler {
     }
 
     /**
+     * Return whether there are any messages or callbacks currently scheduled on this handler.
+     * @hide
+     */
+    public final boolean hasMessagesOrCallbacks() {
+        return mQueue.hasMessages(this);
+    }
+
+    /**
      * Check if there are any pending posts of messages with code 'what' and
      * whose obj is 'object' in the message queue.
      */
@@ -728,6 +736,18 @@ public class Handler {
         }
     }
 
+    /**
+     * @hide
+     */
+    public final void dumpMine(Printer pw, String prefix) {
+        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
+        if (mLooper == null) {
+            pw.println(prefix + "looper uninitialized");
+        } else {
+            mLooper.dump(pw, prefix + "  ", this);
+        }
+    }
+
     @Override
     public String toString() {
         return "Handler (" + getClass().getName() + ") {"
index 44dbcfb..04cceb8 100644 (file)
@@ -310,7 +310,20 @@ public final class Looper {
      */
     public void dump(@NonNull Printer pw, @NonNull String prefix) {
         pw.println(prefix + toString());
-        mQueue.dump(pw, prefix + "  ");
+        mQueue.dump(pw, prefix + "  ", null);
+    }
+
+    /**
+     * Dumps the state of the looper for debugging purposes.
+     *
+     * @param pw A printer to receive the contents of the dump.
+     * @param prefix A prefix to prepend to each line which is printed.
+     * @param handler Only dump messages for this Handler.
+     * @hide
+     */
+    public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
+        pw.println(prefix + toString());
+        mQueue.dump(pw, prefix + "  ", handler);
     }
 
     /** @hide */
index 2a8c52e..624e28a 100644 (file)
@@ -620,6 +620,23 @@ public final class MessageQueue {
         }
     }
 
+    boolean hasMessages(Handler h) {
+        if (h == null) {
+            return false;
+        }
+
+        synchronized (this) {
+            Message p = mMessages;
+            while (p != null) {
+                if (p.target == h) {
+                    return true;
+                }
+                p = p.next;
+            }
+            return false;
+        }
+    }
+
     void removeMessages(Handler h, int what, Object object) {
         if (h == null) {
             return;
@@ -759,12 +776,14 @@ public final class MessageQueue {
         }
     }
 
-    void dump(Printer pw, String prefix) {
+    void dump(Printer pw, String prefix, Handler h) {
         synchronized (this) {
             long now = SystemClock.uptimeMillis();
             int n = 0;
             for (Message msg = mMessages; msg != null; msg = msg.next) {
-                pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+                if (h == null || h == msg.target) {
+                    pw.println(prefix + "Message " + n + ": " + msg.toString(now));
+                }
                 n++;
             }
             pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
index f1ce9d5..65669ee 100755 (executable)
@@ -9029,7 +9029,10 @@ public final class Settings {
          * <pre>
          * max_cached_processes                 (int)
          * background_settle_time               (long)
-         * foreground_service_ui_min_time       (long)
+         * fgservice_min_shown_time             (long)
+         * fgservice_min_report_time            (long)
+         * fgservice_screen_on_before_time      (long)
+         * fgservice_screen_on_after_time       (long)
          * content_provider_retain_time         (long)
          * gc_timeout                           (long)
          * gc_min_interval                      (long)
index c417484..de14477 100644 (file)
@@ -81,6 +81,7 @@ import android.os.RemoteException;
 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;
@@ -175,6 +176,9 @@ public final class ActiveServices {
         long mStartVisibleTime;
         long mEndTime;
         int mNumActive;
+
+        // Temp output of foregroundAppShownEnoughLocked
+        long mHideTime;
     }
 
     /**
@@ -736,50 +740,90 @@ public final class ActiveServices {
         }
     }
 
+    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());
                 }
@@ -882,15 +926,14 @@ public final class ActiveServices {
             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);
                 }
             }
         }
@@ -904,26 +947,44 @@ public final class ActiveServices {
             // 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);
                     }
                 }
             }
@@ -2318,7 +2379,7 @@ public final class ActiveServices {
             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();
         }
@@ -3741,6 +3802,17 @@ public final class ActiveServices {
                             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), "    ");
+                    }
                 }
             }
 
index 5749f31..6c3fe91 100644 (file)
@@ -35,8 +35,14 @@ final class ActivityManagerConstants extends ContentObserver {
     // 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";
@@ -58,7 +64,10 @@ final class ActivityManagerConstants extends ContentObserver {
 
     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;
@@ -85,8 +94,26 @@ final class ActivityManagerConstants extends ContentObserver {
     // 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
@@ -225,8 +252,14 @@ final class ActivityManagerConstants extends ContentObserver {
                     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,
@@ -284,8 +317,14 @@ final class ActivityManagerConstants extends ContentObserver {
         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("=");
index ff5efde..f440100 100644 (file)
@@ -79,6 +79,7 @@ class ActivityManagerDebugConfig {
     static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_SCREENSHOTS = DEBUG_ALL_ACTIVITIES || false;
     static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
+    static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
     static final boolean DEBUG_STACK = DEBUG_ALL || false;
     static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;