Merge "Integrate PhotoPickeractivity to use PickerResult"
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index b5dbd9b..a1f2a0d 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -421,9 +421,21 @@
return mVolumeCache.getVolumeId(file);
}
- public @NonNull Collection<File> getVolumeScanPaths(String volumeName)
+ private @NonNull Collection<File> getAllowedVolumePaths(String volumeName)
throws FileNotFoundException {
- return mVolumeCache.getVolumeScanPaths(volumeName, mCallingIdentity.get().getUser());
+ // This method is used to verify whether a path belongs to a certain volume name;
+ // we can't always use the calling user's identity here to determine exactly which
+ // volume is meant, because the MediaScanner may scan paths belonging to another user,
+ // eg a clone user.
+ // So, for volumes like external_primary, just return allowed paths for all users.
+ List<UserHandle> users = mUserCache.getUsersCached();
+ ArrayList<File> allowedPaths = new ArrayList<>();
+ for (UserHandle user : users) {
+ Collection<File> volumeScanPaths = mVolumeCache.getVolumeScanPaths(volumeName, user);
+ allowedPaths.addAll(volumeScanPaths);
+ }
+
+ return allowedPaths;
}
/**
@@ -3476,7 +3488,7 @@
final String volumeName = resolveVolumeName(uri);
try {
// Quick check that the requested path actually lives on volume
- final Collection<File> allowed = getVolumeScanPaths(volumeName);
+ final Collection<File> allowed = getAllowedVolumePaths(volumeName);
final File actual = new File(values.getAsString(MediaColumns.DATA))
.getCanonicalFile();
if (!FileUtils.contains(allowed, actual)) {
diff --git a/src/com/android/providers/media/TranscodeHelper.java b/src/com/android/providers/media/TranscodeHelper.java
index e1ffbca..9a74ddc 100644
--- a/src/com/android/providers/media/TranscodeHelper.java
+++ b/src/com/android/providers/media/TranscodeHelper.java
@@ -437,8 +437,8 @@
CountDownLatch latch = null;
long startTime = SystemClock.elapsedRealtime();
boolean result = false;
- int errorCode = TranscodingSession.ERROR_NONE;
- int failureReason = TRANSCODING_DATA__FAILURE_CAUSE__CAUSE_UNKNOWN;
+ int errorCode = TranscodingSession.ERROR_SERVICE_DIED;
+ int failureReason = TRANSCODING_DATA__FAILURE_CAUSE__TRANSCODING_SERVICE_ERROR;
try {
synchronized (mLock) {
@@ -481,6 +481,16 @@
transcodingSession.cancel();
}
} finally {
+ if (storageSession == null) {
+ Log.w(TAG, "Failed to create a StorageTranscodingSession");
+ // We were unable to even queue the request. Which means the media service is
+ // in a very bad state
+ reportTranscodingResult(uid, result, errorCode, failureReason,
+ SystemClock.elapsedRealtime() - startTime, reason,
+ src, dst, false /* hasAnr */);
+ return false;
+ }
+
storageSession.notifyFinished(failureReason, errorCode);
if (errorCode == TranscodingSession.ERROR_DROPPED_BY_SERVICE) {
// If the transcoding service drops a request for a uid the uid will be denied
diff --git a/src/com/android/providers/media/photopicker/data/LocalItemsProvider.java b/src/com/android/providers/media/photopicker/data/LocalItemsProvider.java
index 3e1945f..ed7f770 100644
--- a/src/com/android/providers/media/photopicker/data/LocalItemsProvider.java
+++ b/src/com/android/providers/media/photopicker/data/LocalItemsProvider.java
@@ -73,9 +73,8 @@
* @param category the category of items to return, {@link Category.CategoryType} are supported.
* {@code null} defaults to {@link Category#CATEGORY_DEFAULT} which returns
* items from all categories.
- * @param offset the offset after which to return items. Does not respect non-positive
- * values.
- * @param limit the limit of items to return. Does not respect non-positive values.
+ * @param offset the offset after which to return items.
+ * @param limit the limit of number of items to return.
* @param mimeType the mime type of item, only {@code image/*} or {@code video/*} is an
* acceptable mimeType here. Any other mimeType than image/video throws error.
* {@code null} returns all images/videos that are scanned by
@@ -202,12 +201,9 @@
extras.putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
extras.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
MediaColumns.DATE_TAKEN + " DESC");
- if (offset > 0) {
- extras.putInt(ContentResolver.QUERY_ARG_OFFSET, offset);
- }
- if (limit > 0) {
- extras.putString(ContentResolver.QUERY_ARG_LIMIT, String.valueOf(limit));
- }
+ extras.putInt(ContentResolver.QUERY_ARG_OFFSET, offset);
+ extras.putString(ContentResolver.QUERY_ARG_LIMIT, String.valueOf(limit));
+
return client.query(contentUri, projection, extras, null);
} catch (RemoteException ignored) {
// Do nothing, return null.
diff --git a/src/com/android/providers/media/photopicker/data/model/Item.java b/src/com/android/providers/media/photopicker/data/model/Item.java
index 4780027..f57c5bb 100644
--- a/src/com/android/providers/media/photopicker/data/model/Item.java
+++ b/src/com/android/providers/media/photopicker/data/model/Item.java
@@ -19,7 +19,6 @@
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
-import android.provider.MediaStore.Files.FileColumns;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -41,14 +40,16 @@
public static String DISPLAY_NAME = MediaStore.MediaColumns.DISPLAY_NAME;
public static String VOLUME_NAME = MediaStore.MediaColumns.VOLUME_NAME;
public static String DATE_TAKEN = MediaStore.MediaColumns.DATE_TAKEN;
+ public static String DURATION = MediaStore.MediaColumns.DURATION;
public static String USER_ID = MediaStore.Files.FileColumns._USER_ID;
private static final String[] ALL_COLUMNS = {
- ItemColumns.ID,
- ItemColumns.MIME_TYPE,
- ItemColumns.DISPLAY_NAME,
- ItemColumns.VOLUME_NAME,
- ItemColumns.DATE_TAKEN,
+ ID,
+ MIME_TYPE,
+ DISPLAY_NAME,
+ VOLUME_NAME,
+ DATE_TAKEN,
+ DURATION,
// TODO: Unable to query USER_ID
// ItemColumns.USER_ID,
};
@@ -57,8 +58,9 @@
}
private long mId;
- private long mDataTaken;
+ private long mDateTaken;
private String mDisplayName;
+ private int mDuration;
private String mMimeType;
private String mVolumeName;
private Uri mUri;
@@ -85,12 +87,16 @@
return mDisplayName;
}
+ public int getDuration() {
+ return mDuration;
+ }
+
public String getMimeType() {
return mMimeType;
}
- public long getDataTaken() {
- return mDataTaken;
+ public long getDateTaken() {
+ return mDateTaken;
}
public String getVolumeName() {
@@ -111,8 +117,9 @@
mId = getCursorLong(cursor, ItemColumns.ID);
mMimeType = getCursorString(cursor, ItemColumns.MIME_TYPE);
mDisplayName = getCursorString(cursor, ItemColumns.DISPLAY_NAME);
- mDataTaken = getCursorLong(cursor, ItemColumns.DATE_TAKEN);
+ mDateTaken = getCursorLong(cursor, ItemColumns.DATE_TAKEN);
mVolumeName = getCursorString(cursor, ItemColumns.VOLUME_NAME);
+ mDuration = getCursorInt(cursor, ItemColumns.DURATION);
// TODO (b/188867567): Currently, we only has local data source,
// get the uri from provider
diff --git a/src/com/android/providers/media/util/UserCache.java b/src/com/android/providers/media/util/UserCache.java
index 885e07e..276e3f8 100644
--- a/src/com/android/providers/media/util/UserCache.java
+++ b/src/com/android/providers/media/util/UserCache.java
@@ -84,6 +84,12 @@
}
}
+ public @NonNull List<UserHandle> getUsersCached() {
+ synchronized (mLock) {
+ return (List<UserHandle>) mUsers.clone();
+ }
+ }
+
public @NonNull Context getContextForUser(@NonNull UserHandle user) {
Context userContext;
synchronized (mLock) {
diff --git a/tests/src/com/android/providers/media/photopicker/LocalItemsProviderTest.java b/tests/src/com/android/providers/media/photopicker/LocalItemsProviderTest.java
index 4e1a577..84d3d10 100644
--- a/tests/src/com/android/providers/media/photopicker/LocalItemsProviderTest.java
+++ b/tests/src/com/android/providers/media/photopicker/LocalItemsProviderTest.java
@@ -304,7 +304,8 @@
*/
@Test
public void testGetItems() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, null);
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ null);
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
@@ -314,7 +315,8 @@
File imageFile = assertCreateNewImage();
File videoFile = assertCreateNewVideo();
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, null);
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ null);
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -336,7 +338,8 @@
*/
@Test
public void testGetItems_nonMedia() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, null);
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ null);
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
@@ -347,7 +350,8 @@
File imageFileHidden = assertCreateNewImage(hiddenDir);
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, null);
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ null);
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -367,7 +371,8 @@
*/
@Test
public void testGetItemsImages() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, "image/*");
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "image/*");
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
@@ -377,7 +382,8 @@
File imageFile = assertCreateNewImage();
File videoFile = assertCreateNewVideo();
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, "image/*");
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "image/*");
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -399,14 +405,16 @@
*/
@Test
public void testGetItemsImages_png() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, "image/png");
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "image/png");
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
// Create a jpg file image. Tests negative use case, this should not be returned below.
File imageFile = assertCreateNewImage();
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, "image/png");
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "image/png");
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -424,7 +432,8 @@
*/
@Test
public void testGetItemsImages_nonMedia() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, "image/*");
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "image/*");
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
@@ -435,7 +444,8 @@
File imageFileHidden = assertCreateNewImage(hiddenDir);
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, "image/*");
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "image/*");
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -455,7 +465,8 @@
*/
@Test
public void testGetItemsVideos() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, "video/*");
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "video/*");
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
@@ -465,7 +476,8 @@
File imageFile = assertCreateNewImage();
File videoFile = assertCreateNewVideo();
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, "video/*");
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "video/*");
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -487,14 +499,16 @@
*/
@Test
public void testGetItemsVideos_mp4() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, "video/mp4");
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "video/mp4");
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
// Create a mp4 video file. Tests positive use case, this should be returned below.
File videoFile = assertCreateNewVideo();
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, "video/mp4");
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "video/mp4");
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -512,7 +526,8 @@
*/
@Test
public void testGetItemsVideos_nonMedia() throws Exception {
- Cursor res = sLocalItemsProvider.getItems(null, 0, 0, "video/*");
+ Cursor res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ /* limit */ -1, /* mimeType */ "video/*");
assertThat(res).isNotNull();
final int initialCountOfItems = res.getCount();
@@ -522,7 +537,8 @@
File imageFileHidden = assertCreateNewImage(hiddenDir);
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
- res = sLocalItemsProvider.getItems(null, 0, 0, "video/*");
+ res = sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "video/*");
assertThat(res).isNotNull();
final int laterCountOfItems = res.getCount();
@@ -543,7 +559,8 @@
@Test
public void testGetItemsInvalidParam() throws Exception {
try {
- sLocalItemsProvider.getItems(null, 0, 0, "audio/*");
+ sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "audio/*");
fail("Expected IllegalArgumentException for audio mimeType");
} catch (IllegalArgumentException expected) {
// Expected flow
@@ -559,7 +576,8 @@
@Test
public void testGetItemsAllMimeType() throws Exception {
try {
- sLocalItemsProvider.getItems(null, 0, 0, "*/*");
+ sLocalItemsProvider.getItems(/* category */ null, /* offset */ 0, /* limit */ -1,
+ /* mimeType */ "*/*");
fail("Expected IllegalArgumentException for audio mimeType");
} catch (IllegalArgumentException expected) {
// Expected flow
diff --git a/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java b/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
index 8df3889..f94aa67 100644
--- a/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/model/ItemTest.java
@@ -34,29 +34,31 @@
@Test
public void testConstructor() {
final long id = 1;
- final long dataTaken = 12345678l;
+ final long dateTaken = 12345678l;
final String mimeType = "image/png";
final String displayName = "123.png";
final String volumeName = "primary";
+ final int duration = 1000;
final Cursor cursor = generateCursorForItem(id, mimeType, displayName, volumeName,
- dataTaken);
+ dateTaken, duration);
cursor.moveToFirst();
final Item item = new Item(cursor);
assertThat(item.getId()).isEqualTo(id);
- assertThat(item.getDataTaken()).isEqualTo(dataTaken);
+ assertThat(item.getDateTaken()).isEqualTo(dateTaken);
assertThat(item.getDisplayName()).isEqualTo(displayName);
assertThat(item.getMimeType()).isEqualTo(mimeType);
assertThat(item.getVolumeName()).isEqualTo(volumeName);
+ assertThat(item.getDuration()).isEqualTo(duration);
}
private static Cursor generateCursorForItem(long id, String mimeType,
- String displayName, String volumeName, long dataTaken) {
+ String displayName, String volumeName, long dateTaken, int duration) {
final MatrixCursor cursor = new MatrixCursor(
ItemColumns.ALL_COLUMNS_LIST.toArray(new String[0]));
- cursor.addRow(new Object[] {id, mimeType, displayName, volumeName, dataTaken});
+ cursor.addRow(new Object[] {id, mimeType, displayName, volumeName, dateTaken, duration});
return cursor;
}
}