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     @BeforeClass
setUpOnce()65     public static void setUpOnce() {
66         sContext = getInstrumentation().getTargetContext();
67 
68         sPackageManagerInternal = mock(PackageManagerInternal.class);
69         doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
70                 .getSystemUiServiceComponent();
71         LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
72 
73         // We need to run with dexmaker share class loader to make use of
74         // ActivityTaskManagerService from wm package.
75         runWithDexmakerShareClassLoader(() -> {
76             sService = mock(ActivityManagerService.class);
77             sService.mActivityTaskManager = new ActivityTaskManagerService(sContext);
78             sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper());
79             sService.mAtmInternal = sService.mActivityTaskManager.getAtmInternal();
80 
81             setFieldValue(ActivityManagerService.class, sService, "mProcLock",
82                     new ActivityManagerProcLock());
83             sService.mConstants = new ActivityManagerConstants(sContext, sService,
84                     sContext.getMainThreadHandler());
85             final AppProfiler profiler = mock(AppProfiler.class);
86             setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object());
87             setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler);
88             final OomAdjuster.Injector injector = new OomAdjuster.Injector(){
89                 @Override
90                 boolean isChangeEnabled(int changeId, ApplicationInfo app,
91                         boolean defaultValue) {
92                     return true;
93                 }
94             };
95             sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null,
96                     injector);
97             LocalServices.addService(UsageStatsManagerInternal.class,
98                     mock(UsageStatsManagerInternal.class));
99             sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class);
100         });
101     }
102 
setFieldValue(Class clazz, Object obj, String fieldName, T val)103     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
104         try {
105             Field field = clazz.getDeclaredField(fieldName);
106             field.setAccessible(true);
107             Field mfield = Field.class.getDeclaredField("accessFlags");
108             mfield.setAccessible(true);
109             mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
110             field.set(obj, val);
111         } catch (NoSuchFieldException | IllegalAccessException e) {
112         }
113     }
114 
115     @AfterClass
tearDownOnce()116     public static void tearDownOnce() {
117         LocalServices.removeServiceForTest(PackageManagerInternal.class);
118         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
119     }
120 
121     @Before
setUpProcess()122     public void setUpProcess() {
123         // Need to run with dexmaker share class loader to mock package private class.
124         runWithDexmakerShareClassLoader(() -> {
125             mProcessRecord = spy(new ProcessRecord(sService, sContext.getApplicationInfo(),
126                     "name", 12345));
127         });
128 
129         // Ensure certain services and constants are defined properly
130         assertNotNull(sService.mUsageStatsService);
131         assertEquals(USAGE_STATS_INTERACTION,
132                 sService.mConstants.USAGE_STATS_INTERACTION_INTERVAL_POST_S);
133         assertEquals(SERVICE_USAGE_INTERACTION,
134                 sService.mConstants.SERVICE_USAGE_INTERACTION_TIME_POST_S);
135     }
136 
137     @Test
testMaybeUpdateUsageStats_ProcStatePersistentUI()138     public void testMaybeUpdateUsageStats_ProcStatePersistentUI() {
139         final long elapsedTime = ZERO;
140         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI);
141         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
142 
143         assertProcessRecordState(ZERO, true, elapsedTime);
144     }
145 
146     @Test
testMaybeUpdateUsageStats_ProcStateTop()147     public void testMaybeUpdateUsageStats_ProcStateTop() {
148         final long elapsedTime = ZERO;
149         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
150         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
151 
152         assertProcessRecordState(ZERO, true, elapsedTime);
153     }
154 
155     @Test
testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction()156     public void testMaybeUpdateUsageStats_ProcStateTop_PreviousInteraction() {
157         final long elapsedTime = ZERO;
158         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
159         mProcessRecord.mState.setReportedInteraction(true);
160         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
161 
162         assertProcessRecordState(ZERO, true, ZERO);
163     }
164 
165     @Test
testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval()166     public void testMaybeUpdateUsageStats_ProcStateTop_PastUsageInterval() {
167         final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
168         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_TOP);
169         mProcessRecord.mState.setReportedInteraction(true);
170         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
171 
172         assertProcessRecordState(ZERO, true, elapsedTime);
173     }
174 
175     @Test
testMaybeUpdateUsageStats_ProcStateBoundTop()176     public void testMaybeUpdateUsageStats_ProcStateBoundTop() {
177         final long elapsedTime = ZERO;
178         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_BOUND_TOP);
179         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
180 
181         assertProcessRecordState(ZERO, true, elapsedTime);
182     }
183 
184     @Test
testMaybeUpdateUsageStats_ProcStateFGS()185     public void testMaybeUpdateUsageStats_ProcStateFGS() {
186         final long elapsedTime = ZERO;
187         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
188         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
189 
190         assertProcessRecordState(elapsedTime, false, ZERO);
191     }
192 
193     @Test
testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction()194     public void testMaybeUpdateUsageStats_ProcStateFGS_ShortInteraction() {
195         final long elapsedTime = ZERO;
196         final long fgInteractionTime = 1000L;
197         mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
198         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
199         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
200 
201         assertProcessRecordState(fgInteractionTime, false, ZERO);
202     }
203 
204     @Test
testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction()205     public void testMaybeUpdateUsageStats_ProcStateFGS_LongInteraction() {
206         final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
207         final long fgInteractionTime = 1000L;
208         mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
209         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
210         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
211 
212         assertProcessRecordState(fgInteractionTime, true, elapsedTime);
213     }
214 
215     @Test
testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction()216     public void testMaybeUpdateUsageStats_ProcStateFGS_PreviousLongInteraction() {
217         final long elapsedTime = 2 * SERVICE_USAGE_INTERACTION;
218         final long fgInteractionTime = 1000L;
219         mProcessRecord.mState.setFgInteractionTime(fgInteractionTime);
220         mProcessRecord.mState.setReportedInteraction(true);
221         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
222         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
223 
224         assertProcessRecordState(fgInteractionTime, true, ZERO);
225     }
226 
227     @Test
testMaybeUpdateUsageStats_ProcStateFGSLocation()228     public void testMaybeUpdateUsageStats_ProcStateFGSLocation() {
229         final long elapsedTime = ZERO;
230         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
231         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
232 
233         assertProcessRecordState(elapsedTime, false, ZERO);
234     }
235 
236     @Test
testMaybeUpdateUsageStats_ProcStateBFGS()237     public void testMaybeUpdateUsageStats_ProcStateBFGS() {
238         final long elapsedTime = ZERO;
239         mProcessRecord.mState.setCurProcState(
240                 ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
241         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
242 
243         assertProcessRecordState(ZERO, true, elapsedTime);
244     }
245 
246     @Test
testMaybeUpdateUsageStats_ProcStateImportantFG()247     public void testMaybeUpdateUsageStats_ProcStateImportantFG() {
248         final long elapsedTime = ZERO;
249         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
250         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
251 
252         assertProcessRecordState(ZERO, true, elapsedTime);
253     }
254 
255     @Test
testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction()256     public void testMaybeUpdateUsageStats_ProcStateImportantFG_PreviousInteraction() {
257         final long elapsedTime = ZERO;
258         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
259         mProcessRecord.mState.setReportedInteraction(true);
260         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
261 
262         assertProcessRecordState(ZERO, true, ZERO);
263     }
264 
265     @Test
testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval()266     public void testMaybeUpdateUsageStats_ProcStateImportantFG_PastUsageInterval() {
267         final long elapsedTime = 3 * USAGE_STATS_INTERACTION;
268         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
269         mProcessRecord.mState.setReportedInteraction(true);
270         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
271 
272         assertProcessRecordState(ZERO, true, elapsedTime);
273     }
274 
275     @Test
testMaybeUpdateUsageStats_ProcStateImportantBG()276     public void testMaybeUpdateUsageStats_ProcStateImportantBG() {
277         final long elapsedTime = ZERO;
278         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
279         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
280 
281         assertProcessRecordState(ZERO, false, ZERO);
282     }
283 
284     @Test
testMaybeUpdateUsageStats_ProcStateService()285     public void testMaybeUpdateUsageStats_ProcStateService() {
286         final long elapsedTime = ZERO;
287         mProcessRecord.mState.setCurProcState(ActivityManager.PROCESS_STATE_SERVICE);
288         sService.mOomAdjuster.maybeUpdateUsageStats(mProcessRecord, elapsedTime);
289 
290         assertProcessRecordState(ZERO, false, ZERO);
291     }
292 
assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction, long interactionEventTime)293     private void assertProcessRecordState(long fgInteractionTime, boolean reportedInteraction,
294             long interactionEventTime) {
295         assertEquals("Foreground interaction time was not updated correctly.",
296                 fgInteractionTime, mProcessRecord.mState.getFgInteractionTime());
297         assertEquals("Interaction was not updated correctly.",
298                 reportedInteraction, mProcessRecord.mState.hasReportedInteraction());
299         assertEquals("Interaction event time was not updated correctly.",
300                 interactionEventTime, mProcessRecord.mState.getInteractionEventTime());
301     }
302 }
303