1 /* 2 * Copyright (C) 2023 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.device; 18 19 import static android.scopedstorage.cts.device.OwnedFilesRule.RESOURCE_ID_WITH_METADATA; 20 import static android.scopedstorage.cts.lib.FilePathAccessTestUtils.assertFileAccess_listFiles; 21 import static android.scopedstorage.cts.lib.FilePathAccessTestUtils.assertFileAccess_readWrite; 22 import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMatch; 23 import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromFile; 24 import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromRawResource; 25 import static android.scopedstorage.cts.lib.ResolverAccessTestUtils.assertResolver_canReadThumbnail; 26 import static android.scopedstorage.cts.lib.ResolverAccessTestUtils.assertResolver_listFiles; 27 import static android.scopedstorage.cts.lib.ResolverAccessTestUtils.assertResolver_readWrite; 28 import static android.scopedstorage.cts.lib.ResolverAccessTestUtils.assertResolver_uriDoesNotExist; 29 import static android.scopedstorage.cts.lib.ResolverAccessTestUtils.assertResolver_uriIsFavorite; 30 import static android.scopedstorage.cts.lib.ResolverAccessTestUtils.assertResolver_uriIsNotFavorite; 31 import static android.scopedstorage.cts.lib.TestUtils.assertFileContent; 32 import static android.scopedstorage.cts.lib.TestUtils.doEscalation; 33 import static android.scopedstorage.cts.lib.TestUtils.getContentResolver; 34 import static android.scopedstorage.cts.lib.TestUtils.getDcimDir; 35 import static android.scopedstorage.cts.lib.TestUtils.pollForPermission; 36 37 import static com.google.common.truth.Truth.assertThat; 38 39 import android.Manifest; 40 import android.content.ContentResolver; 41 import android.net.Uri; 42 import android.os.Build; 43 import android.os.Environment; 44 import android.os.ParcelFileDescriptor; 45 import android.provider.MediaStore; 46 import android.scopedstorage.cts.lib.ResolverAccessTestUtils; 47 import android.system.Os; 48 import android.util.Log; 49 50 import androidx.test.filters.SdkSuppress; 51 52 import org.junit.BeforeClass; 53 import org.junit.ClassRule; 54 import org.junit.Test; 55 56 import java.io.File; 57 import java.nio.ByteBuffer; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.HashMap; 61 import java.util.Set; 62 63 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE, codeName = "UpsideDownCake") 64 public class StorageOwnedFilesTest { 65 66 private static final String TAG = "StorageOwnedFilesTest"; 67 private static final ContentResolver sContentResolver = getContentResolver(); 68 @ClassRule 69 public static final OwnedFilesRule sFilesRule = new OwnedFilesRule(sContentResolver); 70 71 private static final String STR_DATA1 = "Random dest data"; 72 73 private static final byte[] BYTES_DATA1 = STR_DATA1.getBytes(); 74 private static final File IMAGE_FILE_1 = sFilesRule.getImageFile1(); 75 private static final File IMAGE_FILE_2_METADATA = sFilesRule.getImageFile2Metadata(); 76 private static final File VIDEO_FILE_1 = sFilesRule.getVideoFile1(); 77 78 // Cannot be static as the underlying resource isn't 79 private final Uri mImageUri1 = sFilesRule.getImageUri1(); 80 private final Uri mImageUri2 = sFilesRule.getImageUri2_Metadata(); 81 private final Uri mVideoUri1 = sFilesRule.getVideoUri1(); 82 83 @BeforeClass init()84 public static void init() throws Exception { 85 pollForPermission(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, true); 86 } 87 88 @Test owned_listOwnership()89 public void owned_listOwnership() throws Exception { 90 Set<File> expectedValues = Set.of(IMAGE_FILE_1, IMAGE_FILE_2_METADATA, VIDEO_FILE_1); 91 Set<File> notExpected = Collections.emptySet(); 92 // File access 93 assertFileAccess_listFiles(IMAGE_FILE_1.getParentFile(), expectedValues, notExpected); 94 // Query DCIM 95 assertResolver_listFiles(Environment.DIRECTORY_DCIM, expectedValues, notExpected, 96 sContentResolver); 97 } 98 99 @Test owned_canRead()100 public void owned_canRead() throws Exception { 101 assertResolver_readWrite(mImageUri1, sContentResolver); 102 assertResolver_readWrite(mImageUri2, sContentResolver); 103 assertResolver_readWrite(mVideoUri1, sContentResolver); 104 assertFileAccess_readWrite(IMAGE_FILE_1); 105 assertFileAccess_readWrite(IMAGE_FILE_2_METADATA); 106 assertFileAccess_readWrite(VIDEO_FILE_1); 107 } 108 109 @Test owned_canReadThumbnail()110 public void owned_canReadThumbnail() throws Exception { 111 assertResolver_canReadThumbnail(mImageUri1, sContentResolver); 112 assertResolver_canReadThumbnail(mImageUri2, sContentResolver); 113 // TODO Need a valid video to create a thumbnail 114 // assertResolver_canReadThumbnail(mVideoUri1, sContentResolver); 115 } 116 117 @Test owned_createFavoriteRequest()118 public void owned_createFavoriteRequest() throws Exception { 119 doEscalation(MediaStore.createFavoriteRequest(sContentResolver, Arrays.asList(mImageUri1, 120 mImageUri2, mVideoUri1), 121 true)); 122 assertResolver_uriIsFavorite(mImageUri1, sContentResolver); 123 assertResolver_uriIsFavorite(mImageUri2, sContentResolver); 124 assertResolver_uriIsFavorite(mVideoUri1, sContentResolver); 125 doEscalation(MediaStore.createFavoriteRequest(sContentResolver, Arrays.asList(mImageUri1, 126 mImageUri2, mVideoUri1), 127 false)); 128 assertResolver_uriIsNotFavorite(mImageUri1, sContentResolver); 129 assertResolver_uriIsNotFavorite(mImageUri2, sContentResolver); 130 assertResolver_uriIsNotFavorite(mVideoUri1, sContentResolver); 131 132 } 133 134 @Test owned_deleteRequest()135 public void owned_deleteRequest() throws Exception { 136 File fileToBeDeleted1 = new File(getDcimDir(), TAG + "_delete_1.jpg"); 137 File fileToBeDeleted2 = new File(getDcimDir(), TAG + "_delete_2.mp4"); 138 try { 139 Uri uriToBeDeleted1 = sFilesRule.createFile(fileToBeDeleted1); 140 Uri uriToBeDeleted2 = sFilesRule.createFile(fileToBeDeleted2); 141 Log.e(TAG, "alea " + uriToBeDeleted1 + " " + uriToBeDeleted2); 142 doEscalation( 143 MediaStore.createDeleteRequest(sContentResolver, 144 Arrays.asList(uriToBeDeleted1, uriToBeDeleted2))); 145 assertResolver_uriDoesNotExist(uriToBeDeleted1, sContentResolver); 146 assertResolver_uriDoesNotExist(uriToBeDeleted2, sContentResolver); 147 } finally { 148 fileToBeDeleted1.delete(); 149 fileToBeDeleted2.delete(); 150 } 151 } 152 153 @Test owned_insertRequest()154 public void owned_insertRequest() throws Exception { 155 assertInsertFile(TAG + "_insert_1.jpg", 156 ResolverAccessTestUtils::assertResolver_insertImage); 157 assertInsertFile(TAG + "_insert_2.mp4", 158 ResolverAccessTestUtils::assertResolver_insertVideo); 159 } 160 assertInsertFile(String filename, TriFunction<String, String, ContentResolver, Uri> inserter)161 private void assertInsertFile(String filename, 162 TriFunction<String, String, ContentResolver, Uri> inserter) throws Exception { 163 File fileToBeInserted = new File(getDcimDir(), filename); 164 final Uri targetUri = inserter.apply(fileToBeInserted.getName(), 165 Environment.DIRECTORY_DCIM, sContentResolver); 166 try (ParcelFileDescriptor parcelFileDescriptor = sContentResolver.openFileDescriptor( 167 targetUri, "rw")) { 168 assertThat(parcelFileDescriptor).isNotNull(); 169 Os.write(parcelFileDescriptor.getFileDescriptor(), ByteBuffer.wrap(BYTES_DATA1)); 170 assertFileContent(parcelFileDescriptor.getFileDescriptor(), BYTES_DATA1); 171 } finally { 172 fileToBeInserted.delete(); 173 } 174 } 175 176 @FunctionalInterface 177 interface TriFunction<A, B, C, R> { apply(A a, B b, C c)178 R apply(A a, B b, C c); 179 } 180 181 @Test owned_accessLocationMetadata()182 public void owned_accessLocationMetadata() throws Exception { 183 HashMap<String, String> originalExif = getExifMetadataFromRawResource( 184 RESOURCE_ID_WITH_METADATA); 185 186 HashMap<String, String> exif = getExifMetadataFromFile(IMAGE_FILE_2_METADATA); 187 assertExifMetadataMatch(exif, originalExif); 188 //TODO do we have videos with metadata? 189 } 190 } 191