• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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