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 20 import static com.google.common.truth.Truth.assertThat; 21 22 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 23 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 24 25 import org.junit.Before; 26 import org.junit.Test; 27 import org.junit.runner.RunWith; 28 29 @RunWith(DeviceJUnit4ClassRunner.class) 30 public final class SdkSandboxLifecycleHostTest extends BaseHostJUnit4Test { 31 32 private static final String APP_PACKAGE = "com.android.sdksandbox.app"; 33 private static final String APP_2_PACKAGE = "com.android.sdksandbox.app2"; 34 35 private static final String APP_ACTIVITY = "SdkSandboxTestActivity"; 36 private static final String APP_2_ACTIVITY = "SdkSandboxTestActivity2"; 37 38 private static final String CODE_APK = "TestCodeProvider.apk"; 39 private static final String CODE_APK_2 = "TestCodeProvider2.apk"; 40 clearProcess(String pkg)41 private void clearProcess(String pkg) throws Exception { 42 getDevice().executeShellCommand(String.format("pm clear %s", pkg)); 43 } 44 startActivity(String pkg, String activity)45 private void startActivity(String pkg, String activity) throws Exception { 46 getDevice().executeShellCommand(String.format("am start -W -n %s/.%s", pkg, activity)); 47 } 48 killApp(String pkg)49 private void killApp(String pkg) throws Exception { 50 getDevice().executeShellCommand(String.format("am force-stop %s", pkg)); 51 } 52 getUidForPackage(String pkg)53 private String getUidForPackage(String pkg) throws Exception { 54 String pid = getDevice().getProcessPid(pkg); 55 if (pid == null) { 56 throw new Exception(String.format("Could not find PID for %s", pkg)); 57 } 58 String result = getDevice().executeAdbCommand("shell", "ps", "-p", pid, "-o", "uid"); 59 String[] sections = result.split("\n"); 60 return sections[sections.length - 1]; 61 } 62 63 // TODO(b/216302023): Update sdk sandbox process name format getSdkSandboxNameForPackage(String pkg)64 private String getSdkSandboxNameForPackage(String pkg) throws Exception { 65 String appUid = getUidForPackage(pkg); 66 return String.format("sdk_sandbox_%s", appUid); 67 } 68 69 @Before setUp()70 public void setUp() throws Exception { 71 assertThat(getBuild()).isNotNull(); 72 assertThat(getDevice()).isNotNull(); 73 74 // Ensure neither app is currently running 75 for (String pkg : new String[]{APP_PACKAGE, APP_2_PACKAGE}) { 76 clearProcess(pkg); 77 } 78 79 // Workaround for autoTeardown which removes packages installed in test 80 for (String apk : new String[]{CODE_APK, CODE_APK_2}) { 81 if (!isPackageInstalled(apk)) { 82 installPackage(apk, "-d"); 83 } 84 } 85 } 86 87 @Test testSdkSandboxIsDestroyedOnAppDestroy()88 public void testSdkSandboxIsDestroyedOnAppDestroy() throws Exception { 89 startActivity(APP_PACKAGE, APP_ACTIVITY); 90 String processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 91 assertThat(processDump).contains(APP_PACKAGE); 92 String sdkSandbox = getSdkSandboxNameForPackage(APP_PACKAGE); 93 assertThat(processDump).contains(sdkSandbox); 94 95 killApp(APP_PACKAGE); 96 processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 97 assertThat(processDump).doesNotContain(APP_PACKAGE); 98 assertThat(processDump).doesNotContain(sdkSandbox); 99 } 100 101 @Test testSdkSandboxIsCreatedPerApp()102 public void testSdkSandboxIsCreatedPerApp() throws Exception { 103 startActivity(APP_PACKAGE, APP_ACTIVITY); 104 String processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 105 assertThat(processDump).contains(APP_PACKAGE); 106 String sdkSandbox1 = getSdkSandboxNameForPackage(APP_PACKAGE); 107 assertThat(processDump).contains(sdkSandbox1); 108 109 startActivity(APP_2_PACKAGE, APP_2_ACTIVITY); 110 processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 111 assertThat(processDump).contains(APP_2_PACKAGE); 112 String sdkSandbox2 = getSdkSandboxNameForPackage(APP_2_PACKAGE); 113 assertThat(processDump).contains(sdkSandbox2); 114 assertThat(processDump).contains(APP_PACKAGE); 115 assertThat(processDump).contains(sdkSandbox1); 116 117 killApp(APP_2_PACKAGE); 118 processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 119 assertThat(processDump).doesNotContain(APP_2_PACKAGE); 120 assertThat(processDump).doesNotContain(sdkSandbox2); 121 assertThat(processDump).contains(APP_PACKAGE); 122 assertThat(processDump).contains(sdkSandbox1); 123 } 124 125 @Test testAppAndSdkSandboxAreKilledOnLoadedSdkUpdate()126 public void testAppAndSdkSandboxAreKilledOnLoadedSdkUpdate() throws Exception { 127 startActivity(APP_PACKAGE, APP_ACTIVITY); 128 129 // Should see app/sdk sandbox running 130 String processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 131 assertThat(processDump).contains(APP_PACKAGE); 132 133 String sdkSandbox = getSdkSandboxNameForPackage(APP_PACKAGE); 134 assertThat(processDump).contains(sdkSandbox); 135 136 // Update package loaded by app 137 installPackage(CODE_APK, "-d"); 138 139 // Should no longer see app/sdk sandbox running 140 processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 141 assertThat(processDump).doesNotContain(APP_PACKAGE); 142 assertThat(processDump).doesNotContain(sdkSandbox); 143 } 144 145 @Test testAppAndSdkSandboxAreNotKilledForNonLoadedSdkUpdate()146 public void testAppAndSdkSandboxAreNotKilledForNonLoadedSdkUpdate() throws Exception { 147 startActivity(APP_PACKAGE, APP_ACTIVITY); 148 149 // Should see app/sdk sandbox running 150 String processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 151 assertThat(processDump).contains(APP_PACKAGE); 152 153 String sdkSandbox = getSdkSandboxNameForPackage(APP_PACKAGE); 154 assertThat(processDump).contains(sdkSandbox); 155 156 // Simulate update of package not loaded by app 157 installPackage(CODE_APK_2, "-d"); 158 159 // Should still see app/sdk sandbox running 160 processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 161 assertThat(processDump).contains(APP_PACKAGE); 162 assertThat(processDump).contains(sdkSandbox); 163 } 164 165 @Test testOnlyRelevantAppIsKilledForLoadedSdkUpdate()166 public void testOnlyRelevantAppIsKilledForLoadedSdkUpdate() throws Exception { 167 startActivity(APP_PACKAGE, APP_ACTIVITY); 168 startActivity(APP_2_PACKAGE, APP_2_ACTIVITY); 169 170 // See processes for both apps 171 String processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 172 assertThat(processDump).contains(APP_PACKAGE); 173 174 String sdkSandbox = getSdkSandboxNameForPackage(APP_PACKAGE); 175 assertThat(processDump).contains(sdkSandbox); 176 177 assertThat(processDump).contains(APP_2_PACKAGE); 178 179 String sdkSandbox2 = getSdkSandboxNameForPackage(APP_PACKAGE); 180 assertThat(processDump).contains(sdkSandbox2); 181 182 installPackage(CODE_APK_2, "-d"); 183 184 processDump = getDevice().executeAdbCommand("shell", "ps", "-A"); 185 assertThat(processDump).contains(APP_PACKAGE); 186 assertThat(processDump).doesNotContain(APP_2_PACKAGE); 187 188 // TODO(b/215012578) check that sdk sandbox for app 1 is still running 189 } 190 } 191