android_frameworks_base/media/mtp/MtpMediaScanner.cpp
Mike Lockwood 2d6c9e9c4e MTP: remove separate audio table from prototype MTP database
Change-Id: I5e4b49b741a0a3c6ddf5afcbb20a0d7c9515c8ce
Signed-off-by: Mike Lockwood <lockwood@android.com>
2010-07-08 16:02:16 -04:00

234 lines
7.1 KiB
C++

/*
* 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 "MtpMediaScanner"
#include "MtpDebug.h"
#include "MtpDatabase.h"
#include "MtpMediaScanner.h"
#include "mtp.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>
namespace android {
MtpMediaScanner::MtpMediaScanner(MtpStorageID id, const char* filePath, MtpDatabase* db)
: mStorageID(id),
mFilePath(filePath),
mDatabase(db),
mFileList(NULL),
mFileCount(0)
{
}
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)) {
LOGV("delete missing file %08X", test);
mDatabase->deleteFile(test);
}
}
delete[] mFileList;
mFileCount = 0;
mDatabase->commitTransaction();
return (ret == 0);
}
static const struct MediaFileTypeEntry
{
const char* extension;
MtpObjectFormat format;
} sFileTypes[] =
{
{ "MP3", MTP_FORMAT_MP3, },
{ "M4A", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "WAV", MTP_FORMAT_WAV, },
{ "AMR", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "AWB", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "WMA", MTP_FORMAT_WMA, },
{ "OGG", MTP_FORMAT_OGG, },
{ "OGA", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "AAC", MTP_FORMAT_AAC, },
{ "MID", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "MIDI", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "XMF", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "RTTTL", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "SMF", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "IMY", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "RTX", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "OTA", MTP_FORMAT_UNDEFINED_AUDIO, },
{ "MPEG", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "MP4", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "M4V", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "3GP", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "3GPP", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "3G2", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "3GPP2", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "WMV", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "ASF", MTP_FORMAT_UNDEFINED_VIDEO, },
{ "JPG", MTP_FORMAT_EXIF_JPEG, },
{ "JPEG", MTP_FORMAT_EXIF_JPEG, },
{ "GIF", MTP_FORMAT_GIF, },
{ "PNG", MTP_FORMAT_PNG, },
{ "BMP", MTP_FORMAT_BMP, },
{ "WBMP", MTP_FORMAT_BMP, },
{ "M3U", MTP_FORMAT_M3U_PLAYLIST, },
{ "PLS", MTP_FORMAT_PLS_PLAYLIST, },
{ "WPL", MTP_FORMAT_WPL_PLAYLIST, },
};
MtpObjectFormat MtpMediaScanner::getFileFormat(const char* path)
{
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)) {
return sFileTypes[i].format;
}
}
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) {
LOGE("path too long: %s", path);
}
DIR* dir = opendir(path);
if (!dir) {
LOGE("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) {
LOGE("path too long for %s", name);
continue;
}
strcpy(fileStart, name);
struct stat statbuf;
memset(&statbuf, 0, sizeof(statbuf));
stat(buffer, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
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 (S_ISREG(statbuf.st_mode)) {
scanFile(buffer, parent, statbuf);
}
}
closedir(dir);
return 0;
}
void MtpMediaScanner::scanFile(const char* path, MtpObjectHandle parent, struct stat& statbuf) {
MtpObjectFormat format = getFileFormat(path);
// 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) {
LOGE("addFile failed in MtpMediaScanner::scanFile()");
mDatabase->rollbackTransaction();
return;
}
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;
}
}
LOGE("file %d not found in mFileList", handle);
}
}
} // namespace android