Home | History | Annotate | Download | only in libmedia
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "MediaScanner"
     19 #include <cutils/properties.h>
     20 #include <utils/Log.h>
     21 
     22 #include <media/mediascanner.h>
     23 
     24 #include <sys/stat.h>
     25 #include <dirent.h>
     26 
     27 namespace android {
     28 
     29 MediaScanner::MediaScanner()
     30     : mLocale(NULL), mSkipList(NULL), mSkipIndex(NULL) {
     31     loadSkipList();
     32 }
     33 
     34 MediaScanner::~MediaScanner() {
     35     setLocale(NULL);
     36     free(mSkipList);
     37     free(mSkipIndex);
     38 }
     39 
     40 void MediaScanner::setLocale(const char *locale) {
     41     if (mLocale) {
     42         free(mLocale);
     43         mLocale = NULL;
     44     }
     45     if (locale) {
     46         mLocale = strdup(locale);
     47     }
     48 }
     49 
     50 const char *MediaScanner::locale() const {
     51     return mLocale;
     52 }
     53 
     54 void MediaScanner::loadSkipList() {
     55     mSkipList = (char *)malloc(PROPERTY_VALUE_MAX * sizeof(char));
     56     if (mSkipList) {
     57         property_get("testing.mediascanner.skiplist", mSkipList, "");
     58     }
     59     if (!mSkipList || (strlen(mSkipList) == 0)) {
     60         free(mSkipList);
     61         mSkipList = NULL;
     62         return;
     63     }
     64     mSkipIndex = (int *)malloc(PROPERTY_VALUE_MAX * sizeof(int));
     65     if (mSkipIndex) {
     66         // dup it because strtok will modify the string
     67         char *skipList = strdup(mSkipList);
     68         if (skipList) {
     69             char * path = strtok(skipList, ",");
     70             int i = 0;
     71             while (path) {
     72                 mSkipIndex[i++] = strlen(path);
     73                 path = strtok(NULL, ",");
     74             }
     75             mSkipIndex[i] = -1;
     76             free(skipList);
     77         }
     78     }
     79 }
     80 
     81 MediaScanResult MediaScanner::processDirectory(
     82         const char *path, MediaScannerClient &client) {
     83     int pathLength = strlen(path);
     84     if (pathLength >= PATH_MAX) {
     85         return MEDIA_SCAN_RESULT_SKIPPED;
     86     }
     87     char* pathBuffer = (char *)malloc(PATH_MAX + 1);
     88     if (!pathBuffer) {
     89         return MEDIA_SCAN_RESULT_ERROR;
     90     }
     91 
     92     int pathRemaining = PATH_MAX - pathLength;
     93     strcpy(pathBuffer, path);
     94     if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') {
     95         pathBuffer[pathLength] = '/';
     96         pathBuffer[pathLength + 1] = 0;
     97         --pathRemaining;
     98     }
     99 
    100     client.setLocale(locale());
    101 
    102     MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false);
    103 
    104     free(pathBuffer);
    105 
    106     return result;
    107 }
    108 
    109 bool MediaScanner::shouldSkipDirectory(char *path) {
    110     if (path && mSkipList && mSkipIndex) {
    111         int len = strlen(path);
    112         int idx = 0;
    113         // track the start position of next path in the comma
    114         // separated list obtained from getprop
    115         int startPos = 0;
    116         while (mSkipIndex[idx] != -1) {
    117             // no point to match path name if strlen mismatch
    118             if ((len == mSkipIndex[idx])
    119                 // pick out the path segment from comma separated list
    120                 // to compare against current path parameter
    121                 && (strncmp(path, &mSkipList[startPos], len) == 0)) {
    122                 return true;
    123             }
    124             startPos += mSkipIndex[idx] + 1; // extra char for the delimiter
    125             idx++;
    126         }
    127     }
    128     return false;
    129 }
    130 
    131 MediaScanResult MediaScanner::doProcessDirectory(
    132         char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) {
    133     // place to copy file or directory name
    134     char* fileSpot = path + strlen(path);
    135     struct dirent* entry;
    136 
    137     if (shouldSkipDirectory(path)) {
    138         ALOGD("Skipping: %s", path);
    139         return MEDIA_SCAN_RESULT_OK;
    140     }
    141 
    142     // Treat all files as non-media in directories that contain a  ".nomedia" file
    143     if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
    144         strcpy(fileSpot, ".nomedia");
    145         if (access(path, F_OK) == 0) {
    146             ALOGV("found .nomedia, setting noMedia flag");
    147             noMedia = true;
    148         }
    149 
    150         // restore path
    151         fileSpot[0] = 0;
    152     }
    153 
    154     DIR* dir = opendir(path);
    155     if (!dir) {
    156         ALOGW("Error opening directory '%s', skipping: %s.", path, strerror(errno));
    157         return MEDIA_SCAN_RESULT_SKIPPED;
    158     }
    159 
    160     MediaScanResult result = MEDIA_SCAN_RESULT_OK;
    161     while ((entry = readdir(dir))) {
    162         if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot)
    163                 == MEDIA_SCAN_RESULT_ERROR) {
    164             result = MEDIA_SCAN_RESULT_ERROR;
    165             break;
    166         }
    167     }
    168     closedir(dir);
    169     return result;
    170 }
    171 
    172 MediaScanResult MediaScanner::doProcessDirectoryEntry(
    173         char *path, int pathRemaining, MediaScannerClient &client, bool noMedia,
    174         struct dirent* entry, char* fileSpot) {
    175     struct stat statbuf;
    176     const char* name = entry->d_name;
    177 
    178     // ignore "." and ".."
    179     if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
    180         return MEDIA_SCAN_RESULT_SKIPPED;
    181     }
    182 
    183     int nameLength = strlen(name);
    184     if (nameLength + 1 > pathRemaining) {
    185         // path too long!
    186         return MEDIA_SCAN_RESULT_SKIPPED;
    187     }
    188     strcpy(fileSpot, name);
    189 
    190     int type = entry->d_type;
    191     if (type == DT_UNKNOWN) {
    192         // If the type is unknown, stat() the file instead.
    193         // This is sometimes necessary when accessing NFS mounted filesystems, but
    194         // could be needed in other cases well.
    195         if (stat(path, &statbuf) == 0) {
    196             if (S_ISREG(statbuf.st_mode)) {
    197                 type = DT_REG;
    198             } else if (S_ISDIR(statbuf.st_mode)) {
    199                 type = DT_DIR;
    200             }
    201         } else {
    202             ALOGD("stat() failed for %s: %s", path, strerror(errno) );
    203         }
    204     }
    205     if (type == DT_DIR) {
    206         bool childNoMedia = noMedia;
    207         // set noMedia flag on directories with a name that starts with '.'
    208         // for example, the Mac ".Trashes" directory
    209         if (name[0] == '.')
    210             childNoMedia = true;
    211 
    212         // report the directory to the client
    213         if (stat(path, &statbuf) == 0) {
    214             status_t status = client.scanFile(path, statbuf.st_mtime, 0,
    215                     true /*isDirectory*/, childNoMedia);
    216             if (status) {
    217                 return MEDIA_SCAN_RESULT_ERROR;
    218             }
    219         }
    220 
    221         // and now process its contents
    222         strcat(fileSpot, "/");
    223         MediaScanResult result = doProcessDirectory(path, pathRemaining - nameLength - 1,
    224                 client, childNoMedia);
    225         if (result == MEDIA_SCAN_RESULT_ERROR) {
    226             return MEDIA_SCAN_RESULT_ERROR;
    227         }
    228     } else if (type == DT_REG) {
    229         stat(path, &statbuf);
    230         status_t status = client.scanFile(path, statbuf.st_mtime, statbuf.st_size,
    231                 false /*isDirectory*/, noMedia);
    232         if (status) {
    233             return MEDIA_SCAN_RESULT_ERROR;
    234         }
    235     }
    236 
    237     return MEDIA_SCAN_RESULT_OK;
    238 }
    239 
    240 MediaAlbumArt *MediaAlbumArt::clone() {
    241     size_t byte_size = this->size() + sizeof(MediaAlbumArt);
    242     MediaAlbumArt *result = reinterpret_cast<MediaAlbumArt *>(malloc(byte_size));
    243     result->mSize = this->size();
    244     memcpy(&result->mData[0], &this->mData[0], this->size());
    245     return result;
    246 }
    247 
    248 void MediaAlbumArt::init(MediaAlbumArt *instance, int32_t dataSize, const void *data) {
    249     instance->mSize = dataSize;
    250     memcpy(&instance->mData[0], data, dataSize);
    251 }
    252 
    253 MediaAlbumArt *MediaAlbumArt::fromData(int32_t dataSize, const void* data) {
    254     size_t byte_size = sizeof(MediaAlbumArt) + dataSize;
    255     MediaAlbumArt *result = reinterpret_cast<MediaAlbumArt *>(malloc(byte_size));
    256     init(result, dataSize, data);
    257     return result;
    258 }
    259 
    260 }  // namespace android
    261