summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Varun Shah <varunshah@google.com> 2019-04-12 10:54:36 -0700
committer Varun Shah <varunshah@google.com> 2019-04-26 11:51:46 -0700
commit334d6764f79875e2023c7482d6b27a829df34311 (patch)
treeec7525220e89e58bd5bcc6dae70bd9e671e68159
parent8cee482fdddc678f14eb9a9dd87ee337a3451492 (diff)
Update standby bucket for uninteracted foreground services.
This fixes a bug where foreground services that were started, but never interacted with, were not transitioning to the Active bucket after 30mins - a defined interval. Also created unit tests for #maybeUpdateUsageStats(). Test steps: (adb shell commands are generic) 1) Install a test app which has a foreground service * can install test app provided in bug via adb install 2) Set the bucket of the test app to Rare: $ adb shell am set-stanby-bucket com.packagename rare 3) Launch the foreground service $ adb shell am broadcast -a com.packagename.intentfilter -n com.packagename/.MyReceiver 4) Wait for approximately 30mins 5) Observe the app bucket - should be Active (10) $ adb shell am get-standby-bucket com.packagename Bug: 116189835 Test: atest OomAdjusterTests Test: manual (steps listed above) Change-Id: I6949466297cccdc349428c5f6172d65bd9d77a53
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java4
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java255
3 files changed, 270 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6da7f5fc98d8..64f94c3717a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -543,7 +543,7 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final int NATIVE_DUMP_TIMEOUT_MS = 2000; // 2 seconds;
- final OomAdjuster mOomAdjuster;
+ OomAdjuster mOomAdjuster;
final LowMemDetector mLowMemDetector;
/** All system services */
@@ -1483,7 +1483,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final ServiceThread mProcStartHandlerThread;
final Handler mProcStartHandler;
- final ActivityManagerConstants mConstants;
+ ActivityManagerConstants mConstants;
// Encapsulates the global setting "hidden_api_blacklist_exemptions"
final HiddenApiSettings mHiddenApiBlacklist;
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 8ae7c7d3e0c8..043daefe0fdb 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -75,6 +75,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessStats;
import com.android.server.LocalServices;
import com.android.server.wm.ActivityServiceConnectionsHolder;
@@ -1897,6 +1898,10 @@ public final class OomAdjuster {
// For apps that sit around for a long time in the interactive state, we need
// to report this at least once a day so they don't go idle.
maybeUpdateUsageStatsLocked(app, nowElapsed);
+ } else if (!app.reportedInteraction && (nowElapsed - app.getFgInteractionTime())
+ > mConstants.SERVICE_USAGE_INTERACTION_TIME) {
+ // For foreground services that sit around for a long time but are not interacted with.
+ maybeUpdateUsageStatsLocked(app, nowElapsed);
}
if (changes != 0) {
@@ -1917,6 +1922,14 @@ public final class OomAdjuster {
return success;
}
+ // ONLY used for unit testing in OomAdjusterTests.java
+ @VisibleForTesting
+ void maybeUpdateUsageStats(ProcessRecord app, long nowElapsed) {
+ synchronized (mService) {
+ maybeUpdateUsageStatsLocked(app, nowElapsed);
+ }
+ }
+
@GuardedBy("mService")
private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) {
if (DEBUG_USAGE_STATS) {
diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
new file mode 100644
index 000000000000..d3bcff528155
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 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.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+
+import android.app.ActivityManager;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.Context;
+
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerService;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * Test class for {@link OomAdjuster}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:OomAdjusterTests
+ */
+public class OomAdjusterTests {
+ private static Context sContext;
+ private static ActivityManagerService sService;
+
+ private ProcessRecord mProcessRecord;
+
+ private static final long ZERO = 0L;
+ private static final long USAGE_STATS_INTERACTION = 2 * 60 * 60 * 1000L;
+ private static final long SERVICE_USAGE_INTERACTION = 30 * 60 * 1000;
+
+ @BeforeClass
+ public static void setUpOnce() {
+ sContext = getInstrumentation().getTargetContext();
+
+ // We need to run with dexmaker share class loader to make use of
+ // ActivityTaskManagerService from wm package.
+ runWithDexmakerShareClassLoader(() -> {
+ sService = mock(ActivityManagerService.class);
+ sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
+ sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
+ sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
+
+ sService.mConstants = new ActivityManagerConstants(sContext, sService,
+ sContext.getMainThreadHandler());
+ sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null);
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.addService(UsageStatsManagerInternal.class,
+ mock(UsageStatsManagerInternal.class));
+ sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
+ });
+ }
+
+ @Before
+ public void setUpProcess() {
+ // Need to run with dexmaker share class loader to mock package private class.
+ runWithDexmakerShareClassLoader(() -> {
+ mProcessRecord = spy(new ProcessRecord(sService, sContext.getApplicationInfo(),
+ "name", 12345));
+ });
+
+ // Ensure certain services and constants are defined properly
+ assertNotNull(sService.mUsageStatsService);
+ assertEquals(USAGE_STATS_INTERACTION, sService.mConstants.USAGE_STATS_INTERACTION_INTERVAL);
+ assertEquals(SERVICE_USAGE_INTERACTION, sService.mConstants.SERVICE_USAGE_INTERACTION_TIME);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStatePersistentUI() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateTop() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+ mProcessRecord.reportedInteraction = true;
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval() {
+ final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
+ mProcessRecord.reportedInteraction = true;
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateBoundTop() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateFGS() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(elapsedTime, false, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction() {
+ final long elapsedTime = ZERO;
+ final long fgInteractionTime = 1000L;
+ mProcessRecord.setFgInteractionTime(fgInteractionTime);
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(fgInteractionTime, false, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction() {
+ final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
+ final long fgInteractionTime = 1000L;
+ mProcessRecord.setFgInteractionTime(fgInteractionTime);
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(fgInteractionTime, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction() {
+ final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
+ final long fgInteractionTime = 1000L;
+ mProcessRecord.setFgInteractionTime(fgInteractionTime);
+ mProcessRecord.reportedInteraction = true;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(fgInteractionTime, true, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateFGSLocation() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(elapsedTime, false, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateBFGS() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateImportantFG() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mProcessRecord.reportedInteraction = true;
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval() {
+ final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ mProcessRecord.reportedInteraction = true;
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, true, elapsedTime);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateImportantBG() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, false, ZERO);
+ }
+
+ @Test
+ public void testMaybeUpdateUsageStats_ProcStateService() {
+ final long elapsedTime = ZERO;
+ mProcessRecord.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
+ sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
+
+ assertProcessRecordState(ZERO, false, ZERO);
+ }
+
+ private void assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction,
+ long interactionEventTime) {
+ assertEquals("Foreground interaction time was not updated correctly.",
+ fgInteractionTime, mProcessRecord.getFgInteractionTime());
+ assertEquals("Interaction was not updated correctly.",
+ reportedInteraction, mProcessRecord.reportedInteraction);
+ assertEquals("Interaction event time was not updated correctly.",
+ interactionEventTime, mProcessRecord.getInteractionEventTime());
+ }
+}