1 /* 2 * Copyright (C) 2020 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 23 import android.platform.test.annotations.AppModeFull; 24 25 import com.android.tradefed.device.contentprovider.ContentProviderHandler; 26 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 27 28 import org.junit.After; 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 33 /** 34 * Runs the legacy file path access tests. 35 */ 36 @RunWith(DeviceJUnit4ClassRunner.class) 37 @AppModeFull 38 public class LegacyStorageHostTest extends BaseHostTestCase { 39 40 private boolean mIsExternalStorageSetup; 41 42 private ContentProviderHandler mContentProviderHandler; 43 44 /** 45 * Runs the given phase of LegacyStorageTest by calling into the device. 46 * Throws an exception if the test phase fails. 47 */ runDeviceTest(String phase)48 void runDeviceTest(String phase) throws Exception { 49 assertThat(runDeviceTests("android.scopedstorage.cts.legacy", 50 "android.scopedstorage.cts.legacy.LegacyStorageTest", phase)).isTrue(); 51 } 52 53 /** 54 * <p> Keep in mind that granting WRITE_EXTERNAL_STORAGE also grants READ_EXTERNAL_STORAGE, 55 * so in order to test a case where the reader has only WRITE, we must explicitly revoke READ. 56 */ grantPermissions(String... perms)57 private void grantPermissions(String... perms) throws Exception { 58 int currentUserId = getCurrentUserId(); 59 for (String perm : perms) { 60 executeShellCommand("pm grant --user %d android.scopedstorage.cts.legacy %s", 61 currentUserId, perm); 62 } 63 } 64 revokePermissions(String... perms)65 private void revokePermissions(String... perms) throws Exception { 66 int currentUserId = getCurrentUserId(); 67 for (String perm : perms) { 68 executeShellCommand("pm revoke --user %d android.scopedstorage.cts.legacy %s", 69 currentUserId, perm); 70 } 71 } 72 73 /** 74 * Creates a file {@code filePath} in shell and may bypass Media Provider restrictions for 75 * creating file. 76 */ createFileAsShell(String filePath)77 private void createFileAsShell(String filePath) throws Exception { 78 executeShellCommand("touch %s", filePath); 79 assertThat(getDevice().doesFileExist(filePath)).isTrue(); 80 } 81 setupExternalStorage()82 private void setupExternalStorage() throws Exception { 83 if (!mIsExternalStorageSetup) { 84 runDeviceTest("setupExternalStorage"); 85 mIsExternalStorageSetup = true; 86 } 87 } 88 89 @Before setup()90 public void setup() throws Exception { 91 // Ignore tests on automotive devices b/319785789 92 assumeFalse(hasDeviceFeature("android.hardware.type.automotive")); 93 94 mContentProviderHandler = new ContentProviderHandler(getDevice()); 95 mContentProviderHandler.setUp(); 96 setupExternalStorage(); 97 // Granting WRITE automatically grants READ as well, so we grant them both explicitly by 98 // default in order to avoid confusion. Test cases that don't want any of those permissions 99 // have to revoke the unwanted permissions. 100 grantPermissions("android.permission.WRITE_EXTERNAL_STORAGE", 101 "android.permission.READ_EXTERNAL_STORAGE"); 102 } 103 104 @After tearDown()105 public void tearDown() throws Exception { 106 if (mContentProviderHandler != null) { 107 mContentProviderHandler.tearDown(); 108 } 109 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE", 110 "android.permission.READ_EXTERNAL_STORAGE"); 111 } 112 113 @Test testCreateFilesInRandomPlaces_hasW()114 public void testCreateFilesInRandomPlaces_hasW() throws Exception { 115 revokePermissions("android.permission.READ_EXTERNAL_STORAGE"); 116 executeShellCommand("mkdir -p /sdcard/Android/data/com.android.shell -m 2770"); 117 runDeviceTest("testCreateFilesInRandomPlaces_hasW"); 118 } 119 120 @Test testCantInsertFilesInOtherAppPrivateDir_hasRW()121 public void testCantInsertFilesInOtherAppPrivateDir_hasRW() throws Exception { 122 runDeviceTest("testCantInsertFilesInOtherAppPrivateDir_hasRW"); 123 } 124 125 @Test testCantUpdateFilesInOtherAppPrivateDir_hasRW()126 public void testCantUpdateFilesInOtherAppPrivateDir_hasRW() throws Exception { 127 runDeviceTest("testCantUpdateFilesInOtherAppPrivateDir_hasRW"); 128 } 129 130 @Test testCantInsertFilesInOtherAppPrivateDir_hasMES()131 public void testCantInsertFilesInOtherAppPrivateDir_hasMES() throws Exception { 132 allowAppOps("android:manage_external_storage"); 133 try { 134 runDeviceTest("testCantInsertFilesInOtherAppPrivateDir_hasMES"); 135 } finally { 136 denyAppOps("android:manage_external_storage"); 137 } 138 } 139 140 @Test testCantUpdateFilesInOtherAppPrivateDir_hasMES()141 public void testCantUpdateFilesInOtherAppPrivateDir_hasMES() throws Exception { 142 allowAppOps("android:manage_external_storage"); 143 try { 144 runDeviceTest("testCantUpdateFilesInOtherAppPrivateDir_hasMES"); 145 } finally { 146 denyAppOps("android:manage_external_storage"); 147 } 148 } 149 150 @Test testCantInsertFilesInOtherAppPrivateDir_hasSystemGallery()151 public void testCantInsertFilesInOtherAppPrivateDir_hasSystemGallery() throws Exception { 152 runDeviceTest("testCantInsertFilesInOtherAppPrivateDir_hasSystemGallery"); 153 } 154 155 @Test testCantUpdateFilesInOtherAppPrivateDir_hasSystemGallery()156 public void testCantUpdateFilesInOtherAppPrivateDir_hasSystemGallery() throws Exception { 157 runDeviceTest("testCantUpdateFilesInOtherAppPrivateDir_hasSystemGallery"); 158 } 159 160 @Test testMkdirInRandomPlaces_hasW()161 public void testMkdirInRandomPlaces_hasW() throws Exception { 162 revokePermissions("android.permission.READ_EXTERNAL_STORAGE"); 163 executeShellCommand("mkdir -p /sdcard/Android/data/com.android.shell -m 2770"); 164 runDeviceTest("testMkdirInRandomPlaces_hasW"); 165 } 166 167 @Test testReadOnlyExternalStorage_hasR()168 public void testReadOnlyExternalStorage_hasR() throws Exception { 169 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE"); 170 runDeviceTest("testReadOnlyExternalStorage_hasR"); 171 } 172 173 @Test testCantAccessExternalStorage()174 public void testCantAccessExternalStorage() throws Exception { 175 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE", 176 "android.permission.READ_EXTERNAL_STORAGE"); 177 runDeviceTest("testCantAccessExternalStorage"); 178 } 179 180 @Test testListFiles_hasR()181 public void testListFiles_hasR() throws Exception { 182 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE"); 183 runDeviceTest("testListFiles_hasR"); 184 } 185 186 @Test testInsertHiddenFile()187 public void testInsertHiddenFile() throws Exception { 188 runDeviceTest("testInsertHiddenFile"); 189 } 190 191 @Test testCanRename_hasRW()192 public void testCanRename_hasRW() throws Exception { 193 runDeviceTest("testCanRename_hasRW"); 194 } 195 196 @Test testCanTrashOtherAndroidMediaFiles_hasRW()197 public void testCanTrashOtherAndroidMediaFiles_hasRW() throws Exception { 198 runDeviceTest("testCanTrashOtherAndroidMediaFiles_hasRW"); 199 } 200 201 @Test testCantRename_hasR()202 public void testCantRename_hasR() throws Exception { 203 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE"); 204 runDeviceTest("testCantRename_hasR"); 205 } 206 207 @Test testCantRename_noStoragePermission()208 public void testCantRename_noStoragePermission() throws Exception { 209 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE", 210 "android.permission.READ_EXTERNAL_STORAGE"); 211 runDeviceTest("testCantRename_noStoragePermission"); 212 } 213 214 @Test testRenameDirectoryAndUpdateDB_hasW()215 public void testRenameDirectoryAndUpdateDB_hasW() throws Exception { 216 runDeviceTest("testRenameDirectoryAndUpdateDB_hasW"); 217 } 218 219 @Test testCanDeleteAllFiles_hasRW()220 public void testCanDeleteAllFiles_hasRW() throws Exception { 221 runDeviceTest("testCanDeleteAllFiles_hasRW"); 222 } 223 224 @Test testLegacyAppCanOwnAFile_hasW()225 public void testLegacyAppCanOwnAFile_hasW() throws Exception { 226 runDeviceTest("testLegacyAppCanOwnAFile_hasW"); 227 } 228 229 @Test testCreateAndRenameDoesntLeaveStaleDBRow_hasRW()230 public void testCreateAndRenameDoesntLeaveStaleDBRow_hasRW() throws Exception { 231 runDeviceTest("testCreateAndRenameDoesntLeaveStaleDBRow_hasRW"); 232 } 233 234 @Test testRenameDoesntInvalidateUri_hasRW()235 public void testRenameDoesntInvalidateUri_hasRW() throws Exception { 236 runDeviceTest("testRenameDoesntInvalidateUri_hasRW"); 237 } 238 239 @Test testCanRenameAFileWithNoDBRow_hasRW()240 public void testCanRenameAFileWithNoDBRow_hasRW() throws Exception { 241 runDeviceTest("testCanRenameAFileWithNoDBRow_hasRW"); 242 } 243 244 @Test testCreateDoesntUpsert()245 public void testCreateDoesntUpsert() throws Exception { 246 runDeviceTest("testCreateDoesntUpsert"); 247 } 248 249 @Test testCaseInsensitivity()250 public void testCaseInsensitivity() throws Exception { 251 runDeviceTest("testAndroidDataObbCannotBeDeleted"); 252 } 253 254 @Test testLegacyAppUpdatingOwnershipOfExistingEntry()255 public void testLegacyAppUpdatingOwnershipOfExistingEntry() throws Exception { 256 runDeviceTest("testLegacyAppUpdatingOwnershipOfExistingEntry"); 257 } 258 259 @Test testInsertWithUnsupportedMimeType()260 public void testInsertWithUnsupportedMimeType() throws Exception { 261 runDeviceTest("testInsertWithUnsupportedMimeType"); 262 } 263 264 @Test testLegacySystemGalleryCanRenameImagesAndVideosWithoutDbUpdates()265 public void testLegacySystemGalleryCanRenameImagesAndVideosWithoutDbUpdates() throws Exception { 266 runDeviceTest("testLegacySystemGalleryCanRenameImagesAndVideosWithoutDbUpdates"); 267 } 268 269 /** 270 * (b/205673506): Test that legacy System Gallery can update() media file's relative_path to a 271 * non default top level directory. 272 */ 273 @Test testLegacySystemGalleryCanUpdateToExistingDirectory()274 public void testLegacySystemGalleryCanUpdateToExistingDirectory() throws Exception { 275 runDeviceTest("testLegacySystemGalleryCanUpdateToExistingDirectory"); 276 } 277 278 @Test testLegacySystemGalleryWithoutWESCannotRename()279 public void testLegacySystemGalleryWithoutWESCannotRename() throws Exception { 280 revokePermissions("android.permission.WRITE_EXTERNAL_STORAGE"); 281 runDeviceTest("testLegacySystemGalleryWithoutWESCannotRename"); 282 } 283 284 @Test testLegacyWESCanRenameImagesAndVideosWithDbUpdates_hasW()285 public void testLegacyWESCanRenameImagesAndVideosWithDbUpdates_hasW() throws Exception { 286 runDeviceTest("testLegacyWESCanRenameImagesAndVideosWithDbUpdates_hasW"); 287 } 288 289 @Test testScanUpdatesMetadataForNewlyAddedFile_hasRW()290 public void testScanUpdatesMetadataForNewlyAddedFile_hasRW() throws Exception { 291 runDeviceTest("testScanUpdatesMetadataForNewlyAddedFile_hasRW"); 292 } 293 294 @Test testInsertFromExternalDirsViaData()295 public void testInsertFromExternalDirsViaData() throws Exception { 296 runDeviceTest("testInsertFromExternalDirsViaData"); 297 } 298 299 @Test testUpdateToExternalDirsViaData()300 public void testUpdateToExternalDirsViaData() throws Exception { 301 runDeviceTest("testUpdateToExternalDirsViaData"); 302 } 303 304 @Test testInsertFromExternalDirsViaRelativePath()305 public void testInsertFromExternalDirsViaRelativePath() throws Exception { 306 runDeviceTest("testInsertFromExternalDirsViaRelativePath"); 307 } 308 309 @Test testUpdateToExternalDirsViaRelativePath()310 public void testUpdateToExternalDirsViaRelativePath() throws Exception { 311 runDeviceTest("testUpdateToExternalDirsViaRelativePath"); 312 } 313 314 @Test testInsertToOtherAppPrivateDirFails()315 public void testInsertToOtherAppPrivateDirFails() throws Exception { 316 runDeviceTest("testInsertToOtherAppPrivateDirFails"); 317 } 318 allowAppOps(String... ops)319 private void allowAppOps(String... ops) throws Exception { 320 for (String op : ops) { 321 executeShellCommand("cmd appops set --uid android.scopedstorage.cts.legacy " 322 + op + " allow"); 323 } 324 } 325 denyAppOps(String... ops)326 private void denyAppOps(String... ops) throws Exception { 327 for (String op : ops) { 328 executeShellCommand("cmd appops set --uid android.scopedstorage.cts.legacy " 329 + op + " deny"); 330 } 331 } 332 } 333