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

Commit 2951c6d0 authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "MTP: Use media provider database to implement MTP device support."

parents 07e1b1dd d21eac9c
Loading
Loading
Loading
Loading
+38 −0
Original line number Diff line number Diff line
@@ -239,6 +239,44 @@ public final class MediaStore {
        public static final String MIME_TYPE = "mime_type";
     }



    /**
     * Media provider interface used by MTP implementation.
     * @hide
     */
    public static final class MtpObjects {

        public static Uri getContentUri(String volumeName) {
            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
                    "/object");
        }

        public static final Uri getContentUri(String volumeName,
                long objectId) {
            return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
                    + "/object/" + objectId);
        }

        /**
         * Fields for master table for all media files.
         * Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
         */
        public interface ObjectColumns extends MediaColumns {
            /**
             * The MTP format code of the file
             * <P>Type: INTEGER</P>
             */
            public static final String FORMAT = "format";

            /**
             * The index of the parent directory of the file
             * <P>Type: INTEGER</P>
             */
            public static final String PARENT = "parent";
        }
    }

    /**
     * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
     * to be accessed elsewhere.
+57 −26
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.ContentValues;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.provider.Mtp;
import android.media.DecoderCapabilities;
import android.media.DecoderCapabilities.VideoDecoder;
import android.media.DecoderCapabilities.AudioDecoder;
@@ -100,11 +101,24 @@ public class MediaFile {
            = new HashMap<String, MediaFileType>();
    private static HashMap<String, Integer> sMimeTypeMap
            = new HashMap<String, Integer>();
    // maps file extension to MTP format code
    private static HashMap<String, Integer> sFileTypeToFormatMap
            = new HashMap<String, Integer>();
    // maps mime type to MTP format code
    private static HashMap<String, Integer> sMimeTypeToFormatMap
            = new HashMap<String, Integer>();

    static void addFileType(String extension, int fileType, String mimeType) {
        sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType));
        sMimeTypeMap.put(mimeType, Integer.valueOf(fileType));
    }

    static void addFileType(String extension, int fileType, String mimeType, int mtpFormatCode) {
        addFileType(extension, fileType, mimeType);
        sFileTypeToFormatMap.put(extension, Integer.valueOf(mtpFormatCode));
        sMimeTypeToFormatMap.put(mimeType, Integer.valueOf(mtpFormatCode));
    }

    private static boolean isWMAEnabled() {
        List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders();
        for (AudioDecoder decoder: decoders) {
@@ -126,17 +140,17 @@ public class MediaFile {
    }

    static {
        addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg");
        addFileType("M4A", FILE_TYPE_M4A, "audio/mp4");
        addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav");
        addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg", Mtp.Object.FORMAT_MP3);
        addFileType("M4A", FILE_TYPE_M4A, "audio/mp4", Mtp.Object.FORMAT_MPEG);
        addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav", Mtp.Object.FORMAT_WAV);
        addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
        addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb");
        if (isWMAEnabled()) {
            addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
            addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma", Mtp.Object.FORMAT_WMA);
        }
        addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
        addFileType("OGA", FILE_TYPE_OGG, "application/ogg");
        addFileType("AAC", FILE_TYPE_AAC, "audio/aac");
        addFileType("OGG", FILE_TYPE_OGG, "application/ogg", Mtp.Object.FORMAT_OGG);
        addFileType("OGA", FILE_TYPE_OGG, "application/ogg", Mtp.Object.FORMAT_OGG);
        addFileType("AAC", FILE_TYPE_AAC, "audio/aac", Mtp.Object.FORMAT_AAC);
        addFileType("MKA", FILE_TYPE_MKA, "audio/x-matroska");
 
        addFileType("MID", FILE_TYPE_MID, "audio/midi");
@@ -148,32 +162,32 @@ public class MediaFile {
        addFileType("RTX", FILE_TYPE_MID, "audio/midi");
        addFileType("OTA", FILE_TYPE_MID, "audio/midi");
        
        addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg");
        addFileType("MP4", FILE_TYPE_MP4, "video/mp4");
        addFileType("M4V", FILE_TYPE_M4V, "video/mp4");
        addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp");
        addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
        addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
        addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
        addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg", Mtp.Object.FORMAT_MPEG);
        addFileType("MP4", FILE_TYPE_MP4, "video/mp4", Mtp.Object.FORMAT_MPEG);
        addFileType("M4V", FILE_TYPE_M4V, "video/mp4", Mtp.Object.FORMAT_MPEG);
        addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp",  Mtp.Object.FORMAT_3GP_CONTAINER);
        addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp", Mtp.Object.FORMAT_3GP_CONTAINER);
        addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2", Mtp.Object.FORMAT_3GP_CONTAINER);
        addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2", Mtp.Object.FORMAT_3GP_CONTAINER);
        addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
        addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
        addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");

        if (isWMVEnabled()) {
            addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
            addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv", Mtp.Object.FORMAT_WMV);
            addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
        }

        addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg");
        addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg");
        addFileType("GIF", FILE_TYPE_GIF, "image/gif");
        addFileType("PNG", FILE_TYPE_PNG, "image/png");
        addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp");
        addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg", Mtp.Object.FORMAT_EXIF_JPEG);
        addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg", Mtp.Object.FORMAT_EXIF_JPEG);
        addFileType("GIF", FILE_TYPE_GIF, "image/gif", Mtp.Object.FORMAT_GIF);
        addFileType("PNG", FILE_TYPE_PNG, "image/png", Mtp.Object.FORMAT_PNG);
        addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", Mtp.Object.FORMAT_BMP);
        addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
 
        addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl");
        addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls");
        addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl");
        addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", Mtp.Object.FORMAT_M3U_PLAYLIST);
        addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls", Mtp.Object.FORMAT_PLS_PLAYLIST);
        addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl", Mtp.Object.FORMAT_WPL_PLAYLIST);

        // compute file extensions list for native Media Scanner
        StringBuilder builder = new StringBuilder();
@@ -222,4 +236,21 @@ public class MediaFile {
        return (value == null ? 0 : value.intValue());
    }

    public static int getFormatCode(String fileName, String mimeType) {
        if (mimeType != null) {
            Integer value = sMimeTypeToFormatMap.get(mimeType);
            if (value != null) {
                return value.intValue();
            }
        }
        int lastDot = fileName.lastIndexOf('.');
        if (lastDot > 0) {
            String extension = fileName.substring(lastDot + 1);
            Integer value = sFileTypeToFormatMap.get(extension);
            if (value != null) {
                return value.intValue();
            }
        }
        return Mtp.Object.FORMAT_UNDEFINED;
    }
}
+223 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.content.Context;
import android.content.IContentProvider;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.MediaStore.MtpObjects;
import android.util.Log;

/**
 * {@hide}
 */
