Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f7637d7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add metadata support to MTP docs provider."

parents a2a58679 5a10ff18
Loading
Loading
Loading
Loading
+0 −26
Original line number Diff line number Diff line
@@ -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;

@@ -634,30 +632,6 @@ public abstract class DocumentsProvider extends ContentProvider {
        throw new UnsupportedOperationException("Metadata not supported");
    }

    /**
     * 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
+26 −16
Original line number Diff line number Diff line
@@ -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)) {
+15 −8
Original line number Diff line number Diff line
@@ -38,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;
@@ -51,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;
@@ -118,26 +120,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)
@@ -491,7 +498,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) {
+4 −0
Original line number Diff line number Diff line
@@ -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;
        }
+31 −4
Original line number Diff line number Diff line
@@ -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)) {
Loading