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 
17 package com.android.server.am;
18 
19 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
20 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
21 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
22 
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26 
27 import android.app.ActivityManager;
28 import android.app.ActivityManager.OnUidImportanceListener;
29 import android.app.ActivityManager.RunningAppProcessInfo;
30 import android.app.Instrumentation;
31 import android.content.ComponentName;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.ServiceConnection;
35 import android.content.pm.ApplicationInfo;
36 import android.os.IBinder;
37 import android.os.SystemClock;
38 import android.provider.DeviceConfig;
39 import android.provider.Settings;
40 import android.server.wm.settings.SettingsSession;
41 import android.util.Log;
42 
43 import androidx.test.InstrumentationRegistry;
44 import androidx.test.filters.LargeTest;
45 import androidx.test.runner.AndroidJUnit4;
46 
47 import com.android.compatibility.common.util.SystemUtil;
48 
49 import org.junit.After;
50 import org.junit.Before;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 import java.util.concurrent.CountDownLatch;
55 import java.util.concurrent.TimeUnit;
56 import java.util.regex.Matcher;
57 import java.util.regex.Pattern;
58 
59 /**
60  * Build/Install/Run:
61  *  atest FrameworksServicesTests:ServiceRestarterTest
62  */
63 @RunWith(AndroidJUnit4.class)
64 public final class ServiceRestarterTest {
65     private static final String TAG = "ServiceRestarterTest";
66 
67     private static final String TEST_PACKAGE1_NAME =
68             "com.android.servicestests.apps.simpleservicetestapp1";
69     private static final String TEST_PACKAGE2_NAME =
70             "com.android.servicestests.apps.simpleservicetestapp2";
71     private static final String TEST_PACKAGE3_NAME =
72             "com.android.servicestests.apps.simpleservicetestapp3";
73     private static final String TEST_SERVICE_NAME =
74             "com.android.servicestests.apps.simpleservicetestapp.SimpleService";
75     private static final String[] MEMORY_PRESSURE_MAP = {
76             "NORMAL", "MODERATE", "LOW", "CRITICAL"};
77     private static final String EXTRA_DELAY = "0,2000,4000,8000";
78     private static final int SERVICE_RESTART_DURATION_FACTOR = 2;
79     private static final long BOUND_SERVICE_CRASH_RESTART_DURATION = 1000;
80     private static final long SERVICE_MIN_RESTART_TIME_BETWEEN = 1000;
81 
82     private static final long WAIT_MS = 5 * 1000;
83     private static final long WAIT_LONG_MS = 30 * 1000;
84 
85     private static final int ACTION_START = 1;
86     private static final int ACTION_KILL = 2;
87     private static final int ACTION_WAIT = 4;
88     private static final int ACTION_STOPPKG = 8;
89     private static final int ACTION_ALL = ACTION_START | ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG;
90 
91     private static final String LOCAL_APK_BASE_PATH = "/data/local/tmp/cts/content/";
92     private static final String TEST_PACKAGE3_APK = "SimpleServiceTestApp3.apk";
93     private static final String ACTION_SERVICE_WITH_DEP_PKG =
94             "com.android.servicestests.apps.simpleservicetestapp.ACTION_SERVICE_WITH_DEP_PKG";
95     private static final String EXTRA_TARGET_PACKAGE = "target_package";
96 
97     private SettingsSession<String> mAMConstantsSettings;
98     private DeviceConfigSession<String> mExtraDelaysDeviceConfig;
99     private DeviceConfigSession<Boolean> mEnableExtraDelaysDeviceConfig;
100 
101     private Context mContext;
102     private Instrumentation mInstrumentation;
103     private int mTestPackage1Uid;
104     private int mTestPackage2Uid;
105     private int mTestPackage3Uid;
106 
107     @Before
setUp()108     public void setUp() throws Exception {
109         mInstrumentation = InstrumentationRegistry.getInstrumentation();
110         mContext = InstrumentationRegistry.getContext();
111         ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE1_NAME, 0);
112         mTestPackage1Uid = ai.uid;
113         ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE2_NAME, 0);
114         mTestPackage2Uid = ai.uid;
115         ai = mContext.getPackageManager().getApplicationInfo(TEST_PACKAGE3_NAME, 0);
116         mTestPackage3Uid = ai.uid;
117         final String activityManagerConstants = Settings.Global.ACTIVITY_MANAGER_CONSTANTS;
118         mAMConstantsSettings = new SettingsSession<>(
119                 Settings.Global.getUriFor(activityManagerConstants),
120                 Settings.Global::getString, Settings.Global::putString);
121         mAMConstantsSettings.set(
122                 ActivityManagerConstants.KEY_SERVICE_RESTART_DURATION_FACTOR + "="
123                 + SERVICE_RESTART_DURATION_FACTOR + ","
124                 + ActivityManagerConstants.KEY_BOUND_SERVICE_CRASH_RESTART_DURATION + "="
125                 + BOUND_SERVICE_CRASH_RESTART_DURATION + ","
126                 + ActivityManagerConstants.KEY_SERVICE_MIN_RESTART_TIME_BETWEEN + "="
127                 + SERVICE_MIN_RESTART_TIME_BETWEEN);
128         mExtraDelaysDeviceConfig = new DeviceConfigSession<>(
129                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
130                 ActivityManagerConstants.KEY_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE,
131                 DeviceConfig::getString, null);
132         mExtraDelaysDeviceConfig.set(EXTRA_DELAY);
133         mEnableExtraDelaysDeviceConfig = new DeviceConfigSession<>(
134                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
135                 ActivityManagerConstants.KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE,
136                 DeviceConfig::getBoolean, false);
137         mEnableExtraDelaysDeviceConfig.set(true);
138     }
139 
140     @After
tearDown()141     public void tearDown() throws Exception {
142         if (mAMConstantsSettings != null) {
143             mAMConstantsSettings.close();
144         }
145         if (mExtraDelaysDeviceConfig != null) {
146             mExtraDelaysDeviceConfig.close();
147         }
148         if (mEnableExtraDelaysDeviceConfig != null) {
149             mEnableExtraDelaysDeviceConfig.close();
150         }
151     }
152 
153     @LargeTest
154     @Test
testDisableServiceRestartBackoff()155     public void testDisableServiceRestartBackoff() throws Exception {
156         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
157         final MyUidImportanceListener uid1Listener1 = new MyUidImportanceListener(mTestPackage1Uid);
158         final MyUidImportanceListener uid1Listener2 = new MyUidImportanceListener(mTestPackage1Uid);
159         final MyUidImportanceListener uid2Listener1 = new MyUidImportanceListener(mTestPackage2Uid);
160         final MyUidImportanceListener uid2Listener2 = new MyUidImportanceListener(mTestPackage2Uid);
161         final MyUidImportanceListener uid3Listener1 = new MyUidImportanceListener(mTestPackage3Uid);
162         final MyUidImportanceListener uid3Listener2 = new MyUidImportanceListener(mTestPackage3Uid);
163         try {
164             am.addOnUidImportanceListener(uid1Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
165             am.addOnUidImportanceListener(uid1Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
166             am.addOnUidImportanceListener(uid2Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
167             am.addOnUidImportanceListener(uid2Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
168             am.addOnUidImportanceListener(uid3Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
169             am.addOnUidImportanceListener(uid3Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
170             executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE1_NAME);
171             executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE2_NAME);
172             executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE3_NAME);
173 
174             // Issue the command to enable service backoff policy for app2.
175             executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
176             // Test restarts in normal case
177             final long[] ts1 = startKillAndRestart(am, ACTION_START | ACTION_KILL | ACTION_WAIT,
178                     uid1Listener1, uid1Listener2, uid2Listener1, uid2Listener2,
179                     uid3Listener1, uid3Listener2, Long.MAX_VALUE, false);
180             assertTrue("app1 restart should be before app2", ts1[1] < ts1[2]);
181             assertTrue("app2 restart should be before app3", ts1[2] < ts1[3]);
182 
183             // Issue the command to disable service backoff policy for app2.
184             executeShellCmd("am service-restart-backoff disable " + TEST_PACKAGE2_NAME);
185             // Test restarts again.
186             final long[] ts2 = startKillAndRestart(am, ACTION_KILL | ACTION_WAIT | ACTION_STOPPKG,
187                     uid1Listener1, uid1Listener2, uid2Listener1,
188                     uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE, false);
189             assertTrue("app2 restart should be before app1", ts2[2] < ts2[1]);
190             assertTrue("app1 restart should be before app3", ts2[1] < ts2[3]);
191             assertTrue("app2 should be restart in a very short moment", ts2[2] - ts2[0] < WAIT_MS);
192 
193             // Issue the command to enable service backoff policy for app2.
194             executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
195             // Test restarts again.
196             final long[] ts3 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
197                     uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, Long.MAX_VALUE,
198                     false);
199             assertTrue("app1 restart should be before app2", ts3[1] < ts3[2]);
200             assertTrue("app2 restart should be before app3", ts3[2] < ts3[3]);
201 
202         } finally {
203             executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE1_NAME);
204             executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE2_NAME);
205             executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE3_NAME);
206             executeShellCmd("am service-restart-backoff enable " + TEST_PACKAGE2_NAME);
207             am.removeOnUidImportanceListener(uid1Listener1);
208             am.removeOnUidImportanceListener(uid1Listener2);
209             am.removeOnUidImportanceListener(uid2Listener1);
210             am.removeOnUidImportanceListener(uid2Listener2);
211             am.removeOnUidImportanceListener(uid3Listener1);
212             am.removeOnUidImportanceListener(uid3Listener2);
213             am.forceStopPackage(TEST_PACKAGE1_NAME);
214             am.forceStopPackage(TEST_PACKAGE2_NAME);
215             am.forceStopPackage(TEST_PACKAGE3_NAME);
216         }
217     }
218 
219     @LargeTest
220     @Test
testExtraDelaysInServiceRestartOnLowMem()221     public void testExtraDelaysInServiceRestartOnLowMem() throws Exception {
222         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
223         final MyUidImportanceListener uid1Listener1 = new MyUidImportanceListener(mTestPackage1Uid);
224         final MyUidImportanceListener uid1Listener2 = new MyUidImportanceListener(mTestPackage1Uid);
225         final MyUidImportanceListener uid2Listener1 = new MyUidImportanceListener(mTestPackage2Uid);
226         final MyUidImportanceListener uid2Listener2 = new MyUidImportanceListener(mTestPackage2Uid);
227         final MyUidImportanceListener uid3Listener1 = new MyUidImportanceListener(mTestPackage3Uid);
228         final MyUidImportanceListener uid3Listener2 = new MyUidImportanceListener(mTestPackage3Uid);
229         try {
230             am.addOnUidImportanceListener(uid1Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
231             am.addOnUidImportanceListener(uid1Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
232             am.addOnUidImportanceListener(uid2Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
233             am.addOnUidImportanceListener(uid2Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
234             am.addOnUidImportanceListener(uid3Listener1, RunningAppProcessInfo.IMPORTANCE_SERVICE);
235             am.addOnUidImportanceListener(uid3Listener2, RunningAppProcessInfo.IMPORTANCE_GONE);
236             executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE1_NAME);
237             executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE2_NAME);
238             executeShellCmd("cmd deviceidle whitelist +" + TEST_PACKAGE3_NAME);
239 
240             // Force the memory pressure to normal.
241             setMemoryPressure(ADJ_MEM_FACTOR_NORMAL);
242 
243             // Test restarts in normal condition.
244             final long[] ts1 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
245                     uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, WAIT_LONG_MS, true);
246 
247             // Mimic a memory pressure event.
248             setMemoryPressure(ADJ_MEM_FACTOR_MODERATE);
249 
250             final long[] ts2 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
251                     uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, WAIT_LONG_MS, true);
252 
253             assertTrue("Expect extra delays in service restarts",
254                     (ts2[1] - ts2[0]) > (ts1[1] - ts1[0]));
255             assertTrue("Expect extra delays in service restarts",
256                     (ts2[2] - ts2[0]) > (ts1[2] - ts1[0]));
257             assertTrue("Expect extra delays in service restarts",
258                     (ts2[3] - ts2[0]) > (ts1[3] - ts1[0]));
259 
260             // Increase the memory pressure event.
261             setMemoryPressure(ADJ_MEM_FACTOR_LOW);
262 
263             final long[] ts3 = startKillAndRestart(am, ACTION_ALL, uid1Listener1, uid1Listener2,
264                     uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2, WAIT_LONG_MS, true);
265 
266             assertTrue("Expect extra delays in service restarts",
267                     (ts3[1] - ts3[0]) > (ts2[1] - ts2[0]));
268             assertTrue("Expect extra delays in service restarts",
269                     (ts3[2] - ts3[0]) > (ts2[2] - ts2[0]));
270             assertTrue("Expect extra delays in service restarts",
271                     (ts3[3] - ts3[0]) > (ts2[3] - ts2[0]));
272 
273             // Start and kill them again, but don't wait for the restart.
274             final long now4 = startKillAndRestart(am, ACTION_START | ACTION_KILL, uid1Listener1,
275                     uid1Listener2, uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2,
276                     WAIT_LONG_MS, true)[0];
277 
278             // Set the memory pressure to normal.
279             setMemoryPressure(ADJ_MEM_FACTOR_NORMAL);
280 
281             // Now wait for the restarts.
282             final long[] ts4 = startKillAndRestart(am, ACTION_WAIT | ACTION_STOPPKG, uid1Listener1,
283                     uid1Listener2, uid2Listener1, uid2Listener2, uid3Listener1, uid3Listener2,
284                     WAIT_LONG_MS, true);
285 
286             assertTrue("Expect less delays in service restarts",
287                     (ts4[1] - now4) < (ts2[1] - ts2[0]));
288             assertTrue("Expect less delays in service restarts",
289                     (ts4[2] - now4) < (ts2[2] - ts2[0]));
290             assertTrue("Expect less delays in service restarts",
291                     (ts4[3] - now4) < (ts2[3] - ts2[0]));
292         } finally {
293             executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE1_NAME);
294             executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE2_NAME);
295             executeShellCmd("cmd deviceidle whitelist -" + TEST_PACKAGE3_NAME);
296             resetMemoryPressure();
297             am.removeOnUidImportanceListener(uid1Listener1);
298             am.removeOnUidImportanceListener(uid1Listener2);
299             am.removeOnUidImportanceListener(uid2Listener1);
300             am.removeOnUidImportanceListener(uid2Listener2);
301             am.removeOnUidImportanceListener(uid3Listener1);
302             am.removeOnUidImportanceListener(uid3Listener2);
303             am.forceStopPackage(TEST_PACKAGE1_NAME);
304             am.forceStopPackage(TEST_PACKAGE2_NAME);
305             am.forceStopPackage(TEST_PACKAGE3_NAME);
306         }
307     }
308 
startKillAndRestart(ActivityManager am, int action, MyUidImportanceListener uid1Listener1, MyUidImportanceListener uid1Listener2, MyUidImportanceListener uid2Listener1, MyUidImportanceListener uid2Listener2, MyUidImportanceListener uid3Listener1, MyUidImportanceListener uid3Listener2, long waitDuration, boolean checkStartSeq)309     private long[] startKillAndRestart(ActivityManager am, int action,
310             MyUidImportanceListener uid1Listener1, MyUidImportanceListener uid1Listener2,
311             MyUidImportanceListener uid2Listener1, MyUidImportanceListener uid2Listener2,
312             MyUidImportanceListener uid3Listener1, MyUidImportanceListener uid3Listener2,
313             long waitDuration, boolean checkStartSeq) throws Exception {
314         final long[] res = new long[4];
315         // Test restarts in normal condition.
316         if ((action & ACTION_START) != 0) {
317             startServiceAndWait(TEST_PACKAGE1_NAME, uid1Listener1, WAIT_MS);
318             startServiceAndWait(TEST_PACKAGE2_NAME, uid2Listener1, WAIT_MS);
319             startServiceAndWait(TEST_PACKAGE3_NAME, uid3Listener1, WAIT_MS);
320         }
321 
322         if ((action & ACTION_KILL) != 0) {
323             final long now = res[0] = SystemClock.uptimeMillis();
324             killUidAndWait(am, mTestPackage1Uid, uid1Listener2, WAIT_MS);
325             killUidAndWait(am, mTestPackage2Uid, uid2Listener2, WAIT_MS);
326             killUidAndWait(am, mTestPackage3Uid, uid3Listener2, WAIT_MS);
327         }
328 
329         if ((action & ACTION_WAIT) != 0) {
330             assertTrue("Timed out to restart " + TEST_PACKAGE1_NAME, uid1Listener1.waitFor(
331                     RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
332             assertTrue("Timed out to restart " + TEST_PACKAGE2_NAME, uid2Listener1.waitFor(
333                     RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
334             assertTrue("Timed out to restart " + TEST_PACKAGE3_NAME, uid3Listener1.waitFor(
335                     RunningAppProcessInfo.IMPORTANCE_SERVICE, waitDuration));
336             final long uid1RestartTime = res[1] = uid1Listener1.mCurrentTimestamp;
337             final long uid2RestartTime = res[2] = uid2Listener1.mCurrentTimestamp;
338             final long uid3RestartTime = res[3] = uid3Listener1.mCurrentTimestamp;
339             if (checkStartSeq) {
340                 assertTrue(TEST_PACKAGE1_NAME + " should restart before " + TEST_PACKAGE2_NAME,
341                         uid1RestartTime <= uid2RestartTime);
342                 assertTrue(TEST_PACKAGE2_NAME + " should restart before " + TEST_PACKAGE3_NAME,
343                         uid2RestartTime <= uid3RestartTime);
344             }
345         }
346 
347         if ((action & ACTION_STOPPKG) != 0) {
348             // Force stop these packages to reset the backoff delays.
349             am.forceStopPackage(TEST_PACKAGE1_NAME);
350             am.forceStopPackage(TEST_PACKAGE2_NAME);
351             am.forceStopPackage(TEST_PACKAGE3_NAME);
352             assertTrue("Timed out to force-stop " + mTestPackage1Uid,
353                     uid1Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
354             assertTrue("Timed out to force-stop " + mTestPackage2Uid,
355                     uid2Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
356             assertTrue("Timed out to force-stop " + mTestPackage3Uid,
357                     uid3Listener2.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, WAIT_MS));
358         }
359         return res;
360     }
361 
362     @Test
testServiceWithDepPkgStopped()363     public void testServiceWithDepPkgStopped() throws Exception {
364         final CountDownLatch[] latchHolder = new CountDownLatch[1];
365         final ServiceConnection conn = new ServiceConnection() {
366             @Override
367             public void onServiceConnected(ComponentName name, IBinder service) {
368                 latchHolder[0].countDown();
369             }
370 
371             @Override
372             public void onServiceDisconnected(ComponentName name) {
373                 latchHolder[0].countDown();
374             }
375         };
376 
377         final long timeout = 5_000;
378         final long shortTimeout = 2_000;
379         final Intent intent = new Intent(ACTION_SERVICE_WITH_DEP_PKG);
380         final String testPkg = TEST_PACKAGE2_NAME;
381         final String libPkg = TEST_PACKAGE3_NAME;
382         final String apkPath = LOCAL_APK_BASE_PATH + TEST_PACKAGE3_APK;
383         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
384 
385         intent.setComponent(ComponentName.unflattenFromString(testPkg + "/" + TEST_SERVICE_NAME));
386         intent.putExtra(EXTRA_TARGET_PACKAGE, libPkg);
387         try {
388             executeShellCmd("am service-restart-backoff disable " + testPkg);
389 
390             latchHolder[0] = new CountDownLatch(1);
391             assertTrue("Unable to bind to test service in " + testPkg,
392                     mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE));
393             assertTrue("Timed out to bind service in " + testPkg,
394                     latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
395 
396             Thread.sleep(shortTimeout);
397             assertTrue(libPkg + " should be a dependency package of " + testPkg,
398                     isPackageDependency(testPkg, libPkg));
399 
400             // Force-stop lib package, the test service should be restarted.
401             latchHolder[0] = new CountDownLatch(2);
402             am.forceStopPackage(libPkg);
403             assertTrue("Test service in didn't restart in " + testPkg,
404                     latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
405 
406             Thread.sleep(shortTimeout);
407 
408             // Re-install the lib package, the test service should be restarted.
409             latchHolder[0] = new CountDownLatch(2);
410             assertTrue("Unable to install package " + apkPath, installPackage(apkPath));
411             assertTrue("Test service in didn't restart in " + testPkg,
412                     latchHolder[0].await(timeout, TimeUnit.MILLISECONDS));
413 
414             Thread.sleep(shortTimeout);
415 
416             // Force-stop the service package, the test service should not be restarted.
417             latchHolder[0] = new CountDownLatch(2);
418             am.forceStopPackage(testPkg);
419             assertFalse("Test service should not be restarted in " + testPkg,
420                     latchHolder[0].await(timeout * 2, TimeUnit.MILLISECONDS));
421         } finally {
422             executeShellCmd("am service-restart-backoff enable " + testPkg);
423             mContext.unbindService(conn);
424             am.forceStopPackage(testPkg);
425         }
426     }
427 
isPackageDependency(String pkgName, String libPackage)428     private boolean isPackageDependency(String pkgName, String libPackage) throws Exception {
429         final String output = SystemUtil.runShellCommand("dumpsys activity processes " + pkgName);
430         final Matcher matcher = Pattern.compile("packageDependencies=\\{.*?\\b"
431                 + libPackage + "\\b.*?\\}").matcher(output);
432         return matcher.find();
433     }
434 
installPackage(String apkPath)435     private boolean installPackage(String apkPath) throws Exception {
436         return executeShellCmd("pm install -t " + apkPath).equals("Success\n");
437     }
438 
startServiceAndWait(String pkgName, MyUidImportanceListener uidListener, long timeout)439     private void startServiceAndWait(String pkgName, MyUidImportanceListener uidListener,
440             long timeout) throws Exception {
441         final Intent intent = new Intent();
442         final ComponentName cn = ComponentName.unflattenFromString(
443                 pkgName + "/" + TEST_SERVICE_NAME);
444         intent.setComponent(cn);
445         Log.i(TAG, "Starting service " + cn);
446         assertNotNull(mContext.startService(intent));
447         assertTrue("Timed out to start service " + cn,
448                 uidListener.waitFor(RunningAppProcessInfo.IMPORTANCE_SERVICE, timeout));
449     }
450 
killUidAndWait(ActivityManager am, int uid, MyUidImportanceListener uidListener, long timeout)451     private void killUidAndWait(ActivityManager am, int uid, MyUidImportanceListener uidListener,
452             long timeout) throws Exception {
453         am.killUid(uid, "test service restart");
454         assertTrue("Timed out to kill " + uid,
455                 uidListener.waitFor(RunningAppProcessInfo.IMPORTANCE_GONE, timeout));
456     }
457 
executeShellCmd(String cmd)458     private String executeShellCmd(String cmd) throws Exception {
459         final String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
460         Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
461         return result;
462     }
463 
setMemoryPressure(int pressure)464     private void setMemoryPressure(int pressure) throws Exception {
465         executeShellCmd("am memory-factor set " + MEMORY_PRESSURE_MAP[pressure]);
466     }
467 
resetMemoryPressure()468     private void resetMemoryPressure() throws Exception {
469         executeShellCmd("am memory-factor reset");
470     }
471 
472     private static class MyUidImportanceListener implements OnUidImportanceListener {
473         final CountDownLatch[] mLatchHolder = new CountDownLatch[1];
474         private final int mExpectedUid;
475         private int mExpectedImportance;
476         private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
477         long mCurrentTimestamp;
478 
MyUidImportanceListener(int uid)479         MyUidImportanceListener(int uid) {
480             mExpectedUid = uid;
481         }
482 
483         @Override
onUidImportance(int uid, int importance)484         public void onUidImportance(int uid, int importance) {
485             if (uid == mExpectedUid) {
486                 mCurrentTimestamp = SystemClock.uptimeMillis();
487                 synchronized (this) {
488                     if (importance == mExpectedImportance && mLatchHolder[0] != null) {
489                         mLatchHolder[0].countDown();
490                     }
491                     mCurrentImportance = importance;
492                 }
493                 Log.i(TAG, "uid " + uid + " importance: " + importance);
494             }
495         }
496 
waitFor(int expectedImportance, long timeout)497         boolean waitFor(int expectedImportance, long timeout) throws Exception {
498             synchronized (this) {
499                 mExpectedImportance = expectedImportance;
500                 if (mCurrentImportance == expectedImportance) {
501                     return true;
502                 }
503                 mLatchHolder[0] = new CountDownLatch(1);
504             }
505             return mLatchHolder[0].await(timeout, TimeUnit.MILLISECONDS);
506         }
507     }
508 }
509