Add a process state seq counter to UidRecord.

This seq counter is associated with the process state in UidRecord
and will be incremented whenever the uid state is going from
background to foreground or vice versa.

Bug: 27803922
Test: runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services
Change-Id: I1183d929bc7e0b2c9912de3822eb344d2bb0dcf7
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7a3326b..03d2e0b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -47,6 +47,8 @@
 import static android.os.Process.PROC_OUT_LONG;
 import static android.os.Process.PROC_PARENS;
 import static android.os.Process.PROC_SPACE_TERM;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
 import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
 import static android.provider.Settings.Global.DEBUG_APP;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
@@ -319,6 +321,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.DumpHeapActivity;
 import com.android.internal.app.IAppOpsCallback;
@@ -1440,6 +1443,17 @@
 
     private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet();
 
+    /**
+     * A global counter for generating sequence numbers.
+     * This value will be used when incrementing sequence numbers in individual uidRecords.
+     *
+     * Having a global counter ensures that seq numbers are monotonically increasing for a
+     * particular uid even when the uidRecord is re-created.
+     */
+    @GuardedBy("this")
+    @VisibleForTesting
+    long mProcStateSeqCounter = 0;
+
     static final class ProcessChangeItem {
         static final int CHANGE_ACTIVITIES = 1<<0;
         int changes;
@@ -2603,6 +2617,33 @@
         }
     }
 
+    @VisibleForTesting
+    public ActivityManagerService() {
+        GL_ES_VERSION = 0;
+        mActivityStarter = null;
+        mAppErrors = null;
+        mAppOpsService = null;
+        mBatteryStatsService = null;
+        mCompatModePackages = null;
+        mConstants = null;
+        mGrantFile = null;
+        mHandler = null;
+        mHandlerThread = null;
+        mIntentFirewall = null;
+        mKeyguardController = null;
+        mPermissionReviewRequired = false;
+        mProcessCpuThread = null;
+        mProcessStats = null;
+        mProviderMap = null;
+        mRecentTasks = null;
+        mServices = null;
+        mStackSupervisor = null;
+        mSystemThread = null;
+        mTaskChangeNotificationController = null;
+        mUiHandler = null;
+        mUserController = null;
+    }
+
     // Note: This method is invoked on the main thread but may need to attach various
     // handlers to other threads.  So take care to be explicit about the looper.
     public ActivityManagerService(Context systemContext) {
@@ -21736,6 +21777,10 @@
             }
         }
 
+        for (int i = mActiveUids.size() - 1; i >= 0; --i) {
+            incrementProcStateSeqIfNeeded(mActiveUids.valueAt(i));
+        }
+
         mNumServiceProcs = mNewNumServiceProcs;
 
         // Now determine the memory trimming level of background processes.
@@ -22085,6 +22130,42 @@
         }
     }
 
