Merge "Add a process state seq counter to UidRecord."
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2f2b533..d22e49b 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;
@@ -320,6 +322,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;
@@ -1441,6 +1444,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;
@@ -2606,6 +2620,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) {
@@ -21752,6 +21793,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.
@@ -22101,6 +22146,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