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 android.scopedstorage.cts.host; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assume.assumeFalse; 22 import static org.junit.Assume.assumeTrue; 23 24 import android.platform.test.annotations.AppModeFull; 25 26 import com.android.tradefed.device.DeviceNotAvailableException; 27 import com.android.tradefed.device.contentprovider.ContentProviderHandler; 28 import com.android.tradefed.targetprep.TargetSetupError; 29 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 30 import com.android.tradefed.util.CommandResult; 31 32 import org.junit.After; 33 import org.junit.Before; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 /** 38 * Runs the AppCloning tests. 39 */ 40 @RunWith(DeviceJUnit4ClassRunner.class) 41 @AppModeFull 42 public class AppCloningHostTest extends BaseHostTestCase { 43 private static final String APP_A = "CtsScopedStorageTestAppA.apk"; 44 private static final String APP_A_PACKAGE = "android.scopedstorage.cts.testapp.A.withres"; 45 private static final String CONTENT_PROVIDER_URL = "content://android.tradefed.contentprovider"; 46 private static final int CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS = 20000; 47 private String mCloneUserId; 48 private ContentProviderHandler mContentProviderHandler; 49 50 51 @Before setup()52 public void setup() throws Exception { 53 assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode()); 54 assumeTrue(isAtLeastS()); 55 56 String output = executeShellCommand( 57 "pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE " 58 + "testUser"); 59 mCloneUserId = output.substring(output.lastIndexOf(' ') + 1).replaceAll("[^0-9]", ""); 60 assertThat(mCloneUserId).isNotEmpty(); 61 CommandResult out = executeShellV2Command("am start-user -w %s", mCloneUserId); 62 assertThat(out.getStderr()).isEmpty(); 63 mContentProviderHandler = new ContentProviderHandler(getDevice()); 64 mContentProviderHandler.setUp(); 65 } 66 67 @After tearDown()68 public void tearDown() throws Exception { 69 if (isHeadlessSystemUserMode() || !isAtLeastS()) return; 70 mContentProviderHandler.tearDown(); 71 executeShellCommand("pm remove-user %s", mCloneUserId); 72 } 73 74 @Test testInstallAppTwice()75 public void testInstallAppTwice() throws Exception { 76 installAppAsUser(APP_A, getCurrentUserId()); 77 installAppAsUser(APP_A, Integer.valueOf(mCloneUserId)); 78 uninstallPackage(APP_A_PACKAGE); 79 } 80 81 @Test testCreateCloneUserFile()82 public void testCreateCloneUserFile() throws Exception { 83 CommandResult out; 84 85 // Check that the clone user directories exist 86 eventually(() -> { 87 // Wait for finish. 88 assertThat(isSuccessful( 89 runContentProviderCommand("query", mCloneUserId, "/sdcard", ""))).isTrue(); 90 }, CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS); 91 92 // Create a file on the clone user storage 93 out = executeShellV2Command("touch /sdcard/testFile.txt"); 94 assertThat(isSuccessful(out)).isTrue(); 95 eventually(() -> { 96 // Wait for finish. 97 assertThat(isSuccessful( 98 runContentProviderCommand("write", mCloneUserId, "/sdcard/testFile.txt", 99 "< /sdcard/testFile.txt"))).isTrue(); 100 }, CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS); 101 102 // Check that the above created file exists on the clone user storage 103 out = runContentProviderCommand("query", mCloneUserId, "/sdcard/testFile.txt", ""); 104 assertThat(isSuccessful(out)).isTrue(); 105 106 // Cleanup the created file 107 out = runContentProviderCommand("delete", mCloneUserId, "/sdcard/testFile.txt", ""); 108 assertThat(isSuccessful(out)).isTrue(); 109 } 110 111 @Test testPrivateAppDataDirectoryForCloneUser()112 public void testPrivateAppDataDirectoryForCloneUser() throws Exception { 113 installAppAsUser(APP_A, Integer.valueOf(mCloneUserId)); 114 eventually(() -> { 115 // Wait for finish. 116 assertThat(isPackageInstalled(APP_A_PACKAGE, mCloneUserId)).isTrue(); 117 }, CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS); 118 } 119 installAppAsUser(String packageFile, int userId)120 private void installAppAsUser(String packageFile, int userId) 121 throws TargetSetupError, DeviceNotAvailableException { 122 installPackageAsUser(packageFile, false, userId, "-t"); 123 } 124 runContentProviderCommand(String commandType, String userId, String relativePath, String args)125 private CommandResult runContentProviderCommand(String commandType, String userId, 126 String relativePath, String args) throws Exception { 127 String fullUri = CONTENT_PROVIDER_URL + relativePath; 128 return executeShellV2Command("content %s --user %s --uri %s %s", 129 commandType, userId, fullUri, args); 130 } 131 132 } 133