1 /*
2  * Copyright (C) 2021 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 package com.android.server.am;
17 
18 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
20 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
21 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
22 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
23 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
24 
25 import static org.junit.Assert.assertEquals;
26 import static org.mockito.ArgumentMatchers.any;
27 import static org.mockito.ArgumentMatchers.anyBoolean;
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.ArgumentMatchers.anyLong;
30 import static org.mockito.Mockito.doCallRealMethod;
31 import static org.mockito.Mockito.doNothing;
32 import static org.mockito.Mockito.mock;
33 
34 import android.app.compat.CompatChanges;
35 import android.content.ComponentName;
36 import android.content.pm.ApplicationInfo;
37 import android.content.pm.ServiceInfo;
38 import android.os.SystemClock;
39 import android.util.ArraySet;
40 
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import org.junit.After;
44 import org.junit.Assert;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 import org.mockito.MockitoSession;
49 import org.mockito.quality.Strictness;
50 
51 import java.lang.reflect.Field;
52 import java.lang.reflect.Modifier;
53 import java.util.ArrayList;
54 
55 @RunWith(AndroidJUnit4.class)
56 public class ActiveServicesTest {
57 
58     private static final long DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN = 10 * 1000;
59     private static final long[] DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = {
60             0,
61             DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN,
62             DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN * 2,
63             DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN * 3
64     };
65 
66     private MockitoSession mMockingSession;
67     private ActivityManagerService mService;
68     private ActiveServices mActiveServices;
69     private AppProfiler mProfiler;
70 
71     @Before
setUp()72     public void setUp() {
73         mMockingSession = mockitoSession()
74                 .initMocks(this)
75                 .strictness(Strictness.LENIENT)
76                 .mockStatic(CompatChanges.class)
77                 .startMocking();
78         prepareTestRescheduleServiceRestarts();
79     }
80 
81     @After
tearDown()82     public void tearDown() {
83         if (mMockingSession != null) {
84             mMockingSession.finishMocking();
85         }
86     }
87 
88     @SuppressWarnings("GuardedBy")
89     @Test
testRescheduleServiceRestartsOnChanges()90     public void testRescheduleServiceRestartsOnChanges() throws Exception {
91         final long now = SystemClock.uptimeMillis();
92         final long btwn = mService.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
93         final long rd0 = 0;
94         final long rd1 = 1000;
95         final long rd2 = rd1 + btwn;
96         final long rd3 = rd2 + btwn;
97         final long rd4 = rd3 + btwn * 10;
98         final long rd5 = rd4 + btwn;
99         int memFactor = ADJ_MEM_FACTOR_MODERATE;
100         when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
101         fillInRestartingServices(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
102 
103         // Test enable/disable.
104         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(false, true, now);
105         long extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
106         verifyDelays(now, new long[] {rd0, extra, btwn + extra * 2, btwn * 2 + extra * 3, rd4,
107                 rd5 + extra});
108         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(true, false, now);
109         verifyDelays(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
110 
111         final long elapsed = 10;
112         final long now2 = now + elapsed;
113         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(false, true, now2);
114         verifyDelays(now2, new long[] {rd0 - elapsed, extra - elapsed,
115                 btwn + extra * 2 - elapsed, btwn * 2 + extra * 3 - elapsed, rd4 - elapsed,
116                 rd5 + extra - elapsed});
117 
118         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(true, false, now2);
119         verifyDelays(now2, new long[] {rd0 - elapsed, rd1 - elapsed, rd2 - elapsed, rd3 - elapsed,
120                 rd4 - elapsed, rd5 - elapsed});
121 
122         // Test memory level changes.
123         memFactor = ADJ_MEM_FACTOR_LOW;
124         when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
125         extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
126         final long elapsed3 = elapsed * 2;
127         final long now3 = now + elapsed3;
128         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
129                 ADJ_MEM_FACTOR_MODERATE, memFactor, "test", now3);
130         verifyDelays(now3, new long[] {rd0 - elapsed3, extra - elapsed3,
131                 btwn + extra * 2 - elapsed3, btwn * 2 + extra * 3 - elapsed3, rd4 - elapsed3,
132                 rd5 + extra - elapsed3});
133 
134         memFactor = ADJ_MEM_FACTOR_CRITICAL;
135         when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
136         extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
137         final long elapsed4 = elapsed * 3;
138         final long now4 = now + elapsed4;
139         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
140                 ADJ_MEM_FACTOR_LOW, memFactor, "test", now4);
141         verifyDelays(now4, new long[] {rd0 - elapsed4, extra - elapsed4,
142                 btwn + extra * 2 - elapsed4, btwn * 2 + extra * 3 - elapsed4,
143                 btwn * 3 + extra * 4 - elapsed4, btwn * 4 + extra * 5 - elapsed4});
144 
145         memFactor = ADJ_MEM_FACTOR_MODERATE;
146         when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
147         extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
148         final long elapsed5 = elapsed * 4;
149         final long now5 = now + elapsed5;
150         mActiveServices.rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
151                 ADJ_MEM_FACTOR_CRITICAL, memFactor, "test", now5);
152         verifyDelays(now5, new long[] {rd0 - elapsed5, extra - elapsed5,
153                 btwn + extra * 2 - elapsed5, btwn * 2 + extra * 3 - elapsed5,
154                 rd4 - elapsed5, rd5 + extra - elapsed5});
155     }
156 
157     @SuppressWarnings("GuardedBy")
158     @Test
testRescheduleServiceRestartsOnOtherChanges()159     public void testRescheduleServiceRestartsOnOtherChanges() throws Exception {
160         final long now = SystemClock.uptimeMillis();
161         final long btwn = mService.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN;
162         final long rd0 = 1000;
163         final long rd1 = 2000;
164         final long rd2 = btwn * 10;
165         final long rd3 = 5000;
166         final long rd4 = btwn * 11 + 5000;
167         final long rd5 = 3000;
168         int memFactor = ADJ_MEM_FACTOR_CRITICAL;
169         long extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
170         when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
171 
172         fillInRestartingServices(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
173         setNextRestarts(now, new long[] {extra, btwn + extra * 2, btwn * 2 + extra * 3,
174                 btwn * 3 + extra * 4, btwn * 4 + extra * 5, btwn * 5 + extra * 6});
175         mActiveServices.mRestartingServices.remove(1);
176         mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
177         verifyDelays(now, new long[] {extra, rd2, rd2 + btwn +  extra,
178                 rd2 + (btwn + extra) * 2, rd2 + (btwn + extra) * 3});
179         mActiveServices.mRestartingServices.remove(0);
180         mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
181         verifyDelays(now, new long[] {extra, rd2, rd2 + btwn + extra, rd2 + (btwn + extra) * 2});
182         mActiveServices.mRestartingServices.remove(1);
183         mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
184         verifyDelays(now, new long[] {extra, btwn + extra * 2, rd4});
185 
186         fillInRestartingServices(now, new long[] {rd0, rd1, rd2, rd3, rd4, rd5});
187         setNextRestarts(now, new long[] {extra, btwn + extra * 2, btwn * 2 + extra * 3,
188                 btwn * 3 + extra * 4, btwn * 4 + extra * 5, btwn * 5 + extra * 6});
189         mActiveServices.mRestartingServices.remove(1);
190         mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
191         memFactor = ADJ_MEM_FACTOR_LOW;
192         extra = mService.mConstants.mExtraServiceRestartDelayOnMemPressure[memFactor];
193         when(mService.mAppProfiler.getLastMemoryLevelLocked()).thenReturn(memFactor);
194         mActiveServices.rescheduleServiceRestartIfPossibleLocked(extra, btwn, "test", now);
195         verifyDelays(now, new long[] {extra, btwn + extra * 2, rd2,
196                 rd2 + btwn + extra, rd2 + (btwn + extra) * 2});
197     }
198 
199     @Test
testGetProcessNameForService()200     public void testGetProcessNameForService() throws Exception {
201         // Regular service
202         final ServiceInfo regularService = new ServiceInfo();
203         regularService.processName = "com.foo";
204         String processName = ActiveServices.getProcessNameForService(regularService, null, null,
205                 null, false, false);
206         assertEquals("com.foo", processName);
207 
208         // Isolated service
209         final ServiceInfo isolatedService = new ServiceInfo();
210         isolatedService.processName = "com.foo";
211         isolatedService.flags = ServiceInfo.FLAG_ISOLATED_PROCESS;
212         final ComponentName component = new ComponentName("com.foo", "barService");
213         processName = ActiveServices.getProcessNameForService(isolatedService, component,
214                 null, null, false, false);
215         assertEquals("com.foo:barService", processName);
216 
217         // Isolated service in shared isolated process
218         final ServiceInfo isolatedServiceShared1 = new ServiceInfo();
219         isolatedServiceShared1.flags = ServiceInfo.FLAG_ISOLATED_PROCESS;
220         final String instanceName = "pool";
221         final String callingPackage = "com.foo";
222         final String sharedIsolatedProcessName1 = ActiveServices.getProcessNameForService(
223                 isolatedServiceShared1, null, callingPackage, instanceName, false, true);
224         assertEquals("com.foo:ishared:pool", sharedIsolatedProcessName1);
225 
226         // Bind another one in the same isolated process
227         final ServiceInfo isolatedServiceShared2 = new ServiceInfo(isolatedServiceShared1);
228         final String sharedIsolatedProcessName2 = ActiveServices.getProcessNameForService(
229                 isolatedServiceShared2, null, callingPackage, instanceName, false, true);
230         assertEquals(sharedIsolatedProcessName1, sharedIsolatedProcessName2);
231 
232         // Simulate another app trying to do the bind
233         final ServiceInfo isolatedServiceShared3 = new ServiceInfo(isolatedServiceShared1);
234         final String otherCallingPackage = "com.bar";
235         final String sharedIsolatedProcessName3 = ActiveServices.getProcessNameForService(
236                 isolatedServiceShared3, null, otherCallingPackage, instanceName, false, true);
237         Assert.assertNotEquals(sharedIsolatedProcessName2, sharedIsolatedProcessName3);
238     }
239 
prepareTestRescheduleServiceRestarts()240     private void prepareTestRescheduleServiceRestarts() {
241         mService = mock(ActivityManagerService.class);
242         mService.mConstants = mock(ActivityManagerConstants.class);
243         mService.mConstants.mEnableExtraServiceRestartDelayOnMemPressure = true;
244         mService.mConstants.mExtraServiceRestartDelayOnMemPressure =
245                 DEFAULT_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE;
246         mService.mConstants.SERVICE_MIN_RESTART_TIME_BETWEEN =
247                 DEFAULT_SERVICE_MIN_RESTART_TIME_BETWEEN;
248         mProfiler = mock(AppProfiler.class);
249         setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", mProfiler);
250         when(mProfiler.getLastMemoryLevelLocked()).thenReturn(ADJ_MEM_FACTOR_NORMAL);
251         mActiveServices = mock(ActiveServices.class);
252         setFieldValue(ActiveServices.class, mActiveServices, "mAm", mService);
253         setFieldValue(ActiveServices.class, mActiveServices, "mRestartingServices",
254                 new ArrayList<>());
255         setFieldValue(ActiveServices.class, mActiveServices, "mRestartBackoffDisabledPackages",
256                 new ArraySet<>());
257         doNothing().when(mActiveServices).performScheduleRestartLocked(any(ServiceRecord.class),
258                 any(String.class), any(String.class), anyLong());
259         doCallRealMethod().when(mActiveServices)
260                 .rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
261                         anyBoolean(), anyBoolean(), anyLong());
262         doCallRealMethod().when(mActiveServices)
263                 .rescheduleServiceRestartOnMemoryPressureIfNeededLocked(
264                         anyInt(), anyInt(), any(String.class), anyLong());
265         doCallRealMethod().when(mActiveServices)
266                 .rescheduleServiceRestartIfPossibleLocked(
267                         anyLong(), anyLong(), any(String.class), anyLong());
268         doCallRealMethod().when(mActiveServices)
269                 .performRescheduleServiceRestartOnMemoryPressureLocked(
270                         anyLong(), anyLong(), any(String.class), anyLong());
271         doCallRealMethod().when(mActiveServices).getExtraRestartTimeInBetweenLocked();
272         doCallRealMethod().when(mActiveServices)
273                 .isServiceRestartBackoffEnabledLocked(any(String.class));
274     }
275 
setFieldValue(Class clazz, Object obj, String fieldName, T val)276     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
277         try {
278             Field field = clazz.getDeclaredField(fieldName);
279             field.setAccessible(true);
280             Field mfield = Field.class.getDeclaredField("accessFlags");
281             mfield.setAccessible(true);
282             mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
283             field.set(obj, val);
284         } catch (NoSuchFieldException | IllegalAccessException e) {
285         }
286     }
287 
fillInRestartingServices(long now, long[] delays)288     private void fillInRestartingServices(long now, long[] delays) {
289         mActiveServices.mRestartingServices.clear();
290         for (int i = 0; i < delays.length; i++) {
291             mActiveServices.mRestartingServices.add(
292                     createRestartingService("testpackage" + i, now, delays[i]));
293         }
294     }
295 
setNextRestarts(long now, long[] nextRestartDelays)296     private void setNextRestarts(long now, long[] nextRestartDelays) {
297         for (int i = 0; i < nextRestartDelays.length; i++) {
298             final ServiceRecord r = mActiveServices.mRestartingServices.get(i);
299             r.restartDelay = nextRestartDelays[i];
300             r.nextRestartTime = now + r.restartDelay;
301         }
302     }
303 
createRestartingService(String packageName, long now, long delay)304     private ServiceRecord createRestartingService(String packageName, long now, long delay) {
305         final ServiceRecord r = mock(ServiceRecord.class);
306         r.appInfo = new ApplicationInfo();
307         r.appInfo.flags = delay == 0 ? ApplicationInfo.FLAG_PERSISTENT : 0;
308         final ServiceInfo si = new ServiceInfo();
309         setFieldValue(ServiceRecord.class, r, "serviceInfo", si);
310         setFieldValue(ServiceRecord.class, r, "packageName", packageName);
311         si.applicationInfo = r.appInfo;
312         r.nextRestartTime = r.mEarliestRestartTime = now + delay;
313         r.mRestartSchedulingTime = now;
314         r.restartDelay = delay;
315         return r;
316     }
317 
verifyDelays(long now, long[] delays)318     private void verifyDelays(long now, long[] delays) throws Exception {
319         for (int i = 0; i < delays.length; i++) {
320             final ServiceRecord r = mActiveServices.mRestartingServices.get(i);
321             assertEquals("Expected restart delay=" + delays[i],
322                     Math.max(0, delays[i]), r.restartDelay);
323             assertEquals("Expected next restart time=" + (now + delays[i]),
324                     now + delays[i], r.nextRestartTime);
325         }
326     }
327 }
328