summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Steve McKay <smckay@google.com> 2017-08-01 15:02:50 -0700
committer Steve McKay <smckay@google.com> 2017-08-02 09:45:26 -0700
commit5a10ff1828eabae2aabcc4981ece7b9aff772d3b (patch)
treebc2b6fea630415737f63a76045b0a67ca7a62ea0
parent200ae37936ef34adf1c08e318df7eba4a73499da (diff)
Add metadata support to MTP docs provider.
Remove convenience method from DocumentsProvider, clients to use MetadataReader directly. Concentrate mimetype checking in MetadataReader.isSupportedType. Update FileSystemProvider to use MetadataReader directly. Test: Updated mtp tests. Other functioanlity manually verified. Change-Id: Ie1e3d3092b53107f6c980c18b1451290dd2a9653
-rw-r--r--core/java/android/provider/DocumentsProvider.java26
-rw-r--r--core/java/android/provider/MetadataReader.java42
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java24
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java4
-rw-r--r--packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java35
-rw-r--r--packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java3
-rw-r--r--packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java6
7 files changed, 82 insertions, 58 deletions
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 2298134d469b..81b1921dd809 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -66,8 +66,6 @@ import android.util.Log;
import libcore.io.IoUtils;
import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
import java.util.LinkedList;
import java.util.Objects;
@@ -635,30 +633,6 @@ public abstract class DocumentsProvider extends ContentProvider {
}
/**
- * Returns metadata for arbitrary file given its stream and mimetype.
- *
- * <p><b>Note: Providers should only call this with streams for locally cached resources.
- * Use of network backed streams is inadvisable for performance reasons.
- *
- * @param stream The input stream. Should be backed by locally cached content.
- * Client retains ownership of the stream.
- * @param mimeType The mime type of the file.
- * @param tags The list of tags to load, if known. Pass null to get a default set.
- * @return Bundle containing any metadata found.
- * @throws IOException in the event of an error reading metadata.
- *
- * @hide
- */
- protected Bundle getDocumentMetadataFromStream(InputStream stream, String mimeType)
- throws IOException {
- Bundle metadata = new Bundle();
- // TODO: Remove the last null arg from MetadataReader. It was the "tags" value,
- // the has been removed from the getDocumentMetadata method.
- MetadataReader.getMetadata(metadata, stream, mimeType, null);
- return metadata;
- }
-
- /**
* Return concrete MIME type of the requested document. Must match the value
* of {@link Document#COLUMN_MIME_TYPE} for this document. The default
* implementation queries {@link #queryDocument(String, String[])}, so
diff --git a/core/java/android/provider/MetadataReader.java b/core/java/android/provider/MetadataReader.java
index 5aa5f72ab788..2d23c4eee130 100644
--- a/core/java/android/provider/MetadataReader.java
+++ b/core/java/android/provider/MetadataReader.java
@@ -36,29 +36,31 @@ import java.util.Map;
*/
public final class MetadataReader {
- private MetadataReader() {
- }
+ private MetadataReader() {}
private static final String[] DEFAULT_EXIF_TAGS = {
- ExifInterface.TAG_IMAGE_WIDTH,
- ExifInterface.TAG_IMAGE_LENGTH,
+ ExifInterface.TAG_APERTURE,
+ ExifInterface.TAG_COPYRIGHT,
ExifInterface.TAG_DATETIME,
+ ExifInterface.TAG_EXPOSURE_TIME,
+ ExifInterface.TAG_F_NUMBER,
ExifInterface.TAG_GPS_LATITUDE,
ExifInterface.TAG_GPS_LATITUDE_REF,
ExifInterface.TAG_GPS_LONGITUDE,
ExifInterface.TAG_GPS_LONGITUDE_REF,
+ ExifInterface.TAG_IMAGE_WIDTH,
+ ExifInterface.TAG_IMAGE_LENGTH,
ExifInterface.TAG_MAKE,
ExifInterface.TAG_MODEL,
- ExifInterface.TAG_APERTURE,
- ExifInterface.TAG_SHUTTER_SPEED_VALUE
+ ExifInterface.TAG_ORIENTATION,
+ ExifInterface.TAG_SHUTTER_SPEED_VALUE,
};
- private static final Map<String, Integer> TYPE_MAPPING = new HashMap<>();
- private static final String[] ALL_KNOWN_EXIF_KEYS;
private static final int TYPE_INT = 0;
private static final int TYPE_DOUBLE = 1;
private static final int TYPE_STRING = 2;
+ private static final Map<String, Integer> TYPE_MAPPING = new HashMap<>();
static {
// TODO: Move this over to ExifInterface.java
// Since each ExifInterface item has a type, and there's currently no way to get the type
@@ -198,11 +200,19 @@ public final class MetadataReader {
TYPE_MAPPING.put(ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER, TYPE_INT);
TYPE_MAPPING.put(ExifInterface.TAG_RW2_SENSOR_TOP_BORDER, TYPE_INT);
TYPE_MAPPING.put(ExifInterface.TAG_RW2_ISO, TYPE_INT);
- ALL_KNOWN_EXIF_KEYS = TYPE_MAPPING.keySet().toArray(new String[TYPE_MAPPING.size()]);
}
private static final String JPG_MIME_TYPE = "image/jpg";
private static final String JPEG_MIME_TYPE = "image/jpeg";
+
+ /**
+ * Returns true if caller can generally expect to get metadata results
+ * for the supplied mimetype.
+ */
+ public static boolean isSupportedMimeType(String mimeType) {
+ return JPG_MIME_TYPE.equals(mimeType) || JPEG_MIME_TYPE.equals(mimeType);
+ }
+
/**
* Generic metadata retrieval method that can retrieve any available metadata from a given doc
* Currently only functions for exifdata
@@ -228,10 +238,9 @@ public final class MetadataReader {
*/
public static void getMetadata(Bundle metadata, InputStream stream, String mimeType,
@Nullable String[] tags) throws IOException {
- List<String> metadataTypes = new ArrayList();
- if (mimeType.equals(JPG_MIME_TYPE) || mimeType.equals(JPEG_MIME_TYPE)) {
- ExifInterface exifInterface = new ExifInterface(stream);
- Bundle exifData = getExifData(exifInterface, tags);
+ List<String> metadataTypes = new ArrayList<>();
+ if (isSupportedMimeType(mimeType)) {
+ Bundle exifData = getExifData(stream, tags);
if (exifData.size() > 0) {
metadata.putBundle(DocumentsContract.METADATA_EXIF, exifData);
metadataTypes.add(DocumentsContract.METADATA_EXIF);
@@ -246,8 +255,7 @@ public final class MetadataReader {
/**
* Helper method that is called if getMetadata is called for an image mimeType.
*
- * @param exif the bundle to which we add exif data.
- * @param exifInterface an ExifInterface for an image
+ * @param stream the input stream from which to extra data.
* @param tags a list of ExifInterface tags that are used to retrieve data.
* if null, returns a default set of data from the following keys:
* ExifInterface.TAG_IMAGE_WIDTH,
@@ -262,11 +270,13 @@ public final class MetadataReader {
* ExifInterface.TAG_APERTURE,
* ExifInterface.TAG_SHUTTER_SPEED_VALUE
*/
- private static Bundle getExifData(ExifInterface exifInterface, @Nullable String[] tags)
+ private static Bundle getExifData(InputStream stream, @Nullable String[] tags)
throws IOException {
if (tags == null) {
tags = DEFAULT_EXIF_TAGS;
}
+
+ ExifInterface exifInterface = new ExifInterface(stream);
Bundle exif = new Bundle();
for (String tag : tags) {
if (TYPE_MAPPING.get(tag).equals(TYPE_INT)) {
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 8c805accfbc5..e3a9b7e41d5b 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,7 +28,6 @@ import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
import android.net.Uri;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileObserver;
@@ -39,6 +38,7 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsProvider;
import android.provider.MediaStore;
+import android.provider.MetadataReader;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -52,6 +52,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -120,26 +121,31 @@ public abstract class FileSystemProvider extends DocumentsProvider {
if (!file.isFile()) {
Log.w(TAG, "Can't stream non-regular file. Returning empty metadata.");
- return Bundle.EMPTY;
+ return null;
}
if (!file.canRead()) {
Log.w(TAG, "Can't stream non-readable file. Returning empty metadata.");
- return Bundle.EMPTY;
+ return null;
}
- String filePath = file.getAbsolutePath();
- FileInputStream stream = new FileInputStream(filePath);
+ String mimeType = getTypeForFile(file);
+ if (!MetadataReader.isSupportedMimeType(mimeType)) {
+ return null;
+ }
+ InputStream stream = null;
try {
- return getDocumentMetadataFromStream(stream, getTypeForFile(file));
+ Bundle metadata = new Bundle();
+ stream = new FileInputStream(file.getAbsolutePath());
+ MetadataReader.getMetadata(metadata, stream, mimeType, null);
+ return metadata;
} catch (IOException e) {
Log.e(TAG, "An error occurred retrieving the metadata", e);
+ return null;
} finally {
IoUtils.closeQuietly(stream);
}
-
- return null;
}
protected final List<String> findDocumentPath(File parent, File doc)
@@ -502,7 +508,7 @@ public abstract class FileSystemProvider extends DocumentsProvider {
}
protected boolean typeSupportsMetadata(String mimeType) {
- return MIMETYPE_JPG.equals(mimeType) || MIMETYPE_JPEG.equals(mimeType);
+ return MetadataReader.isSupportedMimeType(mimeType);
}
private static String getTypeForName(String name) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 9b5982b96e31..f243b51d6769 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -34,6 +34,7 @@ import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.provider.DocumentsContract;
+import android.provider.MetadataReader;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
@@ -900,6 +901,9 @@ class MtpDatabase {
protectionState == MtpConstants.PROTECTION_STATUS_NONE) {
flag |= Document.FLAG_DIR_SUPPORTS_CREATE;
}
+ if (MetadataReader.isSupportedMimeType(mimeType)) {
+ flag |= Document.FLAG_SUPPORTS_METADATA;
+ }
if (thumbnailSize > 0) {
flag |= Document.FLAG_SUPPORTS_THUMBNAIL;
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index eb2d8aa71955..8c8116bd342a 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -16,6 +16,7 @@
package com.android.mtp;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -37,11 +38,12 @@ import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.ProxyFileDescriptorCallback;
import android.os.storage.StorageManager;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
-import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
+import android.provider.MetadataReader;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
@@ -50,14 +52,16 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import libcore.io.IoUtils;
+
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
-import libcore.io.IoUtils;
/**
* DocumentsProvider for MTP devices.
@@ -107,7 +111,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
mResources = getContext().getResources();
mMtpManager = new MtpManager(getContext());
mResolver = getContext().getContentResolver();
- mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
+ mDeviceToolkits = new HashMap<>();
mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
mIntentSender = new ServiceIntentSender(getContext());
@@ -151,7 +155,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
mResources = resources;
mMtpManager = mtpManager;
mResolver = resolver;
- mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
+ mDeviceToolkits = new HashMap<>();
mDatabase = database;
mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
mIntentSender = intentSender;
@@ -548,6 +552,29 @@ public class MtpDocumentsProvider extends DocumentsProvider {
}
}
+ @Override
+ public @Nullable Bundle getDocumentMetadata(String docId) throws FileNotFoundException {
+ String mimeType = getDocumentType(docId);
+
+ if (!MetadataReader.isSupportedMimeType(mimeType)) {
+ return null;
+ }
+
+ InputStream stream = null;
+ try {
+ stream = new ParcelFileDescriptor.AutoCloseInputStream(
+ openDocument(docId, "r", null));
+ Bundle metadata = new Bundle();
+ MetadataReader.getMetadata(metadata, stream, mimeType, null);
+ return metadata;
+ } catch (IOException e) {
+ Log.e(TAG, "An error occurred retrieving the metadata", e);
+ return null;
+ } finally {
+ IoUtils.closeQuietly(stream);
+ }
+ }
+
void openDevice(int deviceId) throws IOException {
synchronized (mDeviceListLock) {
if (mDeviceToolkits.containsKey(deviceId)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 7fe6cb963522..32b169e6fa9f 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -237,7 +237,8 @@ public class MtpDatabaseTest extends AndroidTestCase {
assertEquals(
COLUMN_FLAGS,
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
- DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+ DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
+ DocumentsContract.Document.FLAG_SUPPORTS_METADATA,
cursor.getInt(9));
assertEquals(2 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
assertEquals(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 3fa5eb581ef7..65c86dfb6d4c 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -329,7 +329,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
assertEquals(
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
- DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
+ DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL |
+ DocumentsContract.Document.FLAG_SUPPORTS_METADATA,
cursor.getInt(4));
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
}
@@ -474,7 +475,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
assertEquals("image/jpeg", cursor.getString(1));
assertEquals("image.jpg", cursor.getString(2));
assertEquals(0, cursor.getLong(3));
- assertEquals(Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
+ assertEquals(Document.FLAG_SUPPORTS_THUMBNAIL
+ | Document.FLAG_SUPPORTS_METADATA, cursor.getInt(4));
assertEquals(1024 * 1024 * 5, cursor.getInt(5));
cursor.close();