Loading media/mtp/Android.mk +2 −10 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ LOCAL_SRC_FILES:= \ MtpRequestPacket.cpp \ MtpResponsePacket.cpp \ MtpServer.cpp \ MtpSqliteDatabase.cpp \ MtpStorageInfo.cpp \ MtpStringBuffer.cpp \ MtpStorage.cpp \ Loading Loading @@ -70,18 +71,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := scantest LOCAL_SRC_FILES:= \ scantest.cpp \ MtpMediaScanner.cpp \ MtpDatabase.cpp \ MtpDataPacket.cpp \ MtpPacket.cpp \ MtpStringBuffer.cpp \ MtpUtils.cpp \ SqliteDatabase.cpp \ SqliteStatement.cpp \ #LOCAL_STATIC_LIBRARIES := libusbhost #LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := libmtp LOCAL_C_INCLUDES := external/sqlite/dist LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright libmedia Loading media/mtp/MtpDatabase.cpp +2 −502 Original line number Diff line number Diff line Loading @@ -18,196 +18,14 @@ #include "MtpDebug.h" #include "MtpDatabase.h" #include "MtpDataPacket.h" #include "MtpUtils.h" #include "SqliteDatabase.h" #include "SqliteStatement.h" #include <stdio.h> #include <stdlib.h> #include <sqlite3.h> #include "MtpTypes.h" #include "mtp.h" namespace android { #define FILE_ID_COLUMN 1 #define FILE_PATH_COLUMN 2 #define FILE_FORMAT_COLUMN 3 #define FILE_PARENT_COLUMN 4 #define FILE_STORAGE_COLUMN 5 #define FILE_SIZE_COLUMN 6 #define FILE_MODIFIED_COLUMN 7 #define AUDIO_ID_COLUMN 1 #define AUDIO_TITLE_COLUMN 2 #define AUDIO_ARTIST_COLUMN 3 #define AUDIO_ALBUM_COLUMN 4 #define AUDIO_ALBUM_ARTIST_COLUMN 5 #define AUDIO_GENRE_COLUMN 6 #define AUDIO_COMPOSER_COLUMN 7 #define AUDIO_TRACK_NUMBER_COLUMN 8 #define AUDIO_YEAR_COLUMN 9 #define AUDIO_DURATION_COLUMN 10 #define AUDIO_USE_COUNT_COLUMN 11 #define AUDIO_SAMPLE_RATE_COLUMN 12 #define AUDIO_NUM_CHANNELS_COLUMN 13 #define AUDIO_AUDIO_WAVE_CODEC_COLUMN 14 #define AUDIO_AUDIO_BIT_RATE_COLUMN 15 #define FILE_TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \ "_id INTEGER PRIMARY KEY," \ "path TEXT," \ "format INTEGER," \ "parent INTEGER," \ "storage INTEGER," \ "size INTEGER," \ "date_modified INTEGER" \ ");" #define AUDIO_TABLE_CREATE "CREATE TABLE IF NOT EXISTS audio (" \ "id INTEGER PRIMARY KEY," \ "title TEXT," \ "artist TEXT," \ "album TEXT," \ "album_artist TEXT," \ "genre TEXT," \ "composer TEXT," \ "track_number INTEGER," \ "year INTEGER," \ "duration INTEGER," \ "use_count INTEGER," \ "sample_rate INTEGER," \ "num_channels INTEGER," \ "audio_wave_codec TEXT," \ "audio_bit_rate INTEGER" \ ");" #define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);" #define FILE_ID_QUERY "SELECT _id,format FROM files WHERE path = ?;" #define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?" #define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_modified FROM files WHERE _id = ?;" #define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?);" #define FILE_DELETE "DELETE FROM files WHERE _id = ?;" #define AUDIO_INSERT "INSERT INTO audio VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);" #define AUDIO_DELETE "DELETE FROM audio WHERE id = ?;" struct PropertyTableEntry { MtpObjectProperty property; int type; const char* columnName; }; static const PropertyTableEntry kPropertyTable[] = { { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" }, { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" }, { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" }, { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" }, { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" }, { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" }, }; static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) { 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; columnName = entry->columnName; return true; } } return false; } MtpDatabase::MtpDatabase() : mFileIdQuery(NULL), mFilePathQuery(NULL), mObjectInfoQuery(NULL), mFileInserter(NULL), mFileDeleter(NULL), mAudioInserter(NULL), mAudioDeleter(NULL) { } MtpDatabase::~MtpDatabase() { } bool MtpDatabase::open(const char* path, bool create) { if (!SqliteDatabase::open(path, create)) return false; // create tables and indices if necessary if (!exec(FILE_TABLE_CREATE)) { LOGE("could not create file table"); return false; } if (!exec(PATH_INDEX_CREATE)) { LOGE("could not path index on file table"); return false; } if (!exec(AUDIO_TABLE_CREATE)) { LOGE("could not create file table"); return false; } if (!mFileIdQuery) { mFileIdQuery = new SqliteStatement(this); if (!mFileIdQuery->prepare(FILE_ID_QUERY)) { LOGE("could not compile FILE_ID_QUERY"); exit(-1); } } if (!mFilePathQuery) { mFilePathQuery = new SqliteStatement(this); if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) { LOGE("could not compile FILE_PATH_QUERY"); exit(-1); } } if (!mObjectInfoQuery) { mObjectInfoQuery = new SqliteStatement(this); if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) { LOGE("could not compile GET_OBJECT_INFO_QUERY"); exit(-1); } } if (!mFileInserter) { mFileInserter = new SqliteStatement(this); if (!mFileInserter->prepare(FILE_INSERT)) { LOGE("could not compile FILE_INSERT\n"); exit(-1); } } if (!mFileDeleter) { mFileDeleter = new SqliteStatement(this); if (!mFileDeleter->prepare(FILE_DELETE)) { LOGE("could not compile FILE_DELETE\n"); exit(-1); } } if (!mAudioInserter) { mAudioInserter = new SqliteStatement(this); if (!mAudioInserter->prepare(AUDIO_INSERT)) { LOGE("could not compile AUDIO_INSERT\n"); exit(-1); } } if (!mAudioDeleter) { mAudioDeleter = new SqliteStatement(this); if (!mAudioDeleter->prepare(AUDIO_DELETE)) { LOGE("could not compile AUDIO_DELETE\n"); exit(-1); } } return true; } uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) { switch (format) { case MTP_FORMAT_AIFF: Loading Loading @@ -260,322 +78,4 @@ uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) { } } MtpObjectHandle MtpDatabase::getObjectHandle(const char* path) { mFileIdQuery->reset(); mFileIdQuery->bind(1, path); if (mFileIdQuery->step()) { int row = mFileIdQuery->getColumnInt(0); if (row > 0) { MtpObjectFormat format = mFileIdQuery->getColumnInt(1); row |= getTableForFile(format); return row; } } return 0; } MtpObjectHandle MtpDatabase::addFile(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified) { mFileInserter->bind(FILE_PATH_COLUMN, path); mFileInserter->bind(FILE_FORMAT_COLUMN, format); mFileInserter->bind(FILE_PARENT_COLUMN, parent); mFileInserter->bind(FILE_STORAGE_COLUMN, storage); mFileInserter->bind(FILE_SIZE_COLUMN, size); mFileInserter->bind(FILE_MODIFIED_COLUMN, modified); mFileInserter->step(); mFileInserter->reset(); int result = lastInsertedRow(); return (result <= 0 ? kInvalidObjectHandle : result); } MtpObjectHandle MtpDatabase::addAudioFile(MtpObjectHandle handle) { mAudioInserter->bind(AUDIO_ID_COLUMN, handle); mAudioInserter->step(); mAudioInserter->reset(); int result = lastInsertedRow(); handle |= kObjectHandleTableAudio; return (result > 0 ? handle : kInvalidObjectHandle); } MtpObjectHandle MtpDatabase::addAudioFile(MtpObjectHandle handle, const char* title, const char* artist, const char* album, const char* albumArtist, const char* genre, const char* composer, const char* mimeType, int track, int year, int duration) { mAudioInserter->bind(AUDIO_ID_COLUMN, handle); if (title) mAudioInserter->bind(AUDIO_TITLE_COLUMN, title); if (artist) mAudioInserter->bind(AUDIO_ARTIST_COLUMN, artist); if (album) mAudioInserter->bind(AUDIO_ALBUM_COLUMN, album); if (albumArtist) mAudioInserter->bind(AUDIO_ALBUM_ARTIST_COLUMN, albumArtist); if (genre) mAudioInserter->bind(AUDIO_GENRE_COLUMN, genre); if (composer) mAudioInserter->bind(AUDIO_COMPOSER_COLUMN, composer); if (track) mAudioInserter->bind(AUDIO_TRACK_NUMBER_COLUMN, track); if (year) mAudioInserter->bind(AUDIO_YEAR_COLUMN, year); if (duration) mAudioInserter->bind(AUDIO_DURATION_COLUMN, duration); mAudioInserter->step(); mAudioInserter->reset(); int result = lastInsertedRow(); if (result <= 0) return kInvalidObjectHandle; result |= kObjectHandleTableAudio; return result; } MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { bool whereStorage = (storageID != 0xFFFFFFFF); bool whereFormat = (format != 0); bool whereParent = (parent != 0); char intBuffer[20]; MtpString query("SELECT _id,format FROM files"); if (whereStorage || whereFormat || whereParent) query += " WHERE"; if (whereStorage) { snprintf(intBuffer, sizeof(intBuffer), "%d", storageID); query += " storage = "; query += intBuffer; } if (whereFormat) { snprintf(intBuffer, sizeof(intBuffer), "%d", format); if (whereStorage) query += " AND"; query += " format = "; query += intBuffer; } if (whereParent) { if (parent != MTP_PARENT_ROOT) parent &= kObjectHandleIndexMask; snprintf(intBuffer, sizeof(intBuffer), "%d", parent); if (whereStorage || whereFormat) query += " AND"; query += " parent = "; query += intBuffer; } query += ";"; SqliteStatement stmt(this); LOGV("%s", (const char *)query); stmt.prepare(query); MtpObjectHandleList* list = new MtpObjectHandleList(); while (!stmt.isDone()) { if (stmt.step()) { int index = stmt.getColumnInt(0); LOGV("stmt.getColumnInt returned %d", index); if (index > 0) { MtpObjectFormat format = stmt.getColumnInt(1); index |= getTableForFile(format); list->push(index); } } } LOGV("list size: %d", list->size()); return list; } MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet) { int type; const char* columnName; char intBuffer[20]; if (handle != MTP_PARENT_ROOT) handle &= kObjectHandleIndexMask; if (!getPropertyInfo(property, type, columnName)) return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; snprintf(intBuffer, sizeof(intBuffer), "%d", handle); MtpString query("SELECT "); query += columnName; query += " FROM files WHERE _id = "; query += intBuffer; query += ";"; SqliteStatement stmt(this); LOGV("%s", (const char *)query); stmt.prepare(query); if (!stmt.step()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; switch (type) { case MTP_TYPE_INT8: packet.putInt8(stmt.getColumnInt(0)); break; case MTP_TYPE_UINT8: packet.putUInt8(stmt.getColumnInt(0)); break; case MTP_TYPE_INT16: packet.putInt16(stmt.getColumnInt(0)); break; case MTP_TYPE_UINT16: packet.putUInt16(stmt.getColumnInt(0)); break; case MTP_TYPE_INT32: packet.putInt32(stmt.getColumnInt(0)); break; case MTP_TYPE_UINT32: packet.putUInt32(stmt.getColumnInt(0)); break; case MTP_TYPE_INT64: packet.putInt64(stmt.getColumnInt64(0)); break; case MTP_TYPE_UINT64: packet.putUInt64(stmt.getColumnInt64(0)); break; case MTP_TYPE_STR: packet.putString(stmt.getColumnString(0)); break; default: LOGE("unsupported object type\n"); return MTP_RESPONSE_INVALID_OBJECT_HANDLE; } return MTP_RESPONSE_OK; } MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) { char date[20]; if (handle != MTP_PARENT_ROOT) handle &= kObjectHandleIndexMask; mObjectInfoQuery->reset(); mObjectInfoQuery->bind(1, handle); if (!mObjectInfoQuery->step()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0); MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1); MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2); // extract name from path. do we want a separate database entry for this? const char* name = mObjectInfoQuery->getColumnString(3); const char* lastSlash = strrchr(name, '/'); if (lastSlash) name = lastSlash + 1; int64_t size = mObjectInfoQuery->getColumnInt64(4); time_t modified = mObjectInfoQuery->getColumnInt(5); int associationType = (format == MTP_FORMAT_ASSOCIATION ? MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : MTP_ASSOCIATION_TYPE_UNDEFINED); LOGV("storageID: %d, format: %d, parent: %d", storageID, format, parent); 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 packet.putString(name); // file name packet.putEmptyString(); formatDateTime(modified, date, sizeof(date)); packet.putString(date); // date modified packet.putEmptyString(); // keywords return MTP_RESPONSE_OK; } bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle, MtpString& filePath, int64_t& fileLength) { if (handle != MTP_PARENT_ROOT) handle &= kObjectHandleIndexMask; mFilePathQuery->reset(); mFilePathQuery->bind(1, handle); if (!mFilePathQuery->step()) return false; const char* path = mFilePathQuery->getColumnString(0); if (!path) return false; filePath = path; fileLength = mFilePathQuery->getColumnInt64(1); return true; } bool MtpDatabase::deleteFile(MtpObjectHandle handle) { uint32_t table = handle & kObjectHandleTableMask; handle &= kObjectHandleIndexMask; mFileDeleter->bind(1, handle); mFileDeleter->step(); mFileDeleter->reset(); if (table == kObjectHandleTableAudio) { mAudioDeleter->bind(1, handle); mAudioDeleter->step(); mAudioDeleter->reset(); } return true; } MtpObjectHandle* MtpDatabase::getFileList(int& outCount) { MtpObjectHandle* result = NULL; int count = 0; SqliteStatement stmt(this); stmt.prepare("SELECT count(*) FROM files;"); MtpObjectHandleList* list = new MtpObjectHandleList(); if (stmt.step()) count = stmt.getColumnInt(0); if (count > 0) { result = new MtpObjectHandle[count]; memset(result, 0, count * sizeof(*result)); SqliteStatement stmt2(this); stmt2.prepare("SELECT _id,format FROM files;"); for (int i = 0; i < count; i++) { if (!stmt2.step()) { LOGW("getFileList ended early"); count = i; break; } MtpObjectHandle handle = stmt2.getColumnInt(0); MtpObjectFormat format = stmt2.getColumnInt(1); handle |= getTableForFile(format); result[i] = handle; } } outCount = count; return result; } /* for getObjectPropDesc packet.putUInt16(property); packet.putUInt16(dataType); packet.putUInt8(getSet); // default value DTS packet.putUInt32(groupCode); packet.putUInt8(formFlag); // form, variable */ } // namespace android media/mtp/MtpDatabase.h +43 −51 Original line number Diff line number Diff line Loading @@ -23,36 +23,24 @@ namespace android { class MtpDataPacket; class SqliteStatement; class MtpDatabase : public SqliteDatabase { private: SqliteStatement* mFileIdQuery; SqliteStatement* mFilePathQuery; SqliteStatement* mObjectInfoQuery; SqliteStatement* mFileInserter; SqliteStatement* mFileDeleter; SqliteStatement* mAudioInserter; SqliteStatement* mAudioDeleter; class MtpDatabase { public: MtpDatabase(); virtual ~MtpDatabase(); static uint32_t getTableForFile(MtpObjectFormat format); bool open(const char* path, bool create); MtpObjectHandle getObjectHandle(const char* path); MtpObjectHandle addFile(const char* path, virtual MtpObjectHandle getObjectHandle(const char* path) = 0; virtual MtpObjectHandle addFile(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified); time_t modified) = 0; MtpObjectHandle addAudioFile(MtpObjectHandle id); virtual MtpObjectHandle addAudioFile(MtpObjectHandle id) = 0; MtpObjectHandle addAudioFile(MtpObjectHandle id, virtual MtpObjectHandle addAudioFile(MtpObjectHandle id, const char* title, const char* artist, const char* album, Loading @@ -62,26 +50,30 @@ public: const char* mimeType, int track, int year, int duration); int duration) = 0; MtpObjectHandleList* getObjectList(MtpStorageID storageID, virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); MtpObjectHandle parent) = 0; MtpResponseCode getObjectProperty(MtpObjectHandle handle, virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet); MtpDataPacket& packet) = 0; MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet); virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) = 0; bool getObjectFilePath(MtpObjectHandle handle, virtual bool getObjectFilePath(MtpObjectHandle handle, MtpString& filePath, int64_t& fileLength); bool deleteFile(MtpObjectHandle handle); int64_t& fileLength) = 0; virtual bool deleteFile(MtpObjectHandle handle) = 0; // helper for media scanner MtpObjectHandle* getFileList(int& outCount); virtual MtpObjectHandle* getFileList(int& outCount) = 0; virtual void beginTransaction() = 0; virtual void commitTransaction() = 0; virtual void rollbackTransaction() = 0; }; }; // namespace android Loading media/mtp/MtpServer.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -25,9 +25,9 @@ #include <cutils/properties.h> #include "MtpDebug.h" #include "MtpDatabase.h" #include "MtpProperty.h" #include "MtpServer.h" #include "MtpSqliteDatabase.h" #include "MtpStorage.h" #include "MtpStringBuffer.h" Loading Loading @@ -122,7 +122,7 @@ MtpServer::MtpServer(int fd, const char* databasePath) mSendObjectHandle(kInvalidObjectHandle), mSendObjectFileSize(0) { mDatabase = new MtpDatabase(); mDatabase = new MtpSqliteDatabase(); mDatabase->open(databasePath, true); initObjectProperties(); Loading media/mtp/MtpServer.h +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ namespace android { class MtpStorage; class MtpDatabase; class MtpSqliteDatabase; class MtpProperty; class MtpServer { Loading @@ -39,7 +39,7 @@ private: // path to our sqlite3 database const char* mDatabasePath; MtpDatabase* mDatabase; MtpSqliteDatabase* mDatabase; // current session ID MtpSessionID mSessionID; Loading Loading
media/mtp/Android.mk +2 −10 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ LOCAL_SRC_FILES:= \ MtpRequestPacket.cpp \ MtpResponsePacket.cpp \ MtpServer.cpp \ MtpSqliteDatabase.cpp \ MtpStorageInfo.cpp \ MtpStringBuffer.cpp \ MtpStorage.cpp \ Loading Loading @@ -70,18 +71,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := scantest LOCAL_SRC_FILES:= \ scantest.cpp \ MtpMediaScanner.cpp \ MtpDatabase.cpp \ MtpDataPacket.cpp \ MtpPacket.cpp \ MtpStringBuffer.cpp \ MtpUtils.cpp \ SqliteDatabase.cpp \ SqliteStatement.cpp \ #LOCAL_STATIC_LIBRARIES := libusbhost #LOCAL_LDLIBS := -lpthread LOCAL_STATIC_LIBRARIES := libmtp LOCAL_C_INCLUDES := external/sqlite/dist LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright libmedia Loading
media/mtp/MtpDatabase.cpp +2 −502 Original line number Diff line number Diff line Loading @@ -18,196 +18,14 @@ #include "MtpDebug.h" #include "MtpDatabase.h" #include "MtpDataPacket.h" #include "MtpUtils.h" #include "SqliteDatabase.h" #include "SqliteStatement.h" #include <stdio.h> #include <stdlib.h> #include <sqlite3.h> #include "MtpTypes.h" #include "mtp.h" namespace android { #define FILE_ID_COLUMN 1 #define FILE_PATH_COLUMN 2 #define FILE_FORMAT_COLUMN 3 #define FILE_PARENT_COLUMN 4 #define FILE_STORAGE_COLUMN 5 #define FILE_SIZE_COLUMN 6 #define FILE_MODIFIED_COLUMN 7 #define AUDIO_ID_COLUMN 1 #define AUDIO_TITLE_COLUMN 2 #define AUDIO_ARTIST_COLUMN 3 #define AUDIO_ALBUM_COLUMN 4 #define AUDIO_ALBUM_ARTIST_COLUMN 5 #define AUDIO_GENRE_COLUMN 6 #define AUDIO_COMPOSER_COLUMN 7 #define AUDIO_TRACK_NUMBER_COLUMN 8 #define AUDIO_YEAR_COLUMN 9 #define AUDIO_DURATION_COLUMN 10 #define AUDIO_USE_COUNT_COLUMN 11 #define AUDIO_SAMPLE_RATE_COLUMN 12 #define AUDIO_NUM_CHANNELS_COLUMN 13 #define AUDIO_AUDIO_WAVE_CODEC_COLUMN 14 #define AUDIO_AUDIO_BIT_RATE_COLUMN 15 #define FILE_TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \ "_id INTEGER PRIMARY KEY," \ "path TEXT," \ "format INTEGER," \ "parent INTEGER," \ "storage INTEGER," \ "size INTEGER," \ "date_modified INTEGER" \ ");" #define AUDIO_TABLE_CREATE "CREATE TABLE IF NOT EXISTS audio (" \ "id INTEGER PRIMARY KEY," \ "title TEXT," \ "artist TEXT," \ "album TEXT," \ "album_artist TEXT," \ "genre TEXT," \ "composer TEXT," \ "track_number INTEGER," \ "year INTEGER," \ "duration INTEGER," \ "use_count INTEGER," \ "sample_rate INTEGER," \ "num_channels INTEGER," \ "audio_wave_codec TEXT," \ "audio_bit_rate INTEGER" \ ");" #define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);" #define FILE_ID_QUERY "SELECT _id,format FROM files WHERE path = ?;" #define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?" #define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_modified FROM files WHERE _id = ?;" #define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?);" #define FILE_DELETE "DELETE FROM files WHERE _id = ?;" #define AUDIO_INSERT "INSERT INTO audio VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);" #define AUDIO_DELETE "DELETE FROM audio WHERE id = ?;" struct PropertyTableEntry { MtpObjectProperty property; int type; const char* columnName; }; static const PropertyTableEntry kPropertyTable[] = { { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" }, { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" }, { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" }, { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" }, { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" }, { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" }, }; static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) { 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; columnName = entry->columnName; return true; } } return false; } MtpDatabase::MtpDatabase() : mFileIdQuery(NULL), mFilePathQuery(NULL), mObjectInfoQuery(NULL), mFileInserter(NULL), mFileDeleter(NULL), mAudioInserter(NULL), mAudioDeleter(NULL) { } MtpDatabase::~MtpDatabase() { } bool MtpDatabase::open(const char* path, bool create) { if (!SqliteDatabase::open(path, create)) return false; // create tables and indices if necessary if (!exec(FILE_TABLE_CREATE)) { LOGE("could not create file table"); return false; } if (!exec(PATH_INDEX_CREATE)) { LOGE("could not path index on file table"); return false; } if (!exec(AUDIO_TABLE_CREATE)) { LOGE("could not create file table"); return false; } if (!mFileIdQuery) { mFileIdQuery = new SqliteStatement(this); if (!mFileIdQuery->prepare(FILE_ID_QUERY)) { LOGE("could not compile FILE_ID_QUERY"); exit(-1); } } if (!mFilePathQuery) { mFilePathQuery = new SqliteStatement(this); if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) { LOGE("could not compile FILE_PATH_QUERY"); exit(-1); } } if (!mObjectInfoQuery) { mObjectInfoQuery = new SqliteStatement(this); if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) { LOGE("could not compile GET_OBJECT_INFO_QUERY"); exit(-1); } } if (!mFileInserter) { mFileInserter = new SqliteStatement(this); if (!mFileInserter->prepare(FILE_INSERT)) { LOGE("could not compile FILE_INSERT\n"); exit(-1); } } if (!mFileDeleter) { mFileDeleter = new SqliteStatement(this); if (!mFileDeleter->prepare(FILE_DELETE)) { LOGE("could not compile FILE_DELETE\n"); exit(-1); } } if (!mAudioInserter) { mAudioInserter = new SqliteStatement(this); if (!mAudioInserter->prepare(AUDIO_INSERT)) { LOGE("could not compile AUDIO_INSERT\n"); exit(-1); } } if (!mAudioDeleter) { mAudioDeleter = new SqliteStatement(this); if (!mAudioDeleter->prepare(AUDIO_DELETE)) { LOGE("could not compile AUDIO_DELETE\n"); exit(-1); } } return true; } uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) { switch (format) { case MTP_FORMAT_AIFF: Loading Loading @@ -260,322 +78,4 @@ uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) { } } MtpObjectHandle MtpDatabase::getObjectHandle(const char* path) { mFileIdQuery->reset(); mFileIdQuery->bind(1, path); if (mFileIdQuery->step()) { int row = mFileIdQuery->getColumnInt(0); if (row > 0) { MtpObjectFormat format = mFileIdQuery->getColumnInt(1); row |= getTableForFile(format); return row; } } return 0; } MtpObjectHandle MtpDatabase::addFile(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified) { mFileInserter->bind(FILE_PATH_COLUMN, path); mFileInserter->bind(FILE_FORMAT_COLUMN, format); mFileInserter->bind(FILE_PARENT_COLUMN, parent); mFileInserter->bind(FILE_STORAGE_COLUMN, storage); mFileInserter->bind(FILE_SIZE_COLUMN, size); mFileInserter->bind(FILE_MODIFIED_COLUMN, modified); mFileInserter->step(); mFileInserter->reset(); int result = lastInsertedRow(); return (result <= 0 ? kInvalidObjectHandle : result); } MtpObjectHandle MtpDatabase::addAudioFile(MtpObjectHandle handle) { mAudioInserter->bind(AUDIO_ID_COLUMN, handle); mAudioInserter->step(); mAudioInserter->reset(); int result = lastInsertedRow(); handle |= kObjectHandleTableAudio; return (result > 0 ? handle : kInvalidObjectHandle); } MtpObjectHandle MtpDatabase::addAudioFile(MtpObjectHandle handle, const char* title, const char* artist, const char* album, const char* albumArtist, const char* genre, const char* composer, const char* mimeType, int track, int year, int duration) { mAudioInserter->bind(AUDIO_ID_COLUMN, handle); if (title) mAudioInserter->bind(AUDIO_TITLE_COLUMN, title); if (artist) mAudioInserter->bind(AUDIO_ARTIST_COLUMN, artist); if (album) mAudioInserter->bind(AUDIO_ALBUM_COLUMN, album); if (albumArtist) mAudioInserter->bind(AUDIO_ALBUM_ARTIST_COLUMN, albumArtist); if (genre) mAudioInserter->bind(AUDIO_GENRE_COLUMN, genre); if (composer) mAudioInserter->bind(AUDIO_COMPOSER_COLUMN, composer); if (track) mAudioInserter->bind(AUDIO_TRACK_NUMBER_COLUMN, track); if (year) mAudioInserter->bind(AUDIO_YEAR_COLUMN, year); if (duration) mAudioInserter->bind(AUDIO_DURATION_COLUMN, duration); mAudioInserter->step(); mAudioInserter->reset(); int result = lastInsertedRow(); if (result <= 0) return kInvalidObjectHandle; result |= kObjectHandleTableAudio; return result; } MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent) { bool whereStorage = (storageID != 0xFFFFFFFF); bool whereFormat = (format != 0); bool whereParent = (parent != 0); char intBuffer[20]; MtpString query("SELECT _id,format FROM files"); if (whereStorage || whereFormat || whereParent) query += " WHERE"; if (whereStorage) { snprintf(intBuffer, sizeof(intBuffer), "%d", storageID); query += " storage = "; query += intBuffer; } if (whereFormat) { snprintf(intBuffer, sizeof(intBuffer), "%d", format); if (whereStorage) query += " AND"; query += " format = "; query += intBuffer; } if (whereParent) { if (parent != MTP_PARENT_ROOT) parent &= kObjectHandleIndexMask; snprintf(intBuffer, sizeof(intBuffer), "%d", parent); if (whereStorage || whereFormat) query += " AND"; query += " parent = "; query += intBuffer; } query += ";"; SqliteStatement stmt(this); LOGV("%s", (const char *)query); stmt.prepare(query); MtpObjectHandleList* list = new MtpObjectHandleList(); while (!stmt.isDone()) { if (stmt.step()) { int index = stmt.getColumnInt(0); LOGV("stmt.getColumnInt returned %d", index); if (index > 0) { MtpObjectFormat format = stmt.getColumnInt(1); index |= getTableForFile(format); list->push(index); } } } LOGV("list size: %d", list->size()); return list; } MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet) { int type; const char* columnName; char intBuffer[20]; if (handle != MTP_PARENT_ROOT) handle &= kObjectHandleIndexMask; if (!getPropertyInfo(property, type, columnName)) return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; snprintf(intBuffer, sizeof(intBuffer), "%d", handle); MtpString query("SELECT "); query += columnName; query += " FROM files WHERE _id = "; query += intBuffer; query += ";"; SqliteStatement stmt(this); LOGV("%s", (const char *)query); stmt.prepare(query); if (!stmt.step()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; switch (type) { case MTP_TYPE_INT8: packet.putInt8(stmt.getColumnInt(0)); break; case MTP_TYPE_UINT8: packet.putUInt8(stmt.getColumnInt(0)); break; case MTP_TYPE_INT16: packet.putInt16(stmt.getColumnInt(0)); break; case MTP_TYPE_UINT16: packet.putUInt16(stmt.getColumnInt(0)); break; case MTP_TYPE_INT32: packet.putInt32(stmt.getColumnInt(0)); break; case MTP_TYPE_UINT32: packet.putUInt32(stmt.getColumnInt(0)); break; case MTP_TYPE_INT64: packet.putInt64(stmt.getColumnInt64(0)); break; case MTP_TYPE_UINT64: packet.putUInt64(stmt.getColumnInt64(0)); break; case MTP_TYPE_STR: packet.putString(stmt.getColumnString(0)); break; default: LOGE("unsupported object type\n"); return MTP_RESPONSE_INVALID_OBJECT_HANDLE; } return MTP_RESPONSE_OK; } MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) { char date[20]; if (handle != MTP_PARENT_ROOT) handle &= kObjectHandleIndexMask; mObjectInfoQuery->reset(); mObjectInfoQuery->bind(1, handle); if (!mObjectInfoQuery->step()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0); MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1); MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2); // extract name from path. do we want a separate database entry for this? const char* name = mObjectInfoQuery->getColumnString(3); const char* lastSlash = strrchr(name, '/'); if (lastSlash) name = lastSlash + 1; int64_t size = mObjectInfoQuery->getColumnInt64(4); time_t modified = mObjectInfoQuery->getColumnInt(5); int associationType = (format == MTP_FORMAT_ASSOCIATION ? MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : MTP_ASSOCIATION_TYPE_UNDEFINED); LOGV("storageID: %d, format: %d, parent: %d", storageID, format, parent); 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 packet.putString(name); // file name packet.putEmptyString(); formatDateTime(modified, date, sizeof(date)); packet.putString(date); // date modified packet.putEmptyString(); // keywords return MTP_RESPONSE_OK; } bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle, MtpString& filePath, int64_t& fileLength) { if (handle != MTP_PARENT_ROOT) handle &= kObjectHandleIndexMask; mFilePathQuery->reset(); mFilePathQuery->bind(1, handle); if (!mFilePathQuery->step()) return false; const char* path = mFilePathQuery->getColumnString(0); if (!path) return false; filePath = path; fileLength = mFilePathQuery->getColumnInt64(1); return true; } bool MtpDatabase::deleteFile(MtpObjectHandle handle) { uint32_t table = handle & kObjectHandleTableMask; handle &= kObjectHandleIndexMask; mFileDeleter->bind(1, handle); mFileDeleter->step(); mFileDeleter->reset(); if (table == kObjectHandleTableAudio) { mAudioDeleter->bind(1, handle); mAudioDeleter->step(); mAudioDeleter->reset(); } return true; } MtpObjectHandle* MtpDatabase::getFileList(int& outCount) { MtpObjectHandle* result = NULL; int count = 0; SqliteStatement stmt(this); stmt.prepare("SELECT count(*) FROM files;"); MtpObjectHandleList* list = new MtpObjectHandleList(); if (stmt.step()) count = stmt.getColumnInt(0); if (count > 0) { result = new MtpObjectHandle[count]; memset(result, 0, count * sizeof(*result)); SqliteStatement stmt2(this); stmt2.prepare("SELECT _id,format FROM files;"); for (int i = 0; i < count; i++) { if (!stmt2.step()) { LOGW("getFileList ended early"); count = i; break; } MtpObjectHandle handle = stmt2.getColumnInt(0); MtpObjectFormat format = stmt2.getColumnInt(1); handle |= getTableForFile(format); result[i] = handle; } } outCount = count; return result; } /* for getObjectPropDesc packet.putUInt16(property); packet.putUInt16(dataType); packet.putUInt8(getSet); // default value DTS packet.putUInt32(groupCode); packet.putUInt8(formFlag); // form, variable */ } // namespace android
media/mtp/MtpDatabase.h +43 −51 Original line number Diff line number Diff line Loading @@ -23,36 +23,24 @@ namespace android { class MtpDataPacket; class SqliteStatement; class MtpDatabase : public SqliteDatabase { private: SqliteStatement* mFileIdQuery; SqliteStatement* mFilePathQuery; SqliteStatement* mObjectInfoQuery; SqliteStatement* mFileInserter; SqliteStatement* mFileDeleter; SqliteStatement* mAudioInserter; SqliteStatement* mAudioDeleter; class MtpDatabase { public: MtpDatabase(); virtual ~MtpDatabase(); static uint32_t getTableForFile(MtpObjectFormat format); bool open(const char* path, bool create); MtpObjectHandle getObjectHandle(const char* path); MtpObjectHandle addFile(const char* path, virtual MtpObjectHandle getObjectHandle(const char* path) = 0; virtual MtpObjectHandle addFile(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified); time_t modified) = 0; MtpObjectHandle addAudioFile(MtpObjectHandle id); virtual MtpObjectHandle addAudioFile(MtpObjectHandle id) = 0; MtpObjectHandle addAudioFile(MtpObjectHandle id, virtual MtpObjectHandle addAudioFile(MtpObjectHandle id, const char* title, const char* artist, const char* album, Loading @@ -62,26 +50,30 @@ public: const char* mimeType, int track, int year, int duration); int duration) = 0; MtpObjectHandleList* getObjectList(MtpStorageID storageID, virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); MtpObjectHandle parent) = 0; MtpResponseCode getObjectProperty(MtpObjectHandle handle, virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet); MtpDataPacket& packet) = 0; MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet); virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpDataPacket& packet) = 0; bool getObjectFilePath(MtpObjectHandle handle, virtual bool getObjectFilePath(MtpObjectHandle handle, MtpString& filePath, int64_t& fileLength); bool deleteFile(MtpObjectHandle handle); int64_t& fileLength) = 0; virtual bool deleteFile(MtpObjectHandle handle) = 0; // helper for media scanner MtpObjectHandle* getFileList(int& outCount); virtual MtpObjectHandle* getFileList(int& outCount) = 0; virtual void beginTransaction() = 0; virtual void commitTransaction() = 0; virtual void rollbackTransaction() = 0; }; }; // namespace android Loading
media/mtp/MtpServer.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -25,9 +25,9 @@ #include <cutils/properties.h> #include "MtpDebug.h" #include "MtpDatabase.h" #include "MtpProperty.h" #include "MtpServer.h" #include "MtpSqliteDatabase.h" #include "MtpStorage.h" #include "MtpStringBuffer.h" Loading Loading @@ -122,7 +122,7 @@ MtpServer::MtpServer(int fd, const char* databasePath) mSendObjectHandle(kInvalidObjectHandle), mSendObjectFileSize(0) { mDatabase = new MtpDatabase(); mDatabase = new MtpSqliteDatabase(); mDatabase->open(databasePath, true); initObjectProperties(); Loading
media/mtp/MtpServer.h +2 −2 Original line number Diff line number Diff line Loading @@ -27,7 +27,7 @@ namespace android { class MtpStorage; class MtpDatabase; class MtpSqliteDatabase; class MtpProperty; class MtpServer { Loading @@ -39,7 +39,7 @@ private: // path to our sqlite3 database const char* mDatabasePath; MtpDatabase* mDatabase; MtpSqliteDatabase* mDatabase; // current session ID MtpSessionID mSessionID; Loading