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.photopicker.cts.util;
18 
19 import android.app.UiAutomation;
20 import android.content.Context;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.os.FileUtils;
24 import android.os.UserHandle;
25 import android.photopicker.cts.R;
26 import android.provider.MediaStore;
27 import android.provider.cts.media.MediaProviderTestUtils;
28 import android.provider.cts.media.MediaStoreUtils;
29 import android.util.Pair;
30 
31 import androidx.annotation.NonNull;
32 import androidx.test.InstrumentationRegistry;
33 
34 import com.android.compatibility.common.util.ShellUtils;
35 
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 /**
43  * Photo Picker Utility methods for media files creation and deletion.
44  */
45 public class PhotoPickerFilesUtils {
46     public static final String DISPLAY_NAME_PREFIX = "ctsPhotoPicker";
47 
createImagesAndGetUris(int count, int userId)48     public static List<Uri> createImagesAndGetUris(int count, int userId)
49             throws Exception {
50         return createImagesAndGetUris(count, userId, /* isFavorite */ false);
51     }
52 
createImagesAndGetUris(int count, int userId, boolean isFavorite)53     public static List<Uri> createImagesAndGetUris(int count, int userId, boolean isFavorite)
54             throws Exception {
55         List<Pair<Uri, String>> createdImagesData = createImagesAndGetUriAndPath(count, userId,
56                 isFavorite);
57 
58         List<Uri> uriList = new ArrayList<>();
59         for (Pair<Uri, String> createdImageData: createdImagesData) {
60             uriList.add(createdImageData.first);
61         }
62 
63         return uriList;
64     }
65 
66     /**
67      * Create multiple images according to the given parameters and returns the uris and filenames
68      * of the images created.
69      *
70      * @param count number of images to create
71      * @param userId user id to create images in
72      *
73      * @return list of data (uri and filename pair) about the images created
74      */
createImagesAndGetUriAndPath(int count, int userId, boolean isFavorite)75     public static List<Pair<Uri, String>> createImagesAndGetUriAndPath(int count, int userId,
76             boolean isFavorite) throws Exception {
77         List<Pair<Uri, String>> createdImagesData = new ArrayList<>();
78 
79         for (int i = 0; i < count; i++) {
80             Pair<Uri, String> createdImageData = createImage(userId, isFavorite);
81             createdImagesData.add(createdImageData);
82             clearMediaOwner(createdImageData.first, userId);
83         }
84         // Wait for Picker db sync to complete
85         MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver());
86 
87         return createdImagesData;
88     }
89 
createImage(int userId, boolean isFavorite)90     private static Pair<Uri, String> createImage(int userId, boolean isFavorite) throws Exception {
91         return getPermissionAndStageMedia(R.raw.lg_g4_iso_800_jpg,
92                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId, isFavorite);
93     }
94 
createMj2VideosAndGetUris(int count, int userId)95     public static List<Uri> createMj2VideosAndGetUris(int count, int userId) throws Exception {
96         List<Uri> uriList = new ArrayList<>();
97         for (int i = 0; i < count; i++) {
98             final Uri uri = createMj2Video(userId);
99             uriList.add(uri);
100             clearMediaOwner(uri, userId);
101         }
102         // Wait for Picker db sync to complete
103         MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver());
104 
105         return uriList;
106     }
107 
createVideosAndGetUris(int count, int userId)108     public static List<Uri> createVideosAndGetUris(int count, int userId) throws Exception {
109         List<Uri> uriList = new ArrayList<>();
110         for (int i = 0; i < count; i++) {
111             final Uri uri = createVideo(userId);
112             uriList.add(uri);
113             clearMediaOwner(uri, userId);
114         }
115         // Wait for Picker db sync to complete
116         MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver());
117 
118         return uriList;
119     }
120 
121     /**
122      * Return media id from a given Uri
123      * @param Uri uri to parse
124      * @return String media id
125      */
getMediaId(Uri uri)126     public static String getMediaId(Uri uri) throws Exception {
127         return uri.getLastPathSegment();
128     }
129 
deleteMedia(Uri uri, Context context)130     public static void deleteMedia(Uri uri, Context context) throws Exception {
131         try {
132             MediaProviderTestUtils.setOwner(uri, context.getPackageName());
133             context.getContentResolver().delete(uri, Bundle.EMPTY);
134         } catch (Exception ignored) {
135         }
136     }
137 
clearMediaOwner(Uri uri, int userId)138     private static void clearMediaOwner(Uri uri, int userId) throws Exception {
139         final String cmd = String.format(
140                 "content update --uri %s --user %d --bind owner_package_name:n:", uri, userId);
141         ShellUtils.runShellCommand(cmd);
142     }
143 
createMj2Video(int userId)144     private static Uri createMj2Video(int userId) throws Exception {
145         return getPermissionAndStageMedia(R.raw.test_video_mj2,
146                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId,
147                 /* isFavorite */ false).first;
148     }
149 
createVideo(int userId)150     private static Uri createVideo(int userId) throws Exception {
151         return getPermissionAndStageMedia(R.raw.test_video,
152                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId,
153                 /* isFavorite */ false).first;
154     }
155 
getPermissionAndStageMedia(int resId, Uri collectionUri, String mimeType, int userId, boolean isFavorite)156     private static Pair<Uri, String> getPermissionAndStageMedia(int resId, Uri collectionUri,
157             String mimeType, int userId, boolean isFavorite) throws Exception {
158         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
159         uiAutomation.adoptShellPermissionIdentity(
160                 android.Manifest.permission.INTERACT_ACROSS_USERS,
161                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
162         try {
163             final Context context = InstrumentationRegistry.getTargetContext();
164             final Context userContext = userId == context.getUserId() ? context :
165                     context.createPackageContextAsUser("android", /* flags= */ 0,
166                             UserHandle.of(userId));
167             return stageMedia(resId, collectionUri, mimeType, userContext, isFavorite);
168         } finally {
169             uiAutomation.dropShellPermissionIdentity();
170         }
171     }
172 
stageMedia(int resId, Uri collectionUri, String mimeType, Context context, boolean isFavorite)173     private static Pair<Uri, String> stageMedia(int resId, Uri collectionUri, String mimeType,
174             Context context, boolean isFavorite) throws IOException {
175         final String displayName = DISPLAY_NAME_PREFIX + System.nanoTime();
176         final MediaStoreUtils.PendingParams params = new MediaStoreUtils.PendingParams(
177                 collectionUri, displayName, mimeType);
178         params.setIsFavorite(isFavorite);
179         final Uri pendingUri = MediaStoreUtils.createPending(context, params);
180         try (MediaStoreUtils.PendingSession session = MediaStoreUtils.openPending(context,
181                 pendingUri)) {
182             try (InputStream source = context.getResources().openRawResource(resId);
183                  OutputStream target = session.openOutputStream()) {
184                 FileUtils.copy(source, target);
185             }
186             return new Pair(session.publish(), displayName);
187         }
188     }
189 
190     @NonNull
createSvgImage(int userId)191     public static Uri createSvgImage(int userId) throws Exception {
192         return getPermissionAndStageMedia(R.raw.lg_g4_iso_800_svg,
193                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/svg+xml", userId,
194                 /* isFavorite */ false).first;
195     }
196 
197     @NonNull
createImageWithUnknownMimeType(int userId)198     public static Uri createImageWithUnknownMimeType(int userId) throws Exception {
199         return getPermissionAndStageMedia(R.raw.lg_g4_iso_800_unknown_mime_type,
200                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId,
201                 /* isFavorite */ false).first;
202     }
203 
204     @NonNull
createMpegVideo(int userId)205     public static Uri createMpegVideo(int userId) throws Exception {
206         return getPermissionAndStageMedia(R.raw.test_video_mpeg,
207                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mpeg", userId,
208                 /* isFavorite */ false).first;
209     }
210 
211     @NonNull
createVideoWithUnknownMimeType(int userId)212     public static Uri createVideoWithUnknownMimeType(int userId) throws Exception {
213         return getPermissionAndStageMedia(R.raw.test_video_unknown_mime_type,
214                 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId,
215                 /* isFavorite */ false).first;
216     }
217 }
218