+    /**
+     * If {@link UidRecord#curProcStateSeq} needs to be updated, then increments the global seq
+     * counter {@link #mProcStateSeqCounter} and uses that value for {@param uidRec}.
+     */
+    @VisibleForTesting
+    void incrementProcStateSeqIfNeeded(UidRecord uidRec) {
+        if (uidRec.curProcState != uidRec.setProcState && shouldIncrementProcStateSeq(uidRec)) {
+            uidRec.curProcStateSeq = ++mProcStateSeqCounter;
+        }
+    }
+
+    /**
+     * Checks if {@link UidRecord#curProcStateSeq} needs to be incremented depending on whether
+     * the uid is coming from background to foreground state or vice versa.
+     *
+     * @return Returns true if the uid is coming from background to foreground state or vice versa,
+     *                 false otherwise.
+     */
+    @VisibleForTesting
+    boolean shouldIncrementProcStateSeq(UidRecord uidRec) {
+        final boolean isAllowedOnRestrictBackground
+                = isProcStateAllowedWhileOnRestrictBackground(uidRec.curProcState);
+        final boolean isAllowedOnDeviceIdleOrPowerSaveMode
+                = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.curProcState);
+
+        final boolean wasAllowedOnRestrictBackground
+                = isProcStateAllowedWhileOnRestrictBackground(uidRec.setProcState);
+        final boolean wasAllowedOnDeviceIdleOrPowerSaveMode
+                = isProcStateAllowedWhileIdleOrPowerSaveMode(uidRec.setProcState);
+
+        // If the uid is coming from background to foreground or vice versa,
+        // then return true. Otherwise false.
+        return (wasAllowedOnDeviceIdleOrPowerSaveMode != isAllowedOnDeviceIdleOrPowerSaveMode)
+                || (wasAllowedOnRestrictBackground != isAllowedOnRestrictBackground);
+    }
+
     final void runInBackgroundDisabled(int uid) {
         synchronized (this) {
             UidRecord uidRec = mActiveUids.get(uid);
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 302f628..f15543a 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -34,6 +34,12 @@
     boolean setWhitelist;
     boolean idle;
     int numProcs;
+    /**
+     * Sequence number associated with the {@link #curProcState}. This is incremented using
+     * {@link ActivityManagerService#mProcStateSeqCounter}
+     * when {@link #curProcState} changes from background to foreground or vice versa.
+     */
+    long curProcStateSeq;
 
     static final int CHANGE_PROCSTATE = 0;
     static final int CHANGE_GONE = 1;
@@ -83,6 +89,8 @@
         }
         sb.append(" procs:");
         sb.append(numProcs);
+        sb.append(" curProcStateSeq:");
+        sb.append(curProcStateSeq);
         sb.append("}");
         return sb.toString();
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
new file mode 100644
index 0000000..cc2f7d5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.am;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.util.DebugUtils.valueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Function;
+
+/**
+ * Test class for {@link ActivityManagerService}.
+ *
+ * To run the tests, use
+ *
+ * runtest -c com.android.server.am.ActivityManagerServiceTest frameworks-services
+ *
+ * or the following steps:
+ *
+ * Build: m FrameworksServicesTests
+ * Install: adb install -r \
+ *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ * Run: adb shell am instrument -e class com.android.server.am.ActivityManagerServiceTest -w \
+ *     com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ActivityManagerServiceTest {
+    private static final int TEST_UID = 111;
+
+    @Test
+    public void testIncrementProcStateSeqIfNeeded() {
+        final ActivityManagerService ams = new ActivityManagerService();
+        final UidRecord uidRec = new UidRecord(TEST_UID);
+
+        assertEquals("Initially global seq counter should be 0", 0, ams.mProcStateSeqCounter);
+        assertEquals("Initially seq counter in uidRecord should be 0", 0, uidRec.curProcStateSeq);
+
+        // Uid state is not moving from background to foreground or vice versa.
+        uidRec.setProcState = PROCESS_STATE_TOP;
+        uidRec.curProcState = PROCESS_STATE_TOP;
+        ams.incrementProcStateSeqIfNeeded(uidRec);
+        assertEquals(0, ams.mProcStateSeqCounter);
+        assertEquals(0, uidRec.curProcStateSeq);
+
+        // Uid state is moving from foreground to background.
+        uidRec.curProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+        uidRec.setProcState = PROCESS_STATE_SERVICE;
+        ams.incrementProcStateSeqIfNeeded(uidRec);
+        assertEquals(1, ams.mProcStateSeqCounter);
+        assertEquals(1, uidRec.curProcStateSeq);
+
+        // Explicitly setting the seq counter for more verification.
+        ams.mProcStateSeqCounter = 42;
+
+        // Uid state is not moving from background to foreground or vice versa.
+        uidRec.setProcState = PROCESS_STATE_IMPORTANT_BACKGROUND;
+        uidRec.curProcState = PROCESS_STATE_IMPORTANT_FOREGROUND;
+        ams.incrementProcStateSeqIfNeeded(uidRec);
+        assertEquals(42, ams.mProcStateSeqCounter);
+        assertEquals(1, uidRec.curProcStateSeq);
+
+        // Uid state is moving from background to foreground.
+        uidRec.setProcState = PROCESS_STATE_LAST_ACTIVITY;
+        uidRec.curProcState = PROCESS_STATE_TOP;
+        ams.incrementProcStateSeqIfNeeded(uidRec);
+        assertEquals(43, ams.mProcStateSeqCounter);
+        assertEquals(43, uidRec.curProcStateSeq);
+    }
+
+    @Test
+    public void testShouldIncrementProcStateSeq() {
+        final ActivityManagerService ams = new ActivityManagerService();
+        final UidRecord uidRec = new UidRecord(TEST_UID);
+
+        final String error1 = "Seq should be incremented: prevState: %s, curState: %s";
+        final String error2 = "Seq should not be incremented: prevState: %s, curState: %s";
+        Function<String, String> errorMsg = errorTemplate -> {
+            return String.format(errorTemplate,
+                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.setProcState),
+                    valueToString(ActivityManager.class, "PROCESS_STATE_", uidRec.curProcState));
+        };
+
+        // No change in uid state
+        uidRec.setProcState = PROCESS_STATE_RECEIVER;
+        uidRec.curProcState = PROCESS_STATE_RECEIVER;
+        assertFalse(errorMsg.apply(error2), ams.shouldIncrementProcStateSeq(uidRec));
+
+        // Foreground to foreground
+        uidRec.setProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+        uidRec.curProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+        assertFalse(errorMsg.apply(error2), ams.shouldIncrementProcStateSeq(uidRec));
+
+        // Background to background
+        uidRec.setProcState = PROCESS_STATE_CACHED_ACTIVITY;
+        uidRec.curProcState = PROCESS_STATE_CACHED_EMPTY;
+        assertFalse(errorMsg.apply(error2), ams.shouldIncrementProcStateSeq(uidRec));
+
+        // Background to background
+        uidRec.setProcState = PROCESS_STATE_NONEXISTENT;
+        uidRec.curProcState = PROCESS_STATE_CACHED_ACTIVITY;
+        assertFalse(errorMsg.apply(error2), ams.shouldIncrementProcStateSeq(uidRec));
+
+        // Background to foreground
+        uidRec.setProcState = PROCESS_STATE_SERVICE;
+        uidRec.curProcState = PROCESS_STATE_FOREGROUND_SERVICE;
+        assertTrue(errorMsg.apply(error1), ams.shouldIncrementProcStateSeq(uidRec));
+
+        // Foreground to background
+        uidRec.setProcState = PROCESS_STATE_TOP;
+        uidRec.curProcState = PROCESS_STATE_LAST_ACTIVITY;
+        assertTrue(errorMsg.apply(error1), ams.shouldIncrementProcStateSeq(uidRec));
+    }
+}
\ No newline at end of file