• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.tests.sdksandbox.host;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assume.assumeTrue;
22 
23 import android.app.sdksandbox.hosttestutils.AwaitUtils;
24 import android.app.sdksandbox.hosttestutils.SdkSandboxDeviceSupportedHostRule;
25 import android.app.sdksandbox.hosttestutils.SecondaryUserUtils;
26 
27 import com.android.modules.utils.build.testing.DeviceSdkLevel;
28 import com.android.tradefed.device.DeviceNotAvailableException;
29 import com.android.tradefed.invoker.TestInformation;
30 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
31 import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
32 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
33 import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
34 
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Ignore;
38 import org.junit.Rule;
39 import org.junit.Test;
40 import org.junit.runner.RunWith;
41 
42 @RunWith(DeviceJUnit4ClassRunner.class)
43 public final class SdkSandboxLifecycleHostTest extends BaseHostJUnit4Test {
44 
45     @Rule(order = 0)
46     public final SdkSandboxDeviceSupportedHostRule deviceSupportRule =
47             new SdkSandboxDeviceSupportedHostRule(this);
48 
49     private static final String APP_PACKAGE = "com.android.sdksandbox.app";
50     private static final String APP_APK = "SdkSandboxTestApp.apk";
51     private static final String APP_2_PACKAGE = "com.android.sdksandbox.app2";
52 
53     private static final String APP_SHARED_PACKAGE = "com.android.sdksandbox.shared.app1";
54     private static final String APP_SHARED_ACTIVITY = "SdkSandboxTestSharedActivity";
55     private static final String APP_SHARED_2_PACKAGE = "com.android.sdksandbox.shared.app2";
56 
57     private static final String APP_ACTIVITY = "SdkSandboxTestActivity";
58     private static final String APP_2_ACTIVITY = "SdkSandboxTestActivity2";
59     private static final String APP_2_EMPTY_ACTIVITY = "SdkSandboxEmptyActivity";
60 
61     private static final String CODE_APK = "TestCodeProvider.apk";
62     private static final String CODE_APK_2 = "TestCodeProvider2.apk";
63 
64     private static final String APP_2_PROCESS_NAME = "com.android.sdksandbox.processname";
65     private static final String APP_2_PROCESS_NAME_2 = "com.android.sdksandbox.emptyactivity";
66     private static final String SANDBOX_2_PROCESS_NAME = APP_2_PROCESS_NAME
67                                                             + "_sdk_sandbox";
68     /**
69      * process name for app1 is not defined and it takes the package name by default
70      */
71     private static final String SANDBOX_1_PROCESS_NAME = APP_PACKAGE + "_sdk_sandbox";
72 
73     private static final String SANDBOX_SHARED_1_PROCESS_NAME = APP_SHARED_PACKAGE + "_sdk_sandbox";
74     private static final String SANDBOX_SHARED_2_PROCESS_NAME =
75             APP_SHARED_2_PACKAGE + "_sdk_sandbox";
76 
77     private final SecondaryUserUtils mUserUtils = new SecondaryUserUtils(this);
78 
79     private DeviceSdkLevel mDeviceSdkLevel;
80 
81     /** Root device for all tests. */
82     @BeforeClassWithInfo
beforeClassWithDevice(TestInformation testInfo)83     public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
84         assertThat(testInfo.getDevice().enableAdbRoot()).isTrue();
85     }
86 
87     /** UnRoot device after all tests. */
88     @AfterClassWithInfo
afterClassWithDevice(TestInformation testInfo)89     public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
90         testInfo.getDevice().disableAdbRoot();
91     }
92 
93     @Before
setUp()94     public void setUp() throws Exception {
95         assertThat(getBuild()).isNotNull();
96         assertThat(getDevice()).isNotNull();
97 
98         mDeviceSdkLevel = new DeviceSdkLevel(getDevice());
99 
100         if (!getDevice().isPackageInstalled(APP_PACKAGE)) {
101             installPackage(APP_APK);
102         }
103     }
104 
105     @After
tearDown()106     public void tearDown() throws Exception {
107         mUserUtils.removeSecondaryUserIfNecessary();
108         cleanUpAppAndSandboxProcesses();
109     }
110 
111     @Test
testSdkSandboxIsDestroyedOnAppDestroy()112     public void testSdkSandboxIsDestroyedOnAppDestroy() throws Exception {
113         startActivity(APP_PACKAGE, APP_ACTIVITY);
114         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
115         assertThat(processDump).contains(APP_PACKAGE + '\n');
116         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
117 
118         killApp(APP_PACKAGE);
119         waitForProcessDeath(SANDBOX_1_PROCESS_NAME);
120         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
121         assertThat(processDump).doesNotContain(APP_PACKAGE + '\n');
122         assertThat(processDump).doesNotContain(SANDBOX_1_PROCESS_NAME);
123 
124         // Wait 5 seconds to ensure that the sandbox has not restarted dying.
125         Thread.sleep(5000);
126         waitForProcessDeath(SANDBOX_1_PROCESS_NAME);
127     }
128 
129     @Test
testSdkSandboxIsCreatedPerApp()130     public void testSdkSandboxIsCreatedPerApp() throws Exception {
131         startActivity(APP_PACKAGE, APP_ACTIVITY);
132         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
133         assertThat(processDump).contains(APP_PACKAGE + '\n');
134         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
135 
136         startActivity(APP_2_PACKAGE, APP_2_ACTIVITY);
137         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
138         assertThat(processDump).contains(APP_2_PROCESS_NAME + '\n');
139         assertThat(processDump).contains(SANDBOX_2_PROCESS_NAME);
140         assertThat(processDump).contains(APP_PACKAGE + '\n');
141         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
142 
143         killApp(APP_2_PACKAGE);
144         // Wait a bit to allow sandbox death
145         waitForProcessDeath(SANDBOX_2_PROCESS_NAME);
146         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
147         assertThat(processDump).doesNotContain(APP_2_PROCESS_NAME + '\n');
148         assertThat(processDump).doesNotContain(SANDBOX_2_PROCESS_NAME);
149         assertThat(processDump).contains(APP_PACKAGE + '\n');
150         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
151     }
152 
153     @Test
testSdkSandboxIsKilledOnAppUninstall()154     public void testSdkSandboxIsKilledOnAppUninstall() throws Exception {
155         startActivity(APP_PACKAGE, APP_ACTIVITY);
156         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
157         assertThat(processDump).contains(APP_PACKAGE + '\n');
158         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
159 
160         uninstallPackage(APP_PACKAGE);
161         waitForProcessDeath(SANDBOX_1_PROCESS_NAME);
162         // Should no longer see app/sdk sandbox running
163         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
164         assertThat(processDump).doesNotContain(APP_PACKAGE + '\n');
165         assertThat(processDump).doesNotContain(SANDBOX_1_PROCESS_NAME);
166     }
167 
168     @Ignore("b/275299487")
169     @Test
testSandboxIsCreatedPerUser()170     public void testSandboxIsCreatedPerUser() throws Exception {
171         assumeTrue(getDevice().isMultiUserSupported());
172 
173         int secondaryUserId = mUserUtils.createAndStartSecondaryUser();
174         installPackageAsUser(APP_APK, false, secondaryUserId);
175 
176         // Start app for the primary user
177         startActivity(APP_PACKAGE, APP_ACTIVITY);
178         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
179         assertThat(processDump).contains(APP_PACKAGE + '\n');
180         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
181 
182         mUserUtils.switchToSecondaryUser();
183 
184         // Should still see an app/sdk sandbox running.
185         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
186         assertThat(processDump).contains(APP_PACKAGE + '\n');
187         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
188 
189         // Start the app for the secondary user.
190         startActivity(APP_PACKAGE, APP_ACTIVITY);
191         // There should be two instances of app and sandbox processes - one for each user.
192         assertThat(getProcessOccurrenceCount(APP_PACKAGE + '\n')).isEqualTo(2);
193         assertThat(getProcessOccurrenceCount(SANDBOX_1_PROCESS_NAME)).isEqualTo(2);
194 
195         // Kill the app process for the secondary user.
196         killApp(APP_PACKAGE + '\n');
197         // There should be one instance of app and sandbox process after kill
198         assertThat(getProcessOccurrenceCount(APP_PACKAGE + '\n')).isEqualTo(1);
199         assertThat(getProcessOccurrenceCount(SANDBOX_1_PROCESS_NAME)).isEqualTo(1);
200     }
201 
202     @Test
testSdkSandboxIsKilledOnLoadedSdkUpdate()203     public void testSdkSandboxIsKilledOnLoadedSdkUpdate() throws Exception {
204         startActivity(APP_PACKAGE, APP_ACTIVITY);
205 
206         // Should see app/sdk sandbox running
207         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
208         assertThat(processDump).contains(APP_PACKAGE + '\n');
209         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
210 
211         // Update package loaded by app
212         installPackage(CODE_APK, "-d");
213 
214         // SDK sandbox should be killed
215         waitForProcessDeath(SANDBOX_1_PROCESS_NAME);
216     }
217 
218     @Test
testSdkSandboxIsKilledForNonLoadedSdkUpdate()219     public void testSdkSandboxIsKilledForNonLoadedSdkUpdate() throws Exception {
220         // Have the app load the first SDK.
221         startActivity(APP_2_PACKAGE, APP_2_ACTIVITY);
222 
223         // Should see app/sdk sandbox running
224         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
225         assertThat(processDump).contains(APP_2_PROCESS_NAME + '\n');
226         assertThat(processDump).contains(SANDBOX_2_PROCESS_NAME);
227 
228         // Update package consumed by the app, but not loaded into the sandbox.
229         installPackage(CODE_APK_2, "-d");
230 
231         // SDK sandbox should be killed
232         waitForProcessDeath(SANDBOX_2_PROCESS_NAME);
233     }
234 
235     @Test
testAppsWithSharedUidCanLoadSameSdk()236     public void testAppsWithSharedUidCanLoadSameSdk() throws Exception {
237         startActivity(APP_SHARED_PACKAGE, APP_SHARED_ACTIVITY);
238         assertThat(runDeviceTests(APP_SHARED_2_PACKAGE,
239                 "com.android.sdksandbox.shared.app2.SdkSandboxTestSharedApp2",
240                 "testLoadSdkIsSuccessful")).isTrue();
241     }
242 
243     @Test
testAppsWithSharedUid_OneAppDies()244     public void testAppsWithSharedUid_OneAppDies() throws Exception {
245         startActivity(APP_SHARED_PACKAGE, APP_SHARED_ACTIVITY);
246         assertThat(runDeviceTests(APP_SHARED_2_PACKAGE,
247                 "com.android.sdksandbox.shared.app2.SdkSandboxTestSharedApp2",
248                 "testLoadSdkIsSuccessful")).isTrue();
249 
250         // APP_SHARED_2_PACKAGE dies after running device-side tests.
251         waitForProcessDeath(SANDBOX_SHARED_2_PROCESS_NAME);
252 
253         // For U+, the other sandbox should still be alive.
254         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
255         assertThat(processDump).contains(SANDBOX_SHARED_1_PROCESS_NAME);
256     }
257 
258     @Test
testAppOwnedSdkSandboxInterfaceRemoval_AppDies()259     public void testAppOwnedSdkSandboxInterfaceRemoval_AppDies() throws Exception {
260         startActivity(APP_SHARED_PACKAGE, APP_SHARED_ACTIVITY);
261         assertThat(
262                         runDeviceTests(
263                                 APP_SHARED_2_PACKAGE,
264                                 "com.android.sdksandbox.shared.app2.SdkSandboxTestSharedApp2",
265                                 "testRegisterAppOwedSdkSandboxInterfacesBeforeAppDeath"))
266                 .isTrue();
267 
268         // APP_SHARED_2_PACKAGE dies after running device-side tests.
269         waitForProcessDeath(SANDBOX_SHARED_2_PROCESS_NAME);
270         assertThat(
271                         runDeviceTests(
272                                 APP_SHARED_2_PACKAGE,
273                                 "com.android.sdksandbox.shared.app2.SdkSandboxTestSharedApp2",
274                                 "testGetAppOwedSdkSandboxInterfacesOnAppDeath"))
275                 .isTrue();
276     }
277 
278     @Test
testSandboxIsKilledWhenKillswitchEnabled()279     public void testSandboxIsKilledWhenKillswitchEnabled() throws Exception {
280         try {
281             getDevice()
282                     .executeShellCommand("device_config put adservices disable_sdk_sandbox false");
283             startActivity(APP_2_PACKAGE, APP_2_ACTIVITY);
284             String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
285             assertThat(processDump).contains(APP_2_PROCESS_NAME + '\n');
286             assertThat(processDump).contains(SANDBOX_2_PROCESS_NAME);
287 
288             getDevice()
289                     .executeShellCommand("device_config put adservices disable_sdk_sandbox true");
290             waitForProcessDeath(SANDBOX_2_PROCESS_NAME);
291             waitForProcessDeath(APP_2_PROCESS_NAME);
292 
293             processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
294             // In U+ the app should be killed when the sandbox is killed.
295             assertThat(processDump).doesNotContain(APP_2_PROCESS_NAME + '\n');
296             assertThat(processDump).doesNotContain(SANDBOX_2_PROCESS_NAME);
297         } finally {
298             getDevice().executeShellCommand("cmd sdk_sandbox set-state --enabled");
299         }
300     }
301 
302     @Test
testSpecificAppProcessIsKilledOnSandboxDeath()303     public void testSpecificAppProcessIsKilledOnSandboxDeath() throws Exception {
304         try {
305             getDevice()
306                     .executeShellCommand("device_config put adservices disable_sdk_sandbox false");
307 
308             // Start two activities running in two different processes for the same app. One
309             // activity loads an SDK while the other does nothing.
310             startActivity(APP_2_PACKAGE, APP_2_EMPTY_ACTIVITY);
311             startActivity(APP_2_PACKAGE, APP_2_ACTIVITY);
312             String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
313             assertThat(processDump).contains(APP_2_PROCESS_NAME + '\n');
314             assertThat(processDump).contains(APP_2_PROCESS_NAME_2 + '\n');
315             assertThat(processDump).contains(SANDBOX_2_PROCESS_NAME);
316 
317             final String initialAppProcessPid = getDevice().getProcessPid(APP_2_PROCESS_NAME);
318 
319             // Kill the sandbox.
320             getDevice()
321                     .executeShellCommand("device_config put adservices disable_sdk_sandbox true");
322             waitForProcessDeath(SANDBOX_2_PROCESS_NAME);
323             try {
324                 waitForProcessDeath(APP_2_PROCESS_NAME + '\n');
325             } catch (Exception e) {
326                 // If the app process has not died, it could have restarted as it was the top
327                 // activity. Verify that it is not the same process by checking the PID.
328                 final String finalAppProcessPid = getDevice().getProcessPid(APP_2_PROCESS_NAME);
329                 assertThat(finalAppProcessPid).isNotEqualTo(initialAppProcessPid);
330             }
331 
332             // Only the app process which loaded the SDK should die. The other app process should
333             // still be alive.
334             processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
335             assertThat(processDump).doesNotContain(APP_2_PROCESS_NAME + '\n');
336             assertThat(processDump).doesNotContain(SANDBOX_2_PROCESS_NAME);
337             assertThat(processDump).contains(APP_2_PROCESS_NAME_2 + '\n');
338 
339         } finally {
340             getDevice().executeShellCommand("cmd sdk_sandbox set-state --enabled");
341         }
342     }
343 
344     @Test
testBackgroundingAppReducesSandboxPriority()345     public void testBackgroundingAppReducesSandboxPriority() throws Exception {
346         startActivity(APP_PACKAGE, APP_ACTIVITY);
347 
348         // Should see app/sdk sandbox running
349         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
350         assertThat(processDump).contains(APP_PACKAGE + '\n');
351         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
352 
353         int sandboxOomScoreAdj1 = getOomScoreAdj(SANDBOX_1_PROCESS_NAME);
354         int appOomScoreAdj1 = getOomScoreAdj(APP_PACKAGE);
355         // Verify that the sandbox process has lower priority than the app process.
356         assertThat(sandboxOomScoreAdj1).isAtLeast(appOomScoreAdj1);
357 
358         // Navigate to home screen to send both apps to the background.
359         getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
360 
361         // Wait for app to be backgrounded and unbinding of sandbox to complete.
362         Thread.sleep(5000);
363 
364         // Should see app/sdk sandbox running
365         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
366         assertThat(processDump).contains(APP_PACKAGE + '\n');
367         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
368 
369         int sandboxOomScoreAdj2 = getOomScoreAdj(SANDBOX_1_PROCESS_NAME);
370         int appOomScoreAdj2 = getOomScoreAdj(APP_PACKAGE);
371         // The higher the oom adj score, the lower the priority of the process.
372         assertThat(sandboxOomScoreAdj2).isGreaterThan(sandboxOomScoreAdj1);
373         assertThat(appOomScoreAdj2).isGreaterThan(appOomScoreAdj1);
374 
375         if (mDeviceSdkLevel.isDeviceAtLeastV()) {
376             assertThat(sandboxOomScoreAdj2).isAtLeast(appOomScoreAdj2);
377 
378             // Start other apps to try to reduce the priority of the app.
379             startActivity(APP_2_PACKAGE, APP_2_ACTIVITY);
380             startActivity(APP_SHARED_PACKAGE, APP_SHARED_ACTIVITY);
381             Thread.sleep(2000);
382 
383             processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
384             assertThat(processDump).contains(APP_PACKAGE + '\n');
385             assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
386 
387             int sandboxOomScoreAdj3 = getOomScoreAdj(SANDBOX_1_PROCESS_NAME);
388             int appOomScoreAdj3 = getOomScoreAdj(APP_PACKAGE);
389             assertThat(appOomScoreAdj3).isAtLeast(appOomScoreAdj2);
390             assertThat(sandboxOomScoreAdj3).isAtLeast(sandboxOomScoreAdj2);
391             assertThat(sandboxOomScoreAdj3).isAtLeast(appOomScoreAdj3);
392         }
393     }
394 
395     @Test
testSandboxReconnectionAfterDeath()396     public void testSandboxReconnectionAfterDeath() throws Exception {
397         startActivity(APP_PACKAGE, APP_ACTIVITY);
398 
399         // Should see app/sdk sandbox running
400         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
401         assertThat(processDump).contains(APP_PACKAGE + '\n');
402         assertThat(processDump).contains(SANDBOX_1_PROCESS_NAME);
403 
404         String initialSandboxPid = getDevice().getProcessPid(SANDBOX_1_PROCESS_NAME);
405         getDevice().executeShellCommand("kill -9 " + initialSandboxPid);
406 
407         Thread.sleep(5000);
408 
409         processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
410         assertThat(processDump).contains(APP_PACKAGE + '\n');
411         // The sandbox should not restart in U+.
412         assertThat(processDump).doesNotContain(SANDBOX_1_PROCESS_NAME);
413     }
414 
415     @Ignore("b/310160187")
416     @Test
testSdkSandboxProcessNameForSecondaryUser()417     public void testSdkSandboxProcessNameForSecondaryUser() throws Exception {
418         assumeTrue(getDevice().isMultiUserSupported());
419         String appApk2 = "SdkSandboxTestApp2.apk";
420 
421         int secondaryUserId = mUserUtils.createAndStartSecondaryUser();
422         mUserUtils.switchToSecondaryUser();
423         installPackageAsUser(appApk2, false, secondaryUserId);
424         startActivity(APP_2_PACKAGE, APP_2_ACTIVITY);
425         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
426         assertThat(processDump).contains(APP_2_PROCESS_NAME + '\n');
427         assertThat(processDump).contains(SANDBOX_2_PROCESS_NAME);
428     }
429 
startActivity(String pkg, String activity)430     private void startActivity(String pkg, String activity) throws Exception {
431         getDevice()
432                 .executeShellCommand(
433                         String.format(
434                                 "am start -W -n %s/.%s --user %d",
435                                 pkg, activity, getDevice().getCurrentUser()));
436 
437         // Check that the activity has started correctly by checking that its process has started.
438         // Depending on the test package configuration, the package may differ from the process
439         // name.
440         String expectedProcessName = pkg;
441         if (pkg.equals(APP_2_PACKAGE)) {
442             if (activity.equals(APP_2_ACTIVITY)) {
443                 expectedProcessName = APP_2_PROCESS_NAME;
444             } else {
445                 expectedProcessName = APP_2_PROCESS_NAME_2;
446             }
447         }
448         assertThat(
449                         getDevice()
450                                 .executeShellCommand(
451                                         String.format("ps -A | grep %s", expectedProcessName)))
452                 .isNotEmpty();
453     }
454 
killApp(String pkg)455     private void killApp(String pkg) throws Exception {
456         getDevice().executeShellCommand(String.format("am force-stop --user current %s", pkg));
457         waitForProcessDeath(pkg + '\n');
458     }
459 
460     // Get the number of running processes with the given process name.
getProcessOccurrenceCount(String processName)461     private int getProcessOccurrenceCount(String processName) throws Exception {
462         String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
463 
464         int count = 0;
465         int processOccurrenceIndex = processDump.indexOf(processName);
466         while (processOccurrenceIndex >= 0) {
467             count++;
468             processOccurrenceIndex = processDump.indexOf(processName, processOccurrenceIndex + 1);
469         }
470         return count;
471     }
472 
cleanUpAppAndSandboxProcesses()473     private void cleanUpAppAndSandboxProcesses() throws Exception {
474         for (String pkg :
475                 new String[] {
476                     APP_PACKAGE, APP_2_PACKAGE, APP_SHARED_PACKAGE, APP_SHARED_2_PACKAGE
477                 }) {
478             killApp(pkg);
479         }
480 
481         // Ensure no sandbox is currently running
482         for (String sandbox :
483                 new String[] {
484                     SANDBOX_1_PROCESS_NAME,
485                     SANDBOX_2_PROCESS_NAME,
486                     SANDBOX_SHARED_1_PROCESS_NAME,
487                     SANDBOX_SHARED_2_PROCESS_NAME
488                 }) {
489             waitForProcessDeath(sandbox);
490         }
491     }
492 
getOomScoreAdj(String processName)493     private int getOomScoreAdj(String processName) throws DeviceNotAvailableException {
494         String pid = getDevice().getProcessPid(processName);
495         String oomScoreAdj =
496                 getDevice().executeShellCommand("cat /proc/" + pid + "/oom_score_adj").trim();
497         return Integer.parseInt(oomScoreAdj);
498     }
499 
waitForProcessDeath(String processName)500     private void waitForProcessDeath(String processName) throws Exception {
501         AwaitUtils.waitFor(
502                 () -> {
503                     String processDump = getDevice().executeAdbCommand("shell", "ps", "-A");
504                     return !processDump.contains(processName);
505                 },
506                 "Process " + processName + " has not died.");
507     }
508 }
509