Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 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 | |
Andreas Huber | 1629399 | 2010-06-23 11:31:17 -0700 | [diff] [blame] | 17 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "MediaScanner" |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 19 | #include <cutils/properties.h> |
Andreas Huber | 1629399 | 2010-06-23 11:31:17 -0700 | [diff] [blame] | 20 | #include <utils/Log.h> |
| 21 | |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 22 | #include <media/mediascanner.h> |
| 23 | |
| 24 | #include <sys/stat.h> |
| 25 | #include <dirent.h> |
| 26 | |
| 27 | namespace android { |
| 28 | |
| 29 | MediaScanner::MediaScanner() |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 30 | : mLocale(NULL), mSkipList(NULL), mSkipIndex(NULL) { |
| 31 | loadSkipList(); |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 32 | } |
| 33 | |
| 34 | MediaScanner::~MediaScanner() { |
| 35 | setLocale(NULL); |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 36 | free(mSkipList); |
| 37 | free(mSkipIndex); |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 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 | |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 54 | void MediaScanner::loadSkipList() { |
| 55 | mSkipList = (char *)malloc(PROPERTY_VALUE_MAX * sizeof(char)); |
| 56 | if (mSkipList) { |
Glenn Kasten | e53b9ea | 2012-03-12 16:29:55 -0700 | [diff] [blame] | 57 | property_get("testing.mediascanner.skiplist", mSkipList, ""); |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 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 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 81 | MediaScanResult MediaScanner::processDirectory( |
| 82 | const char *path, MediaScannerClient &client) { |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 83 | int pathLength = strlen(path); |
| 84 | if (pathLength >= PATH_MAX) { |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 85 | return MEDIA_SCAN_RESULT_SKIPPED; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 86 | } |
| 87 | char* pathBuffer = (char *)malloc(PATH_MAX + 1); |
| 88 | if (!pathBuffer) { |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 89 | return MEDIA_SCAN_RESULT_ERROR; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | int pathRemaining = PATH_MAX - pathLength; |
| 93 | strcpy(pathBuffer, path); |
Kenny Root | 3e42b44 | 2010-03-15 21:17:08 -0700 | [diff] [blame] | 94 | if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') { |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 95 | pathBuffer[pathLength] = '/'; |
| 96 | pathBuffer[pathLength + 1] = 0; |
| 97 | --pathRemaining; |
| 98 | } |
| 99 | |
| 100 | client.setLocale(locale()); |
| 101 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 102 | MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false); |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 103 | |
| 104 | free(pathBuffer); |
| 105 | |
| 106 | return result; |
| 107 | } |
| 108 | |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 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 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 131 | MediaScanResult MediaScanner::doProcessDirectory( |
| 132 | char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) { |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 133 | // place to copy file or directory name |
| 134 | char* fileSpot = path + strlen(path); |
| 135 | struct dirent* entry; |
| 136 | |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 137 | if (shouldSkipDirectory(path)) { |
Glenn Kasten | e53b9ea | 2012-03-12 16:29:55 -0700 | [diff] [blame] | 138 | ALOGD("Skipping: %s", path); |
| 139 | return MEDIA_SCAN_RESULT_OK; |
Guang Zhu | fb6f034 | 2011-09-07 23:55:27 -0700 | [diff] [blame] | 140 | } |
| 141 | |
Mike Lockwood | 462acca | 2011-04-24 11:15:09 -0700 | [diff] [blame] | 142 | // Treat all files as non-media in directories that contain a ".nomedia" file |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 143 | if (pathRemaining >= 8 /* strlen(".nomedia") */ ) { |
| 144 | strcpy(fileSpot, ".nomedia"); |
| 145 | if (access(path, F_OK) == 0) { |
Glenn Kasten | 90bebef | 2012-01-27 15:24:38 -0800 | [diff] [blame] | 146 | ALOGV("found .nomedia, setting noMedia flag"); |
Mike Lockwood | 462acca | 2011-04-24 11:15:09 -0700 | [diff] [blame] | 147 | noMedia = true; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | // restore path |
| 151 | fileSpot[0] = 0; |
| 152 | } |
| 153 | |
| 154 | DIR* dir = opendir(path); |
| 155 | if (!dir) { |
Steve Block | 5ff1dd5 | 2012-01-05 23:22:43 +0000 | [diff] [blame] | 156 | ALOGW("Error opening directory '%s', skipping: %s.", path, strerror(errno)); |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 157 | return MEDIA_SCAN_RESULT_SKIPPED; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 158 | } |
| 159 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 160 | MediaScanResult result = MEDIA_SCAN_RESULT_OK; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 161 | while ((entry = readdir(dir))) { |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 162 | if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot) |
| 163 | == MEDIA_SCAN_RESULT_ERROR) { |
| 164 | result = MEDIA_SCAN_RESULT_ERROR; |
| 165 | break; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 166 | } |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 167 | } |
| 168 | closedir(dir); |
| 169 | return result; |
| 170 | } |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 171 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 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 { |
Steve Block | b8a8052 | 2011-12-20 16:23:08 +0000 | [diff] [blame] | 202 | ALOGD("stat() failed for %s: %s", path, strerror(errno) ); |
Mike Lockwood | ef04e8f | 2010-06-14 22:57:22 -0700 | [diff] [blame] | 203 | } |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 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; |
Mike Lockwood | ef04e8f | 2010-06-14 22:57:22 -0700 | [diff] [blame] | 211 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 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; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 218 | } |
| 219 | } |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 220 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 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; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 234 | } |
| 235 | } |
| 236 | |
Jeff Brown | 7188e55 | 2011-07-20 16:38:43 -0700 | [diff] [blame] | 237 | return MEDIA_SCAN_RESULT_OK; |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 238 | } |
| 239 | |
Elliott Hughes | f3e80dd | 2014-06-10 16:55:38 -0700 | [diff] [blame] | 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 | |
Andreas Huber | 413f523 | 2009-12-03 11:31:19 -0800 | [diff] [blame] | 260 | } // namespace android |