diff options
Diffstat (limited to 'media/libmedia/MediaScanner.cpp')
-rw-r--r-- | media/libmedia/MediaScanner.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/media/libmedia/MediaScanner.cpp b/media/libmedia/MediaScanner.cpp new file mode 100644 index 0000000..28b5aa7 --- /dev/null +++ b/media/libmedia/MediaScanner.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2009 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_NDEBUG 0 +#define LOG_TAG "MediaScanner" +#include <cutils/properties.h> +#include <utils/Log.h> + +#include <media/mediascanner.h> + +#include <sys/stat.h> +#include <dirent.h> + +namespace android { + +MediaScanner::MediaScanner() + : mLocale(NULL), mSkipList(NULL), mSkipIndex(NULL) { + loadSkipList(); +} + +MediaScanner::~MediaScanner() { + setLocale(NULL); + free(mSkipList); + free(mSkipIndex); +} + +void MediaScanner::setLocale(const char *locale) { + if (mLocale) { + free(mLocale); + mLocale = NULL; + } + if (locale) { + mLocale = strdup(locale); + } +} + +const char *MediaScanner::locale() const { + return mLocale; +} + +void MediaScanner::loadSkipList() { + mSkipList = (char *)malloc(PROPERTY_VALUE_MAX * sizeof(char)); + if (mSkipList) { + property_get("testing.mediascanner.skiplist", mSkipList, ""); + } + if (!mSkipList || (strlen(mSkipList) == 0)) { + free(mSkipList); + mSkipList = NULL; + return; + } + mSkipIndex = (int *)malloc(PROPERTY_VALUE_MAX * sizeof(int)); + if (mSkipIndex) { + // dup it because strtok will modify the string + char *skipList = strdup(mSkipList); + if (skipList) { + char * path = strtok(skipList, ","); + int i = 0; + while (path) { + mSkipIndex[i++] = strlen(path); + path = strtok(NULL, ","); + } + mSkipIndex[i] = -1; + free(skipList); + } + } +} + +MediaScanResult MediaScanner::processDirectory( + const char *path, MediaScannerClient &client) { + int pathLength = strlen(path); + if (pathLength >= PATH_MAX) { + return MEDIA_SCAN_RESULT_SKIPPED; + } + char* pathBuffer = (char *)malloc(PATH_MAX + 1); + if (!pathBuffer) { + return MEDIA_SCAN_RESULT_ERROR; + } + + int pathRemaining = PATH_MAX - pathLength; + strcpy(pathBuffer, path); + if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') { + pathBuffer[pathLength] = '/'; + pathBuffer[pathLength + 1] = 0; + --pathRemaining; + } + + client.setLocale(locale()); + + MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false); + + free(pathBuffer); + + return result; +} + +bool MediaScanner::shouldSkipDirectory(char *path) { + if (path && mSkipList && mSkipIndex) { + int len = strlen(path); + int idx = 0; + // track the start position of next path in the comma + // separated list obtained from getprop + int startPos = 0; + while (mSkipIndex[idx] != -1) { + // no point to match path name if strlen mismatch + if ((len == mSkipIndex[idx]) + // pick out the path segment from comma separated list + // to compare against current path parameter + && (strncmp(path, &mSkipList[startPos], len) == 0)) { + return true; + } + startPos += mSkipIndex[idx] + 1; // extra char for the delimiter + idx++; + } + } + return false; +} + +MediaScanResult MediaScanner::doProcessDirectory( + char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) { + // place to copy file or directory name + char* fileSpot = path + strlen(path); + struct dirent* entry; + + if (shouldSkipDirectory(path)) { + ALOGD("Skipping: %s", path); + return MEDIA_SCAN_RESULT_OK; + } + + // Treat all files as non-media in directories that contain a ".nomedia" file + if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { + strcpy(fileSpot, ".nomedia"); + if (access(path, F_OK) == 0) { + ALOGV("found .nomedia, setting noMedia flag"); + noMedia = true; + } + + // restore path + fileSpot[0] = 0; + } + + DIR* dir = opendir(path); + if (!dir) { + ALOGW("Error opening directory '%s', skipping: %s.", path, strerror(errno)); + return MEDIA_SCAN_RESULT_SKIPPED; + } + + MediaScanResult result = MEDIA_SCAN_RESULT_OK; + while ((entry = readdir(dir))) { + if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot) + == MEDIA_SCAN_RESULT_ERROR) { + result = MEDIA_SCAN_RESULT_ERROR; + break; + } + } + closedir(dir); + return result; +} + +MediaScanResult MediaScanner::doProcessDirectoryEntry( + char *path, int pathRemaining, MediaScannerClient &client, bool noMedia, + struct dirent* entry, char* fileSpot) { + struct stat statbuf; + const char* name = entry->d_name; + + // ignore "." and ".." + if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { + return MEDIA_SCAN_RESULT_SKIPPED; + } + + int nameLength = strlen(name); + if (nameLength + 1 > pathRemaining) { + // path too long! + return MEDIA_SCAN_RESULT_SKIPPED; + } + strcpy(fileSpot, name); + + int type = entry->d_type; + if (type == DT_UNKNOWN) { + // If the type is unknown, stat() the file instead. + // This is sometimes necessary when accessing NFS mounted filesystems, but + // could be needed in other cases well. + if (stat(path, &statbuf) == 0) { + if (S_ISREG(statbuf.st_mode)) { + type = DT_REG; + } else if (S_ISDIR(statbuf.st_mode)) { + type = DT_DIR; + } + } else { + ALOGD("stat() failed for %s: %s", path, strerror(errno) ); + } + } + if (type == DT_DIR) { + bool childNoMedia = noMedia; + // set noMedia flag on directories with a name that starts with '.' + // for example, the Mac ".Trashes" directory + if (name[0] == '.') + childNoMedia = true; + + // report the directory to the client + if (stat(path, &statbuf) == 0) { + status_t status = client.scanFile(path, statbuf.st_mtime, 0, + true /*isDirectory*/, childNoMedia); + if (status) { + return MEDIA_SCAN_RESULT_ERROR; + } + } + + // and now process its contents + strcat(fileSpot, "/"); + MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1, + client, childNoMedia); + if (result == MEDIA_SCAN_RESULT_ERROR) { + return MEDIA_SCAN_RESULT_ERROR; + } + } else if (type == DT_REG) { + stat(path, &statbuf); + status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size, + false /*isDirectory*/, noMedia); + if (status) { + return MEDIA_SCAN_RESULT_ERROR; + } + } + + return MEDIA_SCAN_RESULT_OK; +} + +} // namespace android |