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.sdksandbox.cts.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.AdoptableStorageUtils;
24 import android.app.sdksandbox.hosttestutils.DeviceSupportHostUtils;
25 import android.app.sdksandbox.hosttestutils.SecondaryUserUtils;
26 import android.platform.test.annotations.LargeTest;
27 
28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
29 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
30 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
31 
32 import org.junit.After;
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 @RunWith(DeviceJUnit4ClassRunner.class)
38 public class SdkSandboxDataIsolationHostTest extends BaseHostJUnit4Test {
39 
40     private static final String APP_PACKAGE = "com.android.sdksandbox.cts.app";
41     private static final String APP_APK = "CtsSdkSandboxHostTestApp.apk";
42     private static final String APP_TEST_CLASS = APP_PACKAGE + ".SdkSandboxDataIsolationTestApp";
43 
44     private static final String APP_2_PACKAGE = "com.android.sdksandbox.cts.app2";
45     private static final String APP_2_APK = "CtsSdkSandboxHostTestApp2.apk";
46 
47     private final SecondaryUserUtils mUserUtils = new SecondaryUserUtils(this);
48     private final AdoptableStorageUtils mAdoptableUtils = new AdoptableStorageUtils(this);
49     private final DeviceSupportHostUtils mDeviceSupportUtils = new DeviceSupportHostUtils(this);
50 
51     /**
52      * Runs the given phase of a test by calling into the device. Throws an exception if the test
53      * phase fails.
54      *
55      * <p>For example, <code>runPhase("testExample");</code>
56      */
runPhase(String phase)57     private void runPhase(String phase) throws Exception {
58         assertThat(runDeviceTests(APP_PACKAGE, APP_TEST_CLASS, phase)).isTrue();
59     }
60 
runPhase( String phase, String instrumentationArgKey, String instrumentationArgValue)61     private void runPhase(
62             String phase, String instrumentationArgKey, String instrumentationArgValue)
63             throws Exception {
64         runDeviceTests(
65                 new DeviceTestRunOptions(APP_PACKAGE)
66                         .setDevice(getDevice())
67                         .setTestClassName(APP_TEST_CLASS)
68                         .setTestMethodName(phase)
69                         .addInstrumentationArg(instrumentationArgKey, instrumentationArgValue));
70     }
71 
72     @Before
setUp()73     public void setUp() throws Exception {
74         assumeTrue("Device supports SdkSandbox", mDeviceSupportUtils.isSdkSandboxSupported());
75 
76         // These tests run on system user
77         uninstallPackage(APP_PACKAGE);
78         uninstallPackage(APP_2_PACKAGE);
79     }
80 
81     @After
tearDown()82     public void tearDown() throws Exception {
83         mUserUtils.removeSecondaryUserIfNecessary();
84         uninstallPackage(APP_PACKAGE);
85         uninstallPackage(APP_2_PACKAGE);
86     }
87 
88     @Test
testAppCannotAccessAnySandboxDirectories()89     public void testAppCannotAccessAnySandboxDirectories() throws Exception {
90         installPackage(APP_APK);
91         installPackage(APP_2_APK);
92 
93         runPhase("testAppCannotAccessAnySandboxDirectories");
94     }
95 
96     /** Test whether an SDK can access its provided data directories after data isolation. */
97     @Test
testSdkSandboxDataIsolation_SandboxCanAccessItsDirectory()98     public void testSdkSandboxDataIsolation_SandboxCanAccessItsDirectory() throws Exception {
99         installPackage(APP_APK);
100         runPhase("testSdkSandboxDataIsolation_SandboxCanAccessItsDirectory");
101     }
102 
103     /**
104      * Test whether an SDK can detect if an app is installed by the error obtained from accessing
105      * other sandbox app directories. ENOENT error should occur regardless of whether the app exists
106      * or not.
107      */
108     @Test
testSdkSandboxDataIsolation_CannotVerifyAppExistence()109     public void testSdkSandboxDataIsolation_CannotVerifyAppExistence() throws Exception {
110         installPackage(APP_APK);
111         installPackage(APP_2_APK);
112 
113         runPhase("testSdkSandboxDataIsolation_CannotVerifyAppExistence");
114     }
115 
116     /**
117      * Test whether an SDK can detect if an app is installed by the error obtained from accessing
118      * other sandbox user directories. Permission errors should show up regardless of whether the
119      * app exists, when trying to access other user data.
120      */
121     @Test
122     @LargeTest // Creates user
testSdkSandboxDataIsolation_CannotVerifyOtherUserAppExistence()123     public void testSdkSandboxDataIsolation_CannotVerifyOtherUserAppExistence() throws Exception {
124         assumeTrue(getDevice().isMultiUserSupported());
125 
126         installPackage(APP_APK);
127 
128         int userId = mUserUtils.createAndStartSecondaryUser();
129         installPackageAsUser(APP_2_APK, true, userId);
130 
131         runPhase(
132                 "testSdkSandboxDataIsolation_CannotVerifyOtherUserAppExistence",
133                 "sandbox_isolation_user_id",
134                 Integer.toString(userId));
135     }
136 
137     /**
138      * Test whether an SDK can verify an app's existence by checking other volumes, after data
139      * isolation has occurred.
140      */
141     @Test
142     @LargeTest // Creates volume
testSdkSandboxDataIsolation_CannotVerifyAcrossVolumes()143     public void testSdkSandboxDataIsolation_CannotVerifyAcrossVolumes() throws Exception {
144         assumeTrue(mAdoptableUtils.isAdoptableStorageSupported());
145         mAdoptableUtils.enableVirtualDisk();
146         installPackage(APP_APK);
147         installPackage(APP_2_APK);
148 
149         try {
150             final String uuid = mAdoptableUtils.createNewVolume();
151 
152             // Move second package to the newly created volume
153             assertSuccess(
154                     getDevice()
155                             .executeShellCommand("pm move-package " + APP_2_PACKAGE + " " + uuid));
156 
157             runPhase(
158                     "testSdkSandboxDataIsolation_CannotVerifyAcrossVolumes",
159                     "sandbox_isolation_uuid",
160                     uuid);
161         } finally {
162             mAdoptableUtils.cleanUpVolume();
163         }
164     }
165 
assertSuccess(String str)166     private static void assertSuccess(String str) {
167         if (str == null || !str.startsWith("Success")) {
168             throw new AssertionError("Expected success string but found " + str);
169         }
170     }
171 }
172