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