public class MtpDatabase {

    private static final String TAG = "MtpDatabase";

    private final IContentProvider mMediaProvider;
    private final String mVolumeName;
    private final Uri mObjectsUri;

    private static final String[] ID_PROJECTION = new String[] {
            MtpObjects.ObjectColumns._ID, // 0
    };
    private static final String[] PATH_SIZE_PROJECTION = new String[] {
            MtpObjects.ObjectColumns._ID, // 0
            MtpObjects.ObjectColumns.DATA, // 1
            MtpObjects.ObjectColumns.SIZE, // 2
    };
    private static final String[] OBJECT_INFO_PROJECTION = new String[] {
            MtpObjects.ObjectColumns._ID, // 0
            MtpObjects.ObjectColumns.DATA, // 1
            MtpObjects.ObjectColumns.FORMAT, // 2
            MtpObjects.ObjectColumns.PARENT, // 3
            MtpObjects.ObjectColumns.SIZE, // 4
            MtpObjects.ObjectColumns.DATE_MODIFIED, // 5
    };
    private static final String ID_WHERE = MtpObjects.ObjectColumns._ID + "=?";
    private static final String PATH_WHERE = MtpObjects.ObjectColumns.DATA + "=?";
    private static final String PARENT_WHERE = MtpObjects.ObjectColumns.PARENT + "=?";
    private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
                                            + MtpObjects.ObjectColumns.FORMAT + "=?";

    static {
        System.loadLibrary("media_jni");
    }

    public MtpDatabase(Context context, String volumeName) {
        native_setup();

        mMediaProvider = context.getContentResolver().acquireProvider("media");
        mVolumeName = volumeName;
        mObjectsUri = MtpObjects.getContentUri(volumeName);
    }

    @Override
    protected void finalize() {
        native_finalize();
    }

