Merge "MTP: Use media provider database to implement MTP device support."
This commit is contained in:
committed by
Android (Google) Code Review
commit
2951c6d057
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
@@ -96,15 +97,28 @@ public class MediaFile {
|
||||
}
|
||||
}
|
||||
|
||||
private static HashMap<String, MediaFileType> sFileTypeMap
|
||||
private static HashMap<String, MediaFileType> sFileTypeMap
|
||||
= new HashMap<String, MediaFileType>();
|
||||
private static HashMap<String, Integer> sMimeTypeMap
|
||||
= new HashMap<String, Integer>();
|
||||
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
media/java/android/media/MtpDatabase.java
Normal file
223
media/java/android/media/MtpDatabase.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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 := \
|
||||
|
||||
@@ -766,6 +766,7 @@ extern int register_android_media_ResampleInputStream(JNIEnv *env);
|
||||
extern int register_android_media_MediaProfiles(JNIEnv *env);
|
||||
extern int register_android_media_MtpClient(JNIEnv *env);
|
||||
extern int register_android_media_MtpCursor(JNIEnv *env);
|
||||
extern int register_android_media_MtpDatabase(JNIEnv *env);
|
||||
extern int register_android_media_MtpServer(JNIEnv *env);
|
||||
|
||||
#ifndef NO_OPENCORE
|
||||
@@ -830,6 +831,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (register_android_media_MtpDatabase(env) < 0) {
|
||||
LOGE("ERROR: MtpDatabase native registration failed");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (register_android_media_MtpServer(env) < 0) {
|
||||
LOGE("ERROR: MtpServer native registration failed");
|
||||
goto bail;
|
||||
|
||||
451
media/jni/android_media_MtpDatabase.cpp
Normal file
451
media/jni/android_media_MtpDatabase.cpp
Normal file
@@ -0,0 +1,451 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "MtpDatabaseJNI"
|
||||
#include "utils/Log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "JNIHelp.h"
|
||||
#include "android_runtime/AndroidRuntime.h"
|
||||
|
||||
#include "MtpDatabase.h"
|
||||
#include "MtpDataPacket.h"
|
||||
#include "MtpUtils.h"
|
||||
#include "mtp.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static jmethodID method_getObjectHandle;
|
||||
static jmethodID method_addFile;
|
||||
static jmethodID method_getObjectList;
|
||||
static jmethodID method_getObjectProperty;
|
||||
static jmethodID method_getObjectInfo;
|
||||
static jmethodID method_getObjectFilePath;
|
||||
static jmethodID method_deleteFile;
|
||||
static jfieldID field_context;
|
||||
|
||||
MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) {
|
||||
return (MtpDatabase *)env->GetIntField(database, field_context);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
class MyMtpDatabase : public MtpDatabase {
|
||||
private:
|
||||
jobject mDatabase;
|
||||
jintArray mIntBuffer;
|
||||
jlongArray mLongBuffer;
|
||||
jcharArray mStringBuffer;
|
||||
|
||||
public:
|
||||
MyMtpDatabase(JNIEnv *env, jobject client);
|
||||
virtual ~MyMtpDatabase();
|
||||
void cleanup(JNIEnv *env);
|
||||
|
||||
virtual MtpObjectHandle getObjectHandle(const char* path);
|
||||
|
||||
virtual MtpObjectHandle addFile(const char* path,
|
||||
MtpObjectFormat format,
|
||||
MtpObjectHandle parent,
|
||||
MtpStorageID storage,
|
||||
uint64_t size,
|
||||
time_t modified);
|
||||
|
||||
virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
|
||||
MtpObjectFormat format,
|
||||
MtpObjectHandle parent);
|
||||
|
||||
virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle,
|
||||
MtpObjectProperty property,
|
||||
MtpDataPacket& packet);
|
||||
|
||||
virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
|
||||
MtpDataPacket& packet);
|
||||
|
||||
virtual bool getObjectFilePath(MtpObjectHandle handle,
|
||||
MtpString& filePath,
|
||||
int64_t& fileLength);
|
||||
virtual bool deleteFile(MtpObjectHandle handle);
|
||||
|
||||
// helper for media scanner
|
||||
virtual MtpObjectHandle* getFileList(int& outCount);
|
||||
|
||||
virtual void beginTransaction();
|
||||
virtual void commitTransaction();
|
||||
virtual void rollbackTransaction();
|
||||
|
||||
bool getPropertyInfo(MtpObjectProperty property, int& type);
|
||||
};
|
||||
|
||||
MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client)
|
||||
: mDatabase(env->NewGlobalRef(client)),
|
||||
mIntBuffer(NULL),
|
||||
mLongBuffer(NULL),
|
||||
mStringBuffer(NULL)
|
||||
{
|
||||
jintArray intArray;
|
||||
jlongArray longArray;
|
||||
jcharArray charArray;
|
||||
|
||||
// create buffers for out arguments
|
||||
// we don't need to be thread-safe so this is OK
|
||||
intArray = env->NewIntArray(3);
|
||||
if (!intArray)
|
||||
goto out_of_memory;
|
||||
mIntBuffer = (jintArray)env->NewGlobalRef(intArray);
|
||||
longArray = env->NewLongArray(2);
|
||||
if (!longArray)
|
||||
goto out_of_memory;
|
||||
mLongBuffer = (jlongArray)env->NewGlobalRef(longArray);
|
||||
charArray = env->NewCharArray(256);
|
||||
if (!charArray)
|
||||
goto out_of_memory;
|
||||
mStringBuffer = (jcharArray)env->NewGlobalRef(charArray);
|
||||
return;
|
||||
|
||||
out_of_memory:
|
||||
env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL);
|
||||
}
|
||||
|
||||
void MyMtpDatabase::cleanup(JNIEnv *env) {
|
||||
env->DeleteGlobalRef(mDatabase);
|
||||
env->DeleteGlobalRef(mIntBuffer);
|
||||
env->DeleteGlobalRef(mLongBuffer);
|
||||
env->DeleteGlobalRef(mStringBuffer);
|
||||
}
|
||||
|
||||
MyMtpDatabase::~MyMtpDatabase() {
|
||||
}
|
||||
|
||||
MtpObjectHandle MyMtpDatabase::getObjectHandle(const char* path) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
return env->CallIntMethod(mDatabase, method_getObjectHandle, env->NewStringUTF(path));
|
||||
}
|
||||
|
||||
MtpObjectHandle MyMtpDatabase::addFile(const char* path,
|
||||
MtpObjectFormat format,
|
||||
MtpObjectHandle parent,
|
||||
MtpStorageID storage,
|
||||
uint64_t size,
|
||||
time_t modified) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
return env->CallIntMethod(mDatabase, method_addFile, env->NewStringUTF(path),
|
||||
(jint)format, (jint)parent, (jint)storage, (jlong)size, (jlong)modified);
|
||||
}
|
||||
|
||||
MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
|
||||
MtpObjectFormat format,
|
||||
MtpObjectHandle parent) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList,
|
||||
(jint)storageID, (jint)format, (jint)parent);
|
||||
if (!array)
|
||||
return NULL;
|
||||
MtpObjectHandleList* list = new MtpObjectHandleList();
|
||||
jint* handles = env->GetIntArrayElements(array, 0);
|
||||
jsize length = env->GetArrayLength(array);
|
||||
LOGD("getObjectList length: %d", length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
LOGD("push: %d", handles[i]);
|
||||
list->push(handles[i]);
|
||||
}
|
||||
env->ReleaseIntArrayElements(array, handles, 0);
|
||||
return list;
|
||||
}
|
||||
|
||||
MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle,
|
||||
MtpObjectProperty property,
|
||||
MtpDataPacket& packet) {
|
||||
int type;
|
||||
|
||||
if (!getPropertyInfo(property, type))
|
||||
return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
|
||||
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
jint result = env->CallIntMethod(mDatabase, method_getObjectProperty,
|
||||
(jint)handle, (jint)property, mLongBuffer, mStringBuffer);
|
||||
if (result != MTP_RESPONSE_OK)
|
||||
return result;
|
||||
|
||||
jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
|
||||
jlong longValue = longValues[0];
|
||||
env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
|
||||
|
||||
switch (type) {
|
||||
case MTP_TYPE_INT8:
|
||||
packet.putInt8(longValue);
|
||||
break;
|
||||
case MTP_TYPE_UINT8:
|
||||
packet.putUInt8(longValue);
|
||||
break;
|
||||
case MTP_TYPE_INT16:
|
||||
packet.putInt16(longValue);
|
||||
break;
|
||||
case MTP_TYPE_UINT16:
|
||||
packet.putUInt16(longValue);
|
||||
break;
|
||||
case MTP_TYPE_INT32:
|
||||
packet.putInt32(longValue);
|
||||
break;
|
||||
case MTP_TYPE_UINT32:
|
||||
packet.putUInt32(longValue);
|
||||
break;
|
||||
case MTP_TYPE_INT64:
|
||||
packet.putInt64(longValue);
|
||||
break;
|
||||
case MTP_TYPE_UINT64:
|
||||
packet.putUInt64(longValue);
|
||||
break;
|
||||
case MTP_TYPE_STR:
|
||||
{
|
||||
jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
|
||||
packet.putString(str);
|
||||
env->ReleaseCharArrayElements(mStringBuffer, str, 0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGE("unsupported object type\n");
|
||||
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
|
||||
}
|
||||
return MTP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
|
||||
MtpDataPacket& packet) {
|
||||
char date[20];
|
||||
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
|
||||
(jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
|
||||
if (!result)
|
||||
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
|
||||
|
||||
jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
|
||||
MtpStorageID storageID = intValues[0];
|
||||
MtpObjectFormat format = intValues[1];
|
||||
MtpObjectHandle parent = intValues[2];
|
||||
env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
|
||||
|
||||
jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
|
||||
uint64_t size = longValues[0];
|
||||
uint64_t modified = longValues[1];
|
||||
env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
|
||||
|
||||
int associationType = (format == MTP_FORMAT_ASSOCIATION ?
|
||||
MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
|
||||
MTP_ASSOCIATION_TYPE_UNDEFINED);
|
||||
|
||||
packet.putUInt32(storageID);
|
||||
packet.putUInt16(format);
|
||||
packet.putUInt16(0); // protection status
|
||||
packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
|
||||
packet.putUInt16(0); // thumb format
|
||||
packet.putUInt32(0); // thumb compressed size
|
||||
packet.putUInt32(0); // thumb pix width
|
||||
packet.putUInt32(0); // thumb pix height
|
||||
packet.putUInt32(0); // image pix width
|
||||
packet.putUInt32(0); // image pix height
|
||||
packet.putUInt32(0); // image bit depth
|
||||
packet.putUInt32(parent);
|
||||
packet.putUInt16(associationType);
|
||||
packet.putUInt32(0); // association desc
|
||||
packet.putUInt32(0); // sequence number
|
||||
|
||||
jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
|
||||
packet.putString(str); // file name
|
||||
env->ReleaseCharArrayElements(mStringBuffer, str, 0);
|
||||
|
||||
packet.putEmptyString();
|
||||
formatDateTime(modified, date, sizeof(date));
|
||||
packet.putString(date); // date modified
|
||||
packet.putEmptyString(); // keywords
|
||||
|
||||
return MTP_RESPONSE_OK;
|
||||
}
|
||||
|
||||
bool MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
|
||||
MtpString& filePath,
|
||||
int64_t& fileLength) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectFilePath,
|
||||
(jint)handle, mStringBuffer, mLongBuffer);
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
|
||||
filePath.setTo(str, strlen16(str));
|
||||
env->ReleaseCharArrayElements(mStringBuffer, str, 0);
|
||||
|
||||
jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
|
||||
fileLength = longValues[0];
|
||||
env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
|
||||
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
||||
return env->CallBooleanMethod(mDatabase, method_deleteFile, (jint)handle);
|
||||
}
|
||||
|
||||
// helper for media scanner
|
||||
MtpObjectHandle* MyMtpDatabase::getFileList(int& outCount) {
|
||||
// REMOVE ME
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void MyMtpDatabase::beginTransaction() {
|
||||
// REMOVE ME
|
||||
}
|
||||
|
||||
void MyMtpDatabase::commitTransaction() {
|
||||
// REMOVE ME
|
||||
}
|
||||
|
||||
void MyMtpDatabase::rollbackTransaction() {
|
||||
// REMOVE ME
|
||||
}
|
||||
|
||||
struct PropertyTableEntry {
|
||||
MtpObjectProperty property;
|
||||
int type;
|
||||
};
|
||||
|
||||
static const PropertyTableEntry kPropertyTable[] = {
|
||||
{ MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
|
||||
{ MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
|
||||
{ MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32 },
|
||||
{ MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
|
||||
{ MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
|
||||
{ MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
|
||||
};
|
||||
|
||||
bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) {
|
||||
int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
|
||||
const PropertyTableEntry* entry = kPropertyTable;
|
||||
for (int i = 0; i < count; i++, entry++) {
|
||||
if (entry->property == property) {
|
||||
type = entry->type;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
|
||||
if (env->ExceptionCheck()) {
|
||||
LOGE("An exception was thrown by callback '%s'.", methodName);
|
||||
LOGE_EX(env);
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void
|
||||
android_media_MtpDatabase_setup(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
LOGD("setup\n");
|
||||
MyMtpDatabase* database = new MyMtpDatabase(env, thiz);
|
||||
env->SetIntField(thiz, field_context, (int)database);
|
||||
checkAndClearExceptionFromCallback(env, __FUNCTION__);
|
||||
}
|
||||
|
||||
static void
|
||||
android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
LOGD("finalize\n");
|
||||
MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context);
|
||||
database->cleanup(env);
|
||||
delete database;
|
||||
env->SetIntField(thiz, field_context, 0);
|
||||
checkAndClearExceptionFromCallback(env, __FUNCTION__);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
{"native_setup", "()V", (void *)android_media_MtpDatabase_setup},
|
||||
{"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize},
|
||||
};
|
||||
|
||||
static const char* const kClassPathName = "android/media/MtpDatabase";
|
||||
|
||||
int register_android_media_MtpDatabase(JNIEnv *env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
LOGD("register_android_media_MtpDatabase\n");
|
||||
|
||||
clazz = env->FindClass("android/media/MtpDatabase");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find android/media/MtpDatabase");
|
||||
return -1;
|
||||
}
|
||||
method_getObjectHandle = env->GetMethodID(clazz, "getObjectHandle", "(Ljava/lang/String;)I");
|
||||
if (method_getObjectHandle == NULL) {
|
||||
LOGE("Can't find getObjectHandle");
|
||||
return -1;
|
||||
}
|
||||
method_addFile = env->GetMethodID(clazz, "addFile", "(Ljava/lang/String;IIIJJ)I");
|
||||
if (method_addFile == NULL) {
|
||||
LOGE("Can't find addFile");
|
||||
return -1;
|
||||
}
|
||||
method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
|
||||
if (method_getObjectList == NULL) {
|
||||
LOGE("Can't find getObjectList");
|
||||
return -1;
|
||||
}
|
||||
method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I");
|
||||
if (method_getObjectProperty == NULL) {
|
||||
LOGE("Can't find getObjectProperty");
|
||||
return -1;
|
||||
}
|
||||
method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
|
||||
if (method_getObjectInfo == NULL) {
|
||||
LOGE("Can't find getObjectInfo");
|
||||
return -1;
|
||||
}
|
||||
method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)Z");
|
||||
if (method_getObjectFilePath == NULL) {
|
||||
LOGE("Can't find getObjectFilePath");
|
||||
return -1;
|
||||
}
|
||||
method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)Z");
|
||||
if (method_deleteFile == NULL) {
|
||||
LOGE("Can't find deleteFile");
|
||||
return -1;
|
||||
}
|
||||
field_context = env->GetFieldID(clazz, "mNativeContext", "I");
|
||||
if (field_context == NULL) {
|
||||
LOGE("Can't find MtpDatabase.mNativeContext");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return AndroidRuntime::registerNativeMethods(env,
|
||||
"android/media/MtpDatabase", gMethods, NELEM(gMethods));
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "private/android_filesystem_config.h"
|
||||
|
||||
#include "MtpServer.h"
|
||||
#include "MtpSqliteDatabase.h" // REMOVE
|
||||
|
||||
using namespace android;
|
||||
|
||||
@@ -37,6 +38,8 @@ using namespace android;
|
||||
|
||||
static jfieldID field_context;
|
||||
|
||||
// in android_media_MtpDatabase.cpp
|
||||
extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -47,14 +50,13 @@ static bool ExceptionCheck(void* env)
|
||||
|
||||
class MtpThread : public Thread {
|
||||
private:
|
||||
MtpDatabase* mDatabase;
|
||||
String8 mStoragePath;
|
||||
String8 mDatabasePath;
|
||||
bool mDone;
|
||||
bool mScannedOnce;
|
||||
|
||||
public:
|
||||
MtpThread(const char* storagePath, const char* databasePath)
|
||||
: mStoragePath(storagePath), mDatabasePath(databasePath), mDone(false), mScannedOnce(false)
|
||||
MtpThread(MtpDatabase* database, const char* storagePath)
|
||||
: mDatabase(database), mStoragePath(storagePath), mDone(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -66,12 +68,10 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
MtpServer* server = new MtpServer(fd, mDatabasePath, AID_SDCARD_RW, 0664, 0775);
|
||||
MtpServer* server = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775);
|
||||
server->addStorage(mStoragePath);
|
||||
|
||||
// temporary
|
||||
LOGD("MtpThread server->scanStorage");
|
||||
server->scanStorage();
|
||||
LOGD("MtpThread server->run");
|
||||
server->run();
|
||||
close(fd);
|
||||
@@ -88,18 +88,17 @@ public:
|
||||
};
|
||||
|
||||
static void
|
||||
android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jstring storagePath, jstring databasePath)
|
||||
android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jstring storagePath)
|
||||
{
|
||||
LOGD("setup\n");
|
||||
|
||||
MtpDatabase* database = getMtpDatabase(env, javaDatabase);
|
||||
const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
|
||||
const char *databasePathStr = env->GetStringUTFChars(databasePath, NULL);
|
||||
|
||||
MtpThread* thread = new MtpThread(storagePathStr, databasePathStr);
|
||||
MtpThread* thread = new MtpThread(database, storagePathStr);
|
||||
env->SetIntField(thiz, field_context, (int)thread);
|
||||
|
||||
env->ReleaseStringUTFChars(storagePath, storagePathStr);
|
||||
env->ReleaseStringUTFChars(databasePath, databasePathStr);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -131,7 +130,8 @@ android_media_MtpServer_stop(JNIEnv *env, jobject thiz)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
{"native_setup", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)android_media_MtpServer_setup},
|
||||
{"native_setup", "(Landroid/media/MtpDatabase;Ljava/lang/String;)V",
|
||||
(void *)android_media_MtpServer_setup},
|
||||
{"native_finalize", "()V", (void *)android_media_MtpServer_finalize},
|
||||
{"native_start", "()V", (void *)android_media_MtpServer_start},
|
||||
{"native_stop", "()V", (void *)android_media_MtpServer_stop},
|
||||
|
||||
@@ -299,17 +299,28 @@ void MtpDataPacket::putAUInt64(const uint64_t* values, int count) {
|
||||
putUInt64(*values++);
|
||||
}
|
||||
|
||||
void MtpDataPacket::putString(const MtpStringBuffer& string)
|
||||
{
|
||||
void MtpDataPacket::putString(const MtpStringBuffer& string) {
|
||||
string.writeToPacket(this);
|
||||
}
|
||||
|
||||
void MtpDataPacket::putString(const char* s)
|
||||
{
|
||||
void MtpDataPacket::putString(const char* s) {
|
||||
MtpStringBuffer string(s);
|
||||
string.writeToPacket(this);
|
||||
}
|
||||
|
||||
void MtpDataPacket::putString(const uint16_t* string) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (string[i])
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
putUInt8(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
putUInt16(string[i]);
|
||||
}
|
||||
|
||||
#ifdef MTP_DEVICE
|
||||
int MtpDataPacket::read(int fd) {
|
||||
// first read the header
|
||||
|
||||
@@ -79,6 +79,7 @@ public:
|
||||
void putAUInt64(const uint64_t* values, int count);
|
||||
void putString(const MtpStringBuffer& string);
|
||||
void putString(const char* string);
|
||||
void putString(const uint16_t* string);
|
||||
inline void putEmptyString() { putUInt16(0); }
|
||||
inline void putEmptyArray() { putUInt32(0); }
|
||||
|
||||
|
||||
@@ -113,11 +113,10 @@ static const MtpObjectFormat kSupportedPlaybackFormats[] = {
|
||||
// MTP_FORMAT_PLS_PLAYLIST,
|
||||
};
|
||||
|
||||
MtpServer::MtpServer(int fd, const char* databasePath,
|
||||
MtpServer::MtpServer(int fd, MtpDatabase* database,
|
||||
int fileGroup, int filePerm, int directoryPerm)
|
||||
: mFD(fd),
|
||||
mDatabasePath(databasePath),
|
||||
mDatabase(NULL),
|
||||
mDatabase(database),
|
||||
mFileGroup(fileGroup),
|
||||
mFilePermission(filePerm),
|
||||
mDirectoryPermission(directoryPerm),
|
||||
@@ -126,9 +125,6 @@ MtpServer::MtpServer(int fd, const char* databasePath,
|
||||
mSendObjectHandle(kInvalidObjectHandle),
|
||||
mSendObjectFileSize(0)
|
||||
{
|
||||
mDatabase = new MtpSqliteDatabase();
|
||||
mDatabase->open(databasePath, true);
|
||||
|
||||
initObjectProperties();
|
||||
}
|
||||
|
||||
@@ -427,6 +423,8 @@ MtpResponseCode MtpServer::doGetObjectHandles() {
|
||||
MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
|
||||
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
|
||||
// 0x00000000 for all objects?
|
||||
if (parent == 0xFFFFFFFF)
|
||||
parent = 0;
|
||||
|
||||
MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
|
||||
mData.putAUInt32(handles);
|
||||
@@ -488,9 +486,10 @@ MtpResponseCode MtpServer::doSendObjectInfo() {
|
||||
return MTP_RESPONSE_INVALID_STORAGE_ID;
|
||||
|
||||
// special case the root
|
||||
if (parent == MTP_PARENT_ROOT)
|
||||
if (parent == MTP_PARENT_ROOT) {
|
||||
path = storage->getPath();
|
||||
else {
|
||||
parent = 0;
|
||||
} else {
|
||||
int64_t dummy;
|
||||
if (!mDatabase->getObjectFilePath(parent, path, dummy))
|
||||
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
|
||||
@@ -549,7 +548,7 @@ MtpResponseCode MtpServer::doSendObjectInfo() {
|
||||
}
|
||||
|
||||
mResponse.setParameter(1, storageID);
|
||||
mResponse.setParameter(2, parent);
|
||||
mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
|
||||
mResponse.setParameter(3, handle);
|
||||
|
||||
return MTP_RESPONSE_OK;
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
class MtpStorage;
|
||||
class MtpSqliteDatabase;
|
||||
class MtpDatabase;
|
||||
class MtpProperty;
|
||||
class MtpStorage;
|
||||
|
||||
class MtpServer {
|
||||
|
||||
@@ -36,10 +36,7 @@ private:
|
||||
// file descriptor for MTP kernel driver
|
||||
int mFD;
|
||||
|
||||
// path to our sqlite3 database
|
||||
const char* mDatabasePath;
|
||||
|
||||
MtpSqliteDatabase* mDatabase;
|
||||
MtpDatabase* mDatabase;
|
||||
|
||||
// group to own new files and folders
|
||||
int mFileGroup;
|
||||
@@ -67,7 +64,7 @@ private:
|
||||
size_t mSendObjectFileSize;
|
||||
|
||||
public:
|
||||
MtpServer(int fd, const char* databasePath,
|
||||
MtpServer(int fd, MtpDatabase* database,
|
||||
int fileGroup, int filePerm, int directoryPerm);
|
||||
virtual ~MtpServer();
|
||||
|
||||
|
||||
@@ -414,7 +414,6 @@ MtpObjectHandle* MtpSqliteDatabase::getFileList(int& outCount) {
|
||||
SqliteStatement stmt(mDatabase);
|
||||
stmt.prepare("SELECT count(*) FROM files;");
|
||||
|
||||
MtpObjectHandleList* list = new MtpObjectHandleList();
|
||||
if (stmt.step())
|
||||
count = stmt.getColumnInt(0);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "MtpServer.h"
|
||||
#include "MtpSqliteDatabase.h"
|
||||
#include "MtpStorage.h"
|
||||
#include "f_mtp.h"
|
||||
#include "private/android_filesystem_config.h"
|
||||
@@ -77,7 +78,9 @@ int main(int argc, char* argv[]) {
|
||||
enable_usb_function("usb_mass_storage", false);
|
||||
enable_usb_function("mtp", true);
|
||||
|
||||
MtpServer server(fd, "/data/data/mtp/mtp.db", AID_SDCARD_RW, 0664, 0775);
|
||||
MtpSqliteDatabase* database = new MtpSqliteDatabase();
|
||||
database->open("/data/data/mtp/mtp.db", true);
|
||||
MtpServer server(fd, database, AID_SDCARD_RW, 0664, 0775);
|
||||
server.addStorage(storagePath);
|
||||
server.scanStorage();
|
||||
server.run();
|
||||
|
||||
Reference in New Issue
Block a user