summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2010-05-14 15:35:17 -0400
committerMike Lockwood <lockwood@android.com>2010-05-18 16:12:38 -0400
commitfceef46513db3507b413f604cea89e3c7f352663 (patch)
tree3155d044d58c5af47cd59663ae7dbc242e5d3102
parent15727818edf0c9bd88303656ccb43fccd2515007 (diff)
downloadframeworks_av-fceef46513db3507b413f604cea89e3c7f352663.zip
frameworks_av-fceef46513db3507b413f604cea89e3c7f352663.tar.gz
frameworks_av-fceef46513db3507b413f604cea89e3c7f352663.tar.bz2
MTP: More prototyping work:
New media scanner test program Media scanner now cleans up after files that no longer exist Separate database table for audio files Extract metadata from audio files with libstagefright Change-Id: I2bd0fe877836c741658e72fcfeb89c11be0d9b41 Signed-off-by: Mike Lockwood <lockwood@android.com>
-rw-r--r--media/mtp/Android.mk30
-rw-r--r--media/mtp/MtpDatabase.cpp381
-rw-r--r--media/mtp/MtpDatabase.h23
-rw-r--r--media/mtp/MtpMediaScanner.cpp377
-rw-r--r--media/mtp/MtpMediaScanner.h56
-rw-r--r--media/mtp/MtpServer.cpp46
-rw-r--r--media/mtp/MtpStorage.cpp62
-rw-r--r--media/mtp/MtpStorage.h3
-rw-r--r--media/mtp/mtp.h12
-rw-r--r--media/mtp/scantest.cpp38
10 files changed, 858 insertions, 170 deletions
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 4d86064..d82ace3 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -24,6 +24,7 @@ LOCAL_SRC_FILES:= \
MtpDatabase.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
+ MtpMediaScanner.cpp \
MtpPacket.cpp \
MtpRequestPacket.cpp \
MtpResponsePacket.cpp \
@@ -40,7 +41,7 @@ LOCAL_C_INCLUDES := external/sqlite/dist
LOCAL_CFLAGS := -DMTP_DEVICE
-LOCAL_SHARED_LIBRARIES := libutils libsqlite
+LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright
include $(BUILD_EXECUTABLE)
@@ -70,4 +71,31 @@ LOCAL_LDFLAGS := -g
include $(BUILD_HOST_EXECUTABLE)
+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_C_INCLUDES := external/sqlite/dist
+LOCAL_SHARED_LIBRARIES := libutils libsqlite libstagefright
+
+
+LOCAL_CFLAGS := -g
+LOCAL_LDFLAGS := -g
+
+include $(BUILD_EXECUTABLE)
+
endif \ No newline at end of file
diff --git a/media/mtp/MtpDatabase.cpp b/media/mtp/MtpDatabase.cpp
index 8f6c75d..ab22ddd 100644
--- a/media/mtp/MtpDatabase.cpp
+++ b/media/mtp/MtpDatabase.cpp
@@ -20,38 +20,74 @@
#include "SqliteStatement.h"
#include <stdio.h>
+#include <stdlib.h>
#include <sqlite3.h>
namespace android {
-#define ID_COLUMN 1
-#define PATH_COLUMN 2
-#define FORMAT_COLUMN 3
-#define PARENT_COLUMN 4
-#define STORAGE_COLUMN 5
-#define SIZE_COLUMN 6
-#define CREATED_COLUMN 7
-#define MODIFIED_COLUMN 8
-
-#define TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \
+#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_created INTEGER," \
- "date_modified 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 FROM files WHERE 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_created,date_modified FROM files WHERE _id = ?;"
-#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?,?);"
-#define FILE_DELETE "DELETE FROM files WHERE path = ?;"
+#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;
@@ -65,7 +101,6 @@ static const PropertyTableEntry kPropertyTable[] = {
{ 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_CREATED, MTP_TYPE_STR, "date_created" },
{ MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" },
};
@@ -83,11 +118,14 @@ static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*&
}
+
MtpDatabase::MtpDatabase()
: mFileIdQuery(NULL),
mObjectInfoQuery(NULL),
mFileInserter(NULL),
- mFileDeleter(NULL)
+ mFileDeleter(NULL),
+ mAudioInserter(NULL),
+ mAudioDeleter(NULL)
{
}
@@ -98,66 +136,195 @@ bool MtpDatabase::open(const char* path, bool create) {
if (!SqliteDatabase::open(path, create))
return false;
- // create the table if necessary
- if (!exec(TABLE_CREATE)) {
- fprintf(stderr, "could not create table\n");
+ // create tables and indices if necessary
+ if (!exec(FILE_TABLE_CREATE)) {
+ fprintf(stderr, "could not create file table\n");
return false;
}
if (!exec(PATH_INDEX_CREATE)) {
- fprintf(stderr, "could not path index\n");
+ fprintf(stderr, "could not path index on file table\n");
+ return false;
+ }
+ if (!exec(AUDIO_TABLE_CREATE)) {
+ fprintf(stderr, "could not create file table\n");
return false;
}
- return true;
-}
-
-MtpObjectHandle MtpDatabase::addFile(const char* path,
- MtpObjectFormat format,
- MtpObjectHandle parent,
- MtpStorageID storage,
- uint64_t size,
- time_t created,
- time_t modified) {
- // first check to see if the file exists
- if (mFileIdQuery)
- mFileIdQuery->reset();
- else {
+ if (!mFileIdQuery) {
mFileIdQuery = new SqliteStatement(this);
if (!mFileIdQuery->prepare(FILE_ID_QUERY)) {
fprintf(stderr, "could not compile FILE_ID_QUERY\n");
- delete mFileIdQuery;
- mFileIdQuery = NULL;
- return kInvalidObjectHandle;
+ exit(-1);
}
}
-
- mFileIdQuery->bind(1, path);
- if (mFileIdQuery->step()) {
- int row = mFileIdQuery->getColumnInt(0);
- if (row > 0)
- return row;
+ if (!mFilePathQuery) {
+ mFilePathQuery = new SqliteStatement(this);
+ if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) {
+ fprintf(stderr, "could not compile FILE_PATH_QUERY\n");
+ exit(-1);
+ }
+ }
+ if (!mObjectInfoQuery) {
+ mObjectInfoQuery = new SqliteStatement(this);
+ if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) {
+ fprintf(stderr, "could not compile GET_OBJECT_INFO_QUERY\n");
+ exit(-1);
+ }
}
-
if (!mFileInserter) {
mFileInserter = new SqliteStatement(this);
if (!mFileInserter->prepare(FILE_INSERT)) {
fprintf(stderr, "could not compile FILE_INSERT\n");
- delete mFileInserter;
- mFileInserter = NULL;
- return kInvalidObjectHandle;
+ exit(-1);
+ }
+ }
+ if (!mFileDeleter) {
+ mFileDeleter = new SqliteStatement(this);
+ if (!mFileDeleter->prepare(FILE_DELETE)) {
+ fprintf(stderr, "could not compile FILE_DELETE\n");
+ exit(-1);
+ }
+ }
+ if (!mAudioInserter) {
+ mAudioInserter = new SqliteStatement(this);
+ if (!mAudioInserter->prepare(AUDIO_INSERT)) {
+ fprintf(stderr, "could not compile AUDIO_INSERT\n");
+ exit(-1);
+ }
+ }
+ if (!mAudioDeleter) {
+ mAudioDeleter = new SqliteStatement(this);
+ if (!mAudioDeleter->prepare(AUDIO_DELETE)) {
+ fprintf(stderr, "could not compile AUDIO_DELETE\n");
+ exit(-1);
+ }
+ }
+
+ return true;
+}
+
+uint32_t MtpDatabase::getTableForFile(MtpObjectFormat format) {
+ switch (format) {
+ case MTP_FORMAT_AIFF:
+ case MTP_FORMAT_WAV:
+ case MTP_FORMAT_MP3:
+ case MTP_FORMAT_FLAC:
+ case MTP_FORMAT_UNDEFINED_AUDIO:
+ case MTP_FORMAT_WMA:
+ case MTP_FORMAT_OGG:
+ case MTP_FORMAT_AAC:
+ case MTP_FORMAT_AUDIBLE:
+ return kObjectHandleTableAudio;
+ case MTP_FORMAT_AVI:
+ case MTP_FORMAT_MPEG:
+ case MTP_FORMAT_ASF:
+ case MTP_FORMAT_UNDEFINED_VIDEO:
+ case MTP_FORMAT_WMV:
+ case MTP_FORMAT_MP4_CONTAINER:
+ case MTP_FORMAT_MP2:
+ case MTP_FORMAT_3GP_CONTAINER:
+ return kObjectHandleTableVideo;
+ case MTP_FORMAT_DEFINED:
+ case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_TIFF_EP:
+ case MTP_FORMAT_FLASHPIX:
+ case MTP_FORMAT_BMP:
+ case MTP_FORMAT_CIFF:
+ case MTP_FORMAT_GIF:
+ case MTP_FORMAT_JFIF:
+ case MTP_FORMAT_CD:
+ case MTP_FORMAT_PICT:
+ case MTP_FORMAT_PNG:
+ case MTP_FORMAT_TIFF:
+ case MTP_FORMAT_TIFF_IT:
+ case MTP_FORMAT_JP2:
+ case MTP_FORMAT_JPX:
+ case MTP_FORMAT_WINDOWS_IMAGE_FORMAT:
+ return kObjectHandleTableImage;
+ case MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST:
+ case MTP_FORMAT_ABSTRACT_AV_PLAYLIST:
+ case MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST:
+ case MTP_FORMAT_WPL_PLAYLIST:
+ case MTP_FORMAT_M3U_PLAYLIST:
+ case MTP_FORMAT_MPL_PLAYLIST:
+ case MTP_FORMAT_ASX_PLAYLIST:
+ case MTP_FORMAT_PLS_PLAYLIST:
+ return kObjectHandleTablePlaylist;
+ default:
+ return kObjectHandleTableFile;
+ }
+}
+
+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;
}
}
- mFileInserter->bind(PATH_COLUMN, path);
- mFileInserter->bind(FORMAT_COLUMN, format);
- mFileInserter->bind(PARENT_COLUMN, parent);
- mFileInserter->bind(STORAGE_COLUMN, storage);
- mFileInserter->bind(SIZE_COLUMN, size);
- mFileInserter->bind(CREATED_COLUMN, created);
- mFileInserter->bind(MODIFIED_COLUMN, modified);
+
+ 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 row = lastInsertedRow();
- return (row > 0 ? row : kInvalidObjectHandle);
+ 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,
@@ -168,7 +335,7 @@ MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
bool whereParent = (parent != 0);
char intBuffer[20];
- MtpString query("SELECT _id FROM files");
+ MtpString query("SELECT _id,format FROM files");
if (whereStorage || whereFormat || whereParent)
query += " WHERE";
if (whereStorage) {
@@ -184,6 +351,8 @@ MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
query += intBuffer;
}
if (whereParent) {
+ if (parent != MTP_PARENT_ROOT)
+ parent &= kObjectHandleIndexMask;
snprintf(intBuffer, sizeof(intBuffer), "%d", parent);
if (whereStorage || whereFormat)
query += " AND";
@@ -201,14 +370,18 @@ MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
if (stmt.step()) {
int index = stmt.getColumnInt(0);
printf("stmt.getColumnInt returned %d\n", index);
- if (index > 0)
+ if (index > 0) {
+ MtpObjectFormat format = stmt.getColumnInt(1);
+ index |= getTableForFile(format);
list->push(index);
+ }
}
}
printf("list size: %d\n", list->size());
return list;
}
+
MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
@@ -216,6 +389,9 @@ MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle,
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);
@@ -272,18 +448,10 @@ MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
MtpDataPacket& packet) {
char date[20];
- if (mObjectInfoQuery)
- mObjectInfoQuery->reset();
- else {
- mObjectInfoQuery = new SqliteStatement(this);
- if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) {
- fprintf(stderr, "could not compile FILE_ID_QUERY\n");
- delete mObjectInfoQuery;
- mObjectInfoQuery = NULL;
- return MTP_RESPONSE_GENERAL_ERROR;
- }
- }
+ if (handle != MTP_PARENT_ROOT)
+ handle &= kObjectHandleIndexMask;
+ mObjectInfoQuery->reset();
mObjectInfoQuery->bind(1, handle);
if (!mObjectInfoQuery->step())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
@@ -297,8 +465,7 @@ MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
if (lastSlash)
name = lastSlash + 1;
int64_t size = mObjectInfoQuery->getColumnInt64(4);
- time_t created = mObjectInfoQuery->getColumnInt(5);
- time_t modified = mObjectInfoQuery->getColumnInt(6);
+ time_t modified = mObjectInfoQuery->getColumnInt(5);
int associationType = (format == MTP_FORMAT_ASSOCIATION ?
MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
MTP_ASSOCIATION_TYPE_UNDEFINED);
@@ -321,8 +488,7 @@ MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
packet.putUInt32(0); // association desc
packet.putUInt32(0); // sequence number
packet.putString(name); // file name
- formatDateTime(created, date, sizeof(date));
- packet.putString(date); // date created
+ packet.putEmptyString();
formatDateTime(modified, date, sizeof(date));
packet.putString(date); // date modified
packet.putEmptyString(); // keywords
@@ -333,18 +499,9 @@ MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
MtpString& filePath,
int64_t& fileLength) {
- if (mFilePathQuery)
- mFilePathQuery->reset();
- else {
- mFilePathQuery = new SqliteStatement(this);
- if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) {
- fprintf(stderr, "could not compile FILE_ID_QUERY\n");
- delete mFilePathQuery;
- mFilePathQuery = NULL;
- return kInvalidObjectHandle;
- }
- }
-
+ if (handle != MTP_PARENT_ROOT)
+ handle &= kObjectHandleIndexMask;
+ mFilePathQuery->reset();
mFilePathQuery->bind(1, handle);
if (!mFilePathQuery->step())
return false;
@@ -358,22 +515,52 @@ bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
}
bool MtpDatabase::deleteFile(MtpObjectHandle handle) {
- if (!mFileDeleter) {
- mFileDeleter = new SqliteStatement(this);
- if (!mFileDeleter->prepare(FILE_DELETE)) {
- fprintf(stderr, "could not compile FILE_DELETE\n");
- delete mFileDeleter;
- mFileDeleter = NULL;
- return false;
- }
- }
-printf("deleteFile %d\n", 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()) {
+ printf("getFileList ended early\n");
+ 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
diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h
index 2a48155..a6be6a6 100644
--- a/media/mtp/MtpDatabase.h
+++ b/media/mtp/MtpDatabase.h
@@ -33,20 +33,38 @@ private:
SqliteStatement* mObjectInfoQuery;
SqliteStatement* mFileInserter;
SqliteStatement* mFileDeleter;
+ SqliteStatement* mAudioInserter;
+ SqliteStatement* mAudioDeleter;
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,
MtpObjectFormat format,
MtpObjectHandle parent,
MtpStorageID storage,
uint64_t size,
- time_t created,
time_t modified);
+ MtpObjectHandle addAudioFile(MtpObjectHandle id);
+
+ MtpObjectHandle addAudioFile(MtpObjectHandle id,
+ 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);
+
MtpObjectHandleList* getObjectList(MtpStorageID storageID,
MtpObjectFormat format,
MtpObjectHandle parent);
@@ -62,6 +80,9 @@ public:
MtpString& filePath,
int64_t& fileLength);
bool deleteFile(MtpObjectHandle handle);
+
+ // helper for media scanner
+ MtpObjectHandle* getFileList(int& outCount);
};
}; // namespace android
diff --git a/media/mtp/MtpMediaScanner.cpp b/media/mtp/MtpMediaScanner.cpp
new file mode 100644
index 0000000..8b08f36
--- /dev/null
+++ b/media/mtp/MtpMediaScanner.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+#include "MtpDatabase.h"
+#include "MtpMediaScanner.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include <media/mediascanner.h>
+#include <media/stagefright/StagefrightMediaScanner.h>
+
+namespace android {
+
+class MtpMediaScannerClient : public MediaScannerClient
+{
+public:
+ MtpMediaScannerClient()
+ {
+ reset();
+ }
+
+ virtual ~MtpMediaScannerClient()
+ {
+ }
+
+ // returns true if it succeeded, false if an exception occured in the Java code
+ virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
+ {
+ printf("scanFile %s\n", path);
+ return true;
+ }
+
+ // returns true if it succeeded, false if an exception occured in the Java code
+ virtual bool handleStringTag(const char* name, const char* value)
+ {
+ int temp;
+
+ if (!strcmp(name, "title")) {
+ mTitle = value;
+ mHasTitle = true;
+ } else if (!strcmp(name, "artist")) {
+ mArtist = value;
+ mHasArtist = true;
+ } else if (!strcmp(name, "album")) {
+ mAlbum = value;
+ mHasAlbum = true;
+ } else if (!strcmp(name, "albumartist")) {
+ mAlbumArtist = value;
+ mHasAlbumArtist = true;
+ } else if (!strcmp(name, "genre")) {
+ // FIXME - handle numeric values here
+ mGenre = value;
+ mHasGenre = true;
+ } else if (!strcmp(name, "composer")) {
+ mComposer = value;
+ mHasComposer = true;
+ } else if (!strcmp(name, "tracknumber")) {
+ if (sscanf(value, "%d", &temp) == 1)
+ mTrack = temp;
+ } else if (!strcmp(name, "discnumber")) {
+ // currently unused
+ } else if (!strcmp(name, "year") || !strcmp(name, "date")) {
+ if (sscanf(value, "%d", &temp) == 1)
+ mYear = temp;
+ } else if (!strcmp(name, "duration")) {
+ if (sscanf(value, "%d", &temp) == 1)
+ mDuration = temp;
+ } else {
+ printf("handleStringTag %s : %s\n", name, value);
+ }
+ return true;
+ }
+
+ // returns true if it succeeded, false if an exception occured in the Java code
+ virtual bool setMimeType(const char* mimeType)
+ {
+ mMimeType = mimeType;
+ mHasMimeType = true;
+ return true;
+ }
+
+ // returns true if it succeeded, false if an exception occured in the Java code
+ virtual bool addNoMediaFolder(const char* path)
+ {
+ printf("addNoMediaFolder %s\n", path);
+ return true;
+ }
+
+ void reset()
+ {
+ mHasTitle = false;
+ mHasArtist = false;
+ mHasAlbum = false;
+ mHasAlbumArtist = false;
+ mHasGenre = false;
+ mHasComposer = false;
+ mHasMimeType = false;
+ mTrack = mYear = mDuration = 0;
+ }
+
+ inline const char* getTitle() const { return mHasTitle ? (const char *)mTitle : NULL; }
+ inline const char* getArtist() const { return mHasArtist ? (const char *)mArtist : NULL; }
+ inline const char* getAlbum() const { return mHasAlbum ? (const char *)mAlbum : NULL; }
+ inline const char* getAlbumArtist() const { return mHasAlbumArtist ? (const char *)mAlbumArtist : NULL; }
+ inline const char* getGenre() const { return mHasGenre ? (const char *)mGenre : NULL; }
+ inline const char* getComposer() const { return mHasComposer ? (const char *)mComposer : NULL; }
+ inline const char* getMimeType() const { return mHasMimeType ? (const char *)mMimeType : NULL; }
+ inline int getTrack() const { return mTrack; }
+ inline int getYear() const { return mYear; }
+ inline int getDuration() const { return mDuration; }
+
+private:
+ MtpString mTitle;
+ MtpString mArtist;
+ MtpString mAlbum;
+ MtpString mAlbumArtist;
+ MtpString mGenre;
+ MtpString mComposer;
+ MtpString mMimeType;
+
+ bool mHasTitle;
+ bool mHasArtist;
+ bool mHasAlbum;
+ bool mHasAlbumArtist;
+ bool mHasGenre;
+ bool mHasComposer;
+ bool mHasMimeType;
+
+ int mTrack;
+ int mYear;
+ int mDuration;
+};
+
+
+MtpMediaScanner::MtpMediaScanner(MtpStorageID id, const char* filePath, MtpDatabase* db)
+ : mStorageID(id),
+ mFilePath(filePath),
+ mDatabase(db),
+ mMediaScanner(NULL),
+ mMediaScannerClient(NULL),
+ mFileList(NULL),
+ mFileCount(0)
+{
+ mMediaScanner = new StagefrightMediaScanner;
+ mMediaScannerClient = new MtpMediaScannerClient;
+}
+
+MtpMediaScanner::~MtpMediaScanner() {
+}
+
+bool MtpMediaScanner::scanFiles() {
+ mDatabase->beginTransaction();
+ mFileCount = 0;
+ mFileList = mDatabase->getFileList(mFileCount);
+
+ int ret = scanDirectory(mFilePath, MTP_PARENT_ROOT);
+
+ for (int i = 0; i < mFileCount; i++) {
+ MtpObjectHandle test = mFileList[i];
+ if (! (test & kObjectHandleMarkBit)) {
+ printf("delete missing file %08X\n", test);
+ mDatabase->deleteFile(test);
+ }
+ }
+
+ delete[] mFileList;
+ mFileCount = 0;
+ mDatabase->commitTransaction();
+ return (ret == 0);
+}
+
+
+static const struct MediaFileTypeEntry
+{
+ const char* extension;
+ MtpObjectFormat format;
+ uint32_t table;
+} sFileTypes[] =
+{
+ { "MP3", MTP_FORMAT_MP3, kObjectHandleTableAudio },
+ { "M4A", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "WAV", MTP_FORMAT_WAV, kObjectHandleTableAudio },
+ { "AMR", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "AWB", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "WMA", MTP_FORMAT_WMA, kObjectHandleTableAudio },
+ { "OGG", MTP_FORMAT_OGG, kObjectHandleTableAudio },
+ { "OGA", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "AAC", MTP_FORMAT_AAC, kObjectHandleTableAudio },
+ { "MID", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "MIDI", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "XMF", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "RTTTL", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "SMF", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "IMY", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "RTX", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "OTA", MTP_FORMAT_UNDEFINED_AUDIO, kObjectHandleTableAudio },
+ { "MPEG", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "MP4", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "M4V", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "3GP", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "3GPP", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "3G2", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "3GPP2", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "WMV", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "ASF", MTP_FORMAT_UNDEFINED_VIDEO, kObjectHandleTableVideo },
+ { "JPG", MTP_FORMAT_EXIF_JPEG, kObjectHandleTableImage },
+ { "JPEG", MTP_FORMAT_EXIF_JPEG, kObjectHandleTableImage },
+ { "GIF", MTP_FORMAT_GIF, kObjectHandleTableImage },
+ { "PNG", MTP_FORMAT_PNG, kObjectHandleTableImage },
+ { "BMP", MTP_FORMAT_BMP, kObjectHandleTableImage },
+ { "WBMP", MTP_FORMAT_BMP, kObjectHandleTableImage },
+ { "M3U", MTP_FORMAT_M3U_PLAYLIST, kObjectHandleTablePlaylist },
+ { "PLS", MTP_FORMAT_PLS_PLAYLIST, kObjectHandleTablePlaylist },
+ { "WPL", MTP_FORMAT_WPL_PLAYLIST, kObjectHandleTablePlaylist },
+};
+
+MtpObjectFormat MtpMediaScanner::getFileFormat(const char* path, uint32_t& table)
+{
+ const char* extension = strrchr(path, '.');
+ if (!extension)
+ return MTP_FORMAT_UNDEFINED;
+ extension++; // skip the dot
+
+ for (unsigned i = 0; i < sizeof(sFileTypes) / sizeof(sFileTypes[0]); i++) {
+ if (!strcasecmp(extension, sFileTypes[i].extension)) {
+ table = sFileTypes[i].table;
+ return sFileTypes[i].format;
+ }
+ }
+ table = kObjectHandleTableFile;
+ return MTP_FORMAT_UNDEFINED;
+}
+
+int MtpMediaScanner::scanDirectory(const char* path, MtpObjectHandle parent)
+{
+ char buffer[PATH_MAX];
+ struct dirent* entry;
+
+ unsigned length = strlen(path);
+ if (length > sizeof(buffer) + 2) {
+ fprintf(stderr, "path too long: %s\n", path);
+ }
+
+ DIR* dir = opendir(path);
+ if (!dir) {
+ fprintf(stderr, "opendir %s failed, errno: %d", path, errno);
+ return -1;
+ }
+
+ strncpy(buffer, path, sizeof(buffer));
+ char* fileStart = buffer + length;
+ // make sure we have a trailing slash
+ if (fileStart[-1] != '/') {
+ *(fileStart++) = '/';
+ }
+ int fileNameLength = sizeof(buffer) + fileStart - buffer;
+
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and "..", as well as any files or directories staring with dot
+ if (name[0] == '.') {
+ continue;
+ }
+ if (strlen(name) + 1 > fileNameLength) {
+ fprintf(stderr, "path too long for %s\n", name);
+ continue;
+ }
+ strcpy(fileStart, name);
+
+ struct stat statbuf;
+ memset(&statbuf, 0, sizeof(statbuf));
+ stat(buffer, &statbuf);
+
+ if (entry->d_type == DT_DIR) {
+ MtpObjectHandle handle = mDatabase->getObjectHandle(buffer);
+ if (handle) {
+ markFile(handle);
+ } else {
+ handle = mDatabase->addFile(buffer, MTP_FORMAT_ASSOCIATION,
+ parent, mStorageID, 0, statbuf.st_mtime);
+ }
+ scanDirectory(buffer, handle);
+ } else if (entry->d_type == DT_REG) {
+ scanFile(buffer, parent, statbuf);
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+void MtpMediaScanner::scanFile(const char* path, MtpObjectHandle parent, struct stat& statbuf) {
+ uint32_t table;
+ MtpObjectFormat format = getFileFormat(path, table);
+ // don't scan unknown file types
+ if (format == MTP_FORMAT_UNDEFINED)
+ return;
+ MtpObjectHandle handle = mDatabase->getObjectHandle(path);
+ // fixme - rescan if mod date changed
+ if (handle) {
+ markFile(handle);
+ } else {
+ mDatabase->beginTransaction();
+ handle = mDatabase->addFile(path, format, parent, mStorageID,
+ statbuf.st_size, statbuf.st_mtime);
+ if (handle <= 0) {
+ fprintf(stderr, "addFile failed in MtpMediaScanner::scanFile()\n");
+ mDatabase->rollbackTransaction();
+ return;
+ }
+
+ if (table == kObjectHandleTableAudio) {
+ mMediaScannerClient->reset();
+ mMediaScanner->processFile(path, NULL, *mMediaScannerClient);
+ handle = mDatabase->addAudioFile(handle,
+ mMediaScannerClient->getTitle(),
+ mMediaScannerClient->getArtist(),
+ mMediaScannerClient->getAlbum(),
+ mMediaScannerClient->getAlbumArtist(),
+ mMediaScannerClient->getGenre(),
+ mMediaScannerClient->getComposer(),
+ mMediaScannerClient->getMimeType(),
+ mMediaScannerClient->getTrack(),
+ mMediaScannerClient->getYear(),
+ mMediaScannerClient->getDuration());
+ }
+ mDatabase->commitTransaction();
+ }
+}
+
+void MtpMediaScanner::markFile(MtpObjectHandle handle) {
+ if (mFileList) {
+ handle &= kObjectHandleIndexMask;
+ // binary search for the file in mFileList
+ int low = 0;
+ int high = mFileCount;
+ int index;
+
+ while (low < high) {
+ index = (low + high) >> 1;
+ MtpObjectHandle test = (mFileList[index] & kObjectHandleIndexMask);
+ if (handle < test)
+ high = index; // item is less than index
+ else if (handle > test)
+ low = index + 1; // item is greater than index
+ else {
+ mFileList[index] |= kObjectHandleMarkBit;
+ return;
+ }
+ }
+ fprintf(stderr, "file %d not found in mFileList\n", handle);
+ }
+}
+
+} // namespace android
diff --git a/media/mtp/MtpMediaScanner.h b/media/mtp/MtpMediaScanner.h
new file mode 100644
index 0000000..53d5063
--- /dev/null
+++ b/media/mtp/MtpMediaScanner.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef _MTP_MEDIA_SCANNER_H
+#define _MTP_MEDIA_SCANNER_H
+
+struct stat;
+
+namespace android {
+
+class MtpDatabase;
+class SqliteStatement;
+class MediaScanner;
+class MtpMediaScannerClient;
+
+class MtpMediaScanner {
+private:
+ MtpStorageID mStorageID;
+ const char* mFilePath;
+ MtpDatabase* mDatabase;
+ MediaScanner* mMediaScanner;
+ MtpMediaScannerClient* mMediaScannerClient;
+
+ // for garbage collecting missing files
+ MtpObjectHandle* mFileList;
+ int mFileCount;
+
+public:
+ MtpMediaScanner(MtpStorageID id, const char* filePath, MtpDatabase* db);
+ virtual ~MtpMediaScanner();
+
+ bool scanFiles();
+
+private:
+ MtpObjectFormat getFileFormat(const char* path, uint32_t& table);
+ int scanDirectory(const char* path, MtpObjectHandle parent);
+ void scanFile(const char* path, MtpObjectHandle parent, struct stat& statbuf);
+ void markFile(MtpObjectHandle handle);
+};
+
+}; // namespace android
+
+#endif // _MTP_MEDIA_SCANNER_H
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index d868926..6a90568 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -79,9 +79,35 @@ static const MtpObjectProperty kSupportedObjectProperties[] = {
};
static const MtpObjectFormat kSupportedPlaybackFormats[] = {
- // FIXME - fill this out later
+ // MTP_FORMAT_UNDEFINED,
MTP_FORMAT_ASSOCIATION,
+ // MTP_FORMAT_TEXT,
+ // MTP_FORMAT_HTML,
MTP_FORMAT_MP3,
+ //MTP_FORMAT_AVI,
+ MTP_FORMAT_MPEG,
+ // MTP_FORMAT_ASF,
+ MTP_FORMAT_EXIF_JPEG,
+ MTP_FORMAT_TIFF_EP,
+ // MTP_FORMAT_BMP,
+ MTP_FORMAT_GIF,
+ MTP_FORMAT_JFIF,
+ MTP_FORMAT_PNG,
+ MTP_FORMAT_TIFF,
+ MTP_FORMAT_WMA,
+ MTP_FORMAT_OGG,
+ MTP_FORMAT_AAC,
+ // MTP_FORMAT_FLAC,
+ // MTP_FORMAT_WMV,
+ MTP_FORMAT_MP4_CONTAINER,
+ MTP_FORMAT_MP2,
+ MTP_FORMAT_3GP_CONTAINER,
+ // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
+ // MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
+ // MTP_FORMAT_WPL_PLAYLIST,
+ // MTP_FORMAT_M3U_PLAYLIST,
+ // MTP_FORMAT_MPL_PLAYLIST,
+ // MTP_FORMAT_PLS_PLAYLIST,
};
MtpServer::MtpServer(int fd, const char* databasePath)
@@ -420,9 +446,7 @@ MtpResponseCode MtpServer::doSendObjectInfo() {
mData.getString(modified); // date modified
// keywords follow
- time_t createdTime, modifiedTime;
- if (!parseDateTime(created, createdTime))
- createdTime = 0;
+ time_t modifiedTime;
if (!parseDateTime(modified, modifiedTime))
modifiedTime = 0;
printf("SendObjectInfo format: %04X size: %d name: %s, created: %s, modified: %s\n",
@@ -432,11 +456,17 @@ format, mSendObjectFileSize, (const char*)name, (const char*)created, (const cha
path += "/";
path += (const char *)name;
- MtpObjectHandle handle = mDatabase->addFile((const char*)path,
- format, parent, storageID, mSendObjectFileSize,
- createdTime, modifiedTime);
- if (handle == kInvalidObjectHandle)
+ mDatabase->beginTransaction();
+ MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID,
+ mSendObjectFileSize, modifiedTime);
+ if (handle == kInvalidObjectHandle) {
+ mDatabase->rollbackTransaction();
return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ uint32_t table = MtpDatabase::getTableForFile(format);
+ if (table == kObjectHandleTableAudio)
+ handle = mDatabase->addAudioFile(handle);
+ mDatabase->commitTransaction();
if (format == MTP_FORMAT_ASSOCIATION) {
mode_t mask = umask(0);
diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp
index d4de819..f176148 100644
--- a/media/mtp/MtpStorage.cpp
+++ b/media/mtp/MtpStorage.cpp
@@ -16,6 +16,7 @@
#include "MtpDatabase.h"
#include "MtpStorage.h"
+#include "MtpMediaScanner.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -74,65 +75,8 @@ const char* MtpStorage::getDescription() const {
}
bool MtpStorage::scanFiles() {
- mDatabase->beginTransaction();
- int ret = scanDirectory(mFilePath, MTP_PARENT_ROOT);
- mDatabase->commitTransaction();
- return (ret == 0);
-}
-
-int MtpStorage::scanDirectory(const char* path, MtpObjectHandle parent)
-{
- char buffer[PATH_MAX];
- struct dirent* entry;
-
- int length = strlen(path);
- if (length > sizeof(buffer) + 2) {
- fprintf(stderr, "path too long: %s\n", path);
- }
-
- DIR* dir = opendir(path);
- if (!dir) {
- fprintf(stderr, "opendir %s failed, errno: %d", path, errno);
- return -1;
- }
-
- strncpy(buffer, path, sizeof(buffer));
- char* fileStart = buffer + length;
- // make sure we have a trailing slash
- if (fileStart[-1] != '/') {
- *(fileStart++) = '/';
- }
- int fileNameLength = sizeof(buffer) + fileStart - buffer;
-
- while ((entry = readdir(dir))) {
- const char* name = entry->d_name;
-
- // ignore "." and "..", as well as any files or directories staring with dot
- if (name[0] == '.') {
- continue;
- }
- if (strlen(name) + 1 > fileNameLength) {
- fprintf(stderr, "path too long for %s\n", name);
- continue;
- }
- strcpy(fileStart, name);
-
- struct stat statbuf;
- memset(&statbuf, 0, sizeof(statbuf));
- stat(buffer, &statbuf);
-
- if (entry->d_type == DT_DIR) {
- MtpObjectHandle handle = mDatabase->addFile(buffer, MTP_FORMAT_ASSOCIATION,
- parent, mStorageID, 0, 0, statbuf.st_mtime);
- scanDirectory(buffer, handle);
- } else if (entry->d_type == DT_REG) {
- mDatabase->addFile(buffer, MTP_FORMAT_UNDEFINED, parent, mStorageID,
- statbuf.st_size, 0, statbuf.st_mtime);
- }
- }
-
- closedir(dir);
- return 0;
+ MtpMediaScanner scanner(mStorageID, mFilePath, mDatabase);
+ return scanner.scanFiles();
}
} // namespace android
diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h
index b1d4408..6097272 100644
--- a/media/mtp/MtpStorage.h
+++ b/media/mtp/MtpStorage.h
@@ -46,9 +46,6 @@ public:
inline const char* getPath() const { return mFilePath; }
bool scanFiles();
-
-private:
- int scanDirectory(const char* path, MtpObjectHandle parent);
};
}; // namespace android
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 27abaa7..57a0281 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -32,7 +32,17 @@ typedef uint16_t MtpObjectProperty;
// values 0x00000000 and 0xFFFFFFFF are reserved for special purposes.
typedef uint32_t MtpObjectHandle;
-#define kInvalidObjectHandle 0xFFFFFFFF
+#define kInvalidObjectHandle 0xFFFFFFFF
+
+// MtpObjectHandle bits and masks
+#define kObjectHandleMarkBit 0x80000000 // used for mark & sweep by MtpMediaScanner
+#define kObjectHandleTableMask 0x70000000 // mask for object table
+#define kObjectHandleTableFile 0x00000000 // object is only in the file table
+#define kObjectHandleTableAudio 0x10000000 // object is in the audio table
+#define kObjectHandleTableVideo 0x20000000 // object is in the video table
+#define kObjectHandleTableImage 0x30000000 // object is in the images table
+#define kObjectHandleTablePlaylist 0x40000000 // object is in the playlist table
+#define kObjectHandleIndexMask 0x0FFFFFFF // mask for object index in file table
#define MTP_STANDARD_VERSION 100
diff --git a/media/mtp/scantest.cpp b/media/mtp/scantest.cpp
new file mode 100644
index 0000000..f910bb6
--- /dev/null
+++ b/media/mtp/scantest.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+
+#include "MtpDatabase.h"
+#include "MtpMediaScanner.h"
+
+using namespace android;
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <storage path>\n", argv[0]);
+ return -1;
+ }
+
+ MtpDatabase* database = new MtpDatabase();
+ database->open("scantest.db", true);
+
+ MtpMediaScanner scanner(1, argv[1], database);
+ scanner.scanFiles();
+ database->close();
+
+ return 0;
+}