    // called from native code
    private int getObjectHandle(String path) {
        Log.d(TAG, "getObjectHandle " + path);
        Cursor c = null;
        try {
            c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PATH_WHERE, new String[] { path }, null);
            if (c != null && c.moveToNext()) {
                return c.getInt(0);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in getObjectHandle", e);
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return 0;
    }

    private int addFile(String path, int format, int parent,
                         int storage, long size, long modified) {
        Log.d(TAG, "addFile " + path);
        return 0;
    }

    private int[] getObjectList(int storageID, int format, int parent) {
        // we can ignore storageID until we support multiple storages
        Log.d(TAG, "getObjectList parent: " + parent);
        Cursor c = null;
        try {
            if (format != 0) {
                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PARENT_FORMAT_WHERE,
                            new String[] { Integer.toString(parent), Integer.toString(format) },
                             null);
            } else {
                c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
                            PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
            }
            if (c == null) {
                Log.d(TAG, "null cursor");
                return null;
            }
            int count = c.getCount();
            if (count > 0) {
                int[] result = new int[count];
                for (int i = 0; i < count; i++) {
                    c.moveToNext();
                    result[i] = c.getInt(0);
                }
                Log.d(TAG, "returning " + result);
                return result;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in getObjectList", e);
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return null;
    }

    private int getObjectProperty(int handle, int property,
                            long[] outIntValue, char[] outStringValue) {
        Log.d(TAG, "getObjectProperty: " + property);
        return 0;
    }

    private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
                        char[] outName, long[] outSizeModified) {
        Log.d(TAG, "getObjectInfo: " + handle);
        Cursor c = null;
        try {
            c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
                            ID_WHERE, new String[] {  Integer.toString(handle) }, null);
            if (c != null && c.moveToNext()) {
                outStorageFormatParent[0] = 0x00010001;
                outStorageFormatParent[1] = c.getInt(2);
                outStorageFormatParent[2] = c.getInt(3);

                // extract name from path
                String path = c.getString(1);
                int lastSlash = path.lastIndexOf('/');
                int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
                int end = path.length();
                if (end - start > 255) {
                    end = start + 255;
                }
                path.getChars(start, end, outName, 0);
                outName[end - start] = 0;

                outSizeModified[0] = c.getLong(4);
                outSizeModified[1] = c.getLong(5);
                return true;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in getObjectProperty", e);
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return false;
    }

    private boolean getObjectFilePath(int handle, char[] outFilePath, long[] outFileLength) {
        Log.d(TAG, "getObjectFilePath: " + handle);
        Cursor c = null;
        try {
            c = mMediaProvider.query(mObjectsUri, PATH_SIZE_PROJECTION,
                            ID_WHERE, new String[] {  Integer.toString(handle) }, null);
            if (c != null && c.moveToNext()) {
                String path = c.getString(1);
                path.getChars(0, path.length(), outFilePath, 0);
                outFilePath[path.length()] = 0;
                outFileLength[0] = c.getLong(2);
                return true;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in getObjectFilePath", e);
        } finally {
            if (c != null) {
                c.close();
            }
        }
        return false;
    }

    private boolean deleteFile(int handle) {
        Log.d(TAG, "deleteFile: " + handle);
        Uri uri = MtpObjects.getContentUri(mVolumeName, handle);
        try {
            return (mMediaProvider.delete(uri, null, null) == 1);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in deleteFile", e);
            return false;
        }
    }

    // used by the JNI code
    private int mNativeContext;

    private native final void native_setup();
    private native final void native_finalize();
}
+3 −3
Original line number Diff line number Diff line
@@ -30,8 +30,8 @@ public class MtpServer {
        System.loadLibrary("media_jni");
    }

    public MtpServer(String storagePath, String databasePath) {
        native_setup(storagePath, databasePath);
    public MtpServer(MtpDatabase database, String storagePath) {
        native_setup(database, storagePath);
    }

    @Override
@@ -50,7 +50,7 @@ public class MtpServer {
    // used by the JNI code
    private int mNativeContext;

    private native final void native_setup(String storagePath, String databasePath);
    private native final void native_setup(MtpDatabase database, String storagePath);
    private native final void native_finalize();
    private native final void native_start();
    private native final void native_stop();
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ LOCAL_SRC_FILES:= \
    android_media_AmrInputStream.cpp \
	android_media_MtpClient.cpp \
	android_media_MtpCursor.cpp \
	android_media_MtpDatabase.cpp \
	android_media_MtpServer.cpp \

LOCAL_SHARED_LIBRARIES := \
Loading