summaryrefslogtreecommitdiffstats
path: root/media/libmedia/MediaScanner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmedia/MediaScanner.cpp')
-rw-r--r--media/libmedia/MediaScanner.cpp240
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