1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.am;
18 
19 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
20 
21 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertNotNull;
25 import static org.mockito.Mockito.doReturn;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.spy;
28 
29 import android.app.ActivityManager;
30 import android.app.usage.UsageStatsManagerInternal;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.pm.ApplicationInfo;
34 import android.content.pm.PackageManagerInternal;
35 
36 import com.android.server.LocalServices;
37 import com.android.server.wm.ActivityTaskManagerService;
38 
39 import org.junit.AfterClass;
40 import org.junit.Before;
41 import org.junit.BeforeClass;
42 import org.junit.Test;
43 
44 import java.lang.reflect.Field;
45 import java.lang.reflect.Modifier;
46 
47 /**
48  * Test class for {@link OomAdjuster}.
49  *
50  * Build/Install/Run:
51  *  atest FrameworksServicesTests:OomAdjusterTests
52  */
53 public class OomAdjusterTests {
54     private static Context sContext;
55     private static ActivityManagerService sService;
56     private static PackageManagerInternal sPackageManagerInternal;
57 
58     private ProcessRecord mProcessRecord;
59 
60     private static final long ZERO = 0L;
61     private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L;
62     private static final long SERVICE_USAGE_INTERACTION = 60 * 1000;
63 
64     static class MyOomAdjuster extends OomAdjuster {
65 
MyOomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids)66         MyOomAdjuster(ActivityManagerService service, ProcessList processList,
67                 ActiveUids activeUids) {
68             super(service, processList, activeUids);
69         }
70 
71         @Override
isChangeEnabled(int changeId, ApplicationInfo app, boolean defaultValue)72         protected boolean isChangeEnabled(int changeId, ApplicationInfo app,
73                 boolean defaultValue) {
74             return true;
75         }
76     }
77 
78     @BeforeClass
setUpOnce()79     public static void setUpOnce() {
80         sContext = getInstrumentation().getTargetContext();
81 
82         sPackageManagerInternal = mock(PackageManagerInternal.class);
83         doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
84                 .getSystemUiServiceComponent();
85         LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
86 
87         // We need to run with dexmaker share class loader to make use of
88         // ActivityTaskManagerService from wm package.
89         runWithDexmakerShareClassLoader(() -> {
90             sService = mock(ActivityManagerService.class);
91             sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
92             sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
93             sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
94 
95             setFieldValue(ActivityManagerService.class, sService, "mProcLock",
96                     new ActivityManagerProcLock());
97             sService.mConstants = new ActivityManagerConstants(sContext, sService,
98                     sContext.getMainThreadHandler());
99             final AppProfiler profiler = mock(AppProfiler.class);
100             setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
101             setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
102             sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null);
103             LocalServices.addService(UsageStatsManagerInternal.class,
104                     mock(UsageStatsManagerInternal.class));
105             sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
106         });
107     }
108 
setFieldValue(Class clazz, Object obj, String fieldName, T val)109     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
110         try {
111             Field field = clazz.getDeclaredField(fieldName);
112             field.setAccessible(true);
113             Field mfield = Field.class.getDeclaredField("accessFlags");
114             mfield.setAccessible(true);
115             mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
116             field.set(obj, val);
117         } catch (NoSuchFieldException | IllegalAccessException e) {
118         }
119     }
120 
121     @AfterClass
tearDownOnce()122     public static void tearDownOnce() {
123         LocalServices.removeServiceForTest(PackageManagerInternal.class);
124         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
125     }
126 
127     @Before
setUpProcess()128     public void setUpProcess() {
129         // Need to run with dexmaker share class loader to mock package private class.
130         runWithDexmakerShareClassLoader(() -> {
131             mProcessRecord = spy(new ProcessRecord(sService, sContext.getApplicationInfo(),
132                     "name", 12345));
133         });
134 
135         // Ensure certain services and constants are defined properly
136         assertNotNull(sService.mUsageStatsService);
137         assertEquals(USAGE_STATS_INTERACTION,
138                 sService.mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S);
139         assertEquals(SERVICE_USAGE_INTERACTION,
140                 sService.mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S);
141     }
142 
143     @Test
testMaybeUpdateUsageStats_ProcStatePersistentUI()144     public void testMaybeUpdateUsageStats_ProcStatePersistentUI() {
145         final long elapsedTime = ZERO;
146         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
147         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
148 
149         assertProcessRecordState(ZERO, true, elapsedTime);
150     }
151 
152     @Test
testMaybeUpdateUsageStats_ProcStateTop()153     public void testMaybeUpdateUsageStats_ProcStateTop() {
154         final long elapsedTime = ZERO;
155         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
156         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
157 
158         assertProcessRecordState(ZERO, true, elapsedTime);
159     }
160 
161     @Test
testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction()162     public void testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction() {
163         final long elapsedTime = ZERO;
164         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
165         mProcessRecord.mState.setReportedInteraction(true);
166         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
167 
168         assertProcessRecordState(ZERO, true, ZERO);
169     }
170 
171     @Test
testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval()172     public void testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval() {
173         final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
174         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
175         mProcessRecord.mState.setReportedInteraction(true);
176         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
177 
178         assertProcessRecordState(ZERO, true, elapsedTime);
179     }
180 
181     @Test
testMaybeUpdateUsageStats_ProcStateBoundTop()182     public void testMaybeUpdateUsageStats_ProcStateBoundTop() {
183         final long elapsedTime = ZERO;
184         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
185         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
186 
187         assertProcessRecordState(ZERO, true, elapsedTime);
188     }
189 
190     @Test
testMaybeUpdateUsageStats_ProcStateFGS()191     public void testMaybeUpdateUsageStats_ProcStateFGS() {
192         final long elapsedTime = ZERO;
193         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
194         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
195 
196         assertProcessRecordState(elapsedTime, false, ZERO);
197     }
198 
199     @Test
testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction()200     public void testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction() {
201         final long elapsedTime = ZERO;
202         final long fgInteractionTime = 1000L;
203         mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
204         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
205         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
206 
207         assertProcessRecordState(fgInteractionTime, false, ZERO);
208     }
209 
210     @Test
testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction()211     public void testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction() {
212         final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
213         final long fgInteractionTime = 1000L;
214         mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
215         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
216         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
217 
218         assertProcessRecordState(fgInteractionTime, true, elapsedTime);
219     }
220 
221     @Test
testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction()222     public void testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction() {
223         final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
224         final long fgInteractionTime = 1000L;
225         mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
226         mProcessRecord.mState.setReportedInteraction(true);
227         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
228         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
229 
230         assertProcessRecordState(fgInteractionTime, true, ZERO);
231     }
232 
233     @Test
testMaybeUpdateUsageStats_ProcStateFGSLocation()234     public void testMaybeUpdateUsageStats_ProcStateFGSLocation() {
235         final long elapsedTime = ZERO;
236         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
237         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
238 
239         assertProcessRecordState(elapsedTime, false, ZERO);
240     }
241 
242     @Test
testMaybeUpdateUsageStats_ProcStateBFGS()243     public void testMaybeUpdateUsageStats_ProcStateBFGS() {
244         final long elapsedTime = ZERO;
245         mProcessRecord.mState.setCurProcState(
246                 ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
247         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
248 
249         assertProcessRecordState(ZERO, true, elapsedTime);
250     }
251 
252     @Test
testMaybeUpdateUsageStats_ProcStateImportantFG()253     public void testMaybeUpdateUsageStats_ProcStateImportantFG() {
254         final long elapsedTime = ZERO;
255         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
256         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
257 
258         assertProcessRecordState(ZERO, true, elapsedTime);
259     }
260 
261     @Test
testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction()262     public void testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction() {
263         final long elapsedTime = ZERO;
264         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
265         mProcessRecord.mState.setReportedInteraction(true);
266         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
267 
268         assertProcessRecordState(ZERO, true, ZERO);
269     }
270 
271     @Test
testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval()272     public void testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval() {
273         final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
274         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
275         mProcessRecord.mState.setReportedInteraction(true);
276         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
277 
278         assertProcessRecordState(ZERO, true, elapsedTime);
279     }
280 
281     @Test
testMaybeUpdateUsageStats_ProcStateImportantBG()282     public void testMaybeUpdateUsageStats_ProcStateImportantBG() {
283         final long elapsedTime = ZERO;
284         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
285         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
286 
287         assertProcessRecordState(ZERO, false, ZERO);
288     }
289 
290     @Test
testMaybeUpdateUsageStats_ProcStateService()291     public void testMaybeUpdateUsageStats_ProcStateService() {
292         final long elapsedTime = ZERO;
293         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
294         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
295 
296         assertProcessRecordState(ZERO, false, ZERO);
297     }
298 
assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction, long interactionEventTime)299     private void assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction,
300             long interactionEventTime) {
301         assertEquals("Foreground interaction time was not updated correctly.",
302                 fgInteractionTime, mProcessRecord.mState.getFgInteractionTime());
303         assertEquals("Interaction was not updated correctly.",
304                 reportedInteraction, mProcessRecord.mState.hasReportedInteraction());
305         assertEquals("Interaction event time was not updated correctly.",
306                 interactionEventTime, mProcessRecord.mState.getInteractionEventTime());
307     }
308 }
309