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