blob: cda185f932b07d72956c91eff530e32200f0f69d [file] [log] [blame]
Andreas Huberbfb9fb12009-12-03 11:31:19 -08001/*
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 Huberfb41d592010-06-23 11:31:17 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "MediaScanner"
Guang Zhu973f5532011-09-07 23:55:27 -070019#include <cutils/properties.h>
Andreas Huberfb41d592010-06-23 11:31:17 -070020#include <utils/Log.h>
21
Andreas Huberbfb9fb12009-12-03 11:31:19 -080022#include <media/mediascanner.h>
23
24#include <sys/stat.h>
25#include <dirent.h>
26
27namespace android {
28
29MediaScanner::MediaScanner()
Guang Zhu973f5532011-09-07 23:55:27 -070030 : mLocale(NULL), mSkipList(NULL), mSkipIndex(NULL) {
31 loadSkipList();
Andreas Huberbfb9fb12009-12-03 11:31:19 -080032}
33
34MediaScanner::~MediaScanner() {
35 setLocale(NULL);
Guang Zhu973f5532011-09-07 23:55:27 -070036 free(mSkipList);
37 free(mSkipIndex);
Andreas Huberbfb9fb12009-12-03 11:31:19 -080038}
39
40void 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
50const char *MediaScanner::locale() const {
51 return mLocale;
52}
53
Guang Zhu973f5532011-09-07 23:55:27 -070054void 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
Jeff Brown2c70d4a2011-07-20 16:38:43 -070081MediaScanResult MediaScanner::processDirectory(
82 const char *path, MediaScannerClient &client) {
Andreas Huberbfb9fb12009-12-03 11:31:19 -080083 int pathLength = strlen(path);
84 if (pathLength >= PATH_MAX) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -070085 return MEDIA_SCAN_RESULT_SKIPPED;
Andreas Huberbfb9fb12009-12-03 11:31:19 -080086 }
87 char* pathBuffer = (char *)malloc(PATH_MAX + 1);
88 if (!pathBuffer) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -070089 return MEDIA_SCAN_RESULT_ERROR;
Andreas Huberbfb9fb12009-12-03 11:31:19 -080090 }
91
92 int pathRemaining = PATH_MAX - pathLength;
93 strcpy(pathBuffer, path);
Kenny Roota8c02d72010-03-15 21:17:08 -070094 if (pathLength > 0 && pathBuffer[pathLength - 1] != '/') {
Andreas Huberbfb9fb12009-12-03 11:31:19 -080095 pathBuffer[pathLength] = '/';
96 pathBuffer[pathLength + 1] = 0;
97 --pathRemaining;
98 }
99
100 client.setLocale(locale());
101
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700102 MediaScanResult result = doProcessDirectory(pathBuffer, pathRemaining, client, false);
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800103
104 free(pathBuffer);
105
106 return result;
107}
108
Guang Zhu973f5532011-09-07 23:55:27 -0700109bool 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 Brown2c70d4a2011-07-20 16:38:43 -0700131MediaScanResult MediaScanner::doProcessDirectory(
132 char *path, int pathRemaining, MediaScannerClient &client, bool noMedia) {
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800133 // place to copy file or directory name
134 char* fileSpot = path + strlen(path);
135 struct dirent* entry;
136
Guang Zhu973f5532011-09-07 23:55:27 -0700137 if (shouldSkipDirectory(path)) {
Steve Block5baa3a62011-12-20 16:23:08 +0000138 ALOGD("Skipping: %s", path);
Guang Zhu973f5532011-09-07 23:55:27 -0700139 return MEDIA_SCAN_RESULT_OK;
140 }
141
Mike Lockwood9c112a82011-04-24 11:15:09 -0700142 // Treat all files as non-media in directories that contain a ".nomedia" file
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800143 if (pathRemaining >= 8 /* strlen(".nomedia") */ ) {
144 strcpy(fileSpot, ".nomedia");
145 if (access(path, F_OK) == 0) {
Steve Block71f2cf12011-10-20 11:56:00 +0100146 ALOGV("found .nomedia, setting noMedia flag\n");
Mike Lockwood9c112a82011-04-24 11:15:09 -0700147 noMedia = true;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800148 }
149
150 // restore path
151 fileSpot[0] = 0;
152 }
153
154 DIR* dir = opendir(path);
155 if (!dir) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700156 LOGW("Error opening directory '%s', skipping: %s.", path, strerror(errno));
157 return MEDIA_SCAN_RESULT_SKIPPED;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800158 }
159
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700160 MediaScanResult result = MEDIA_SCAN_RESULT_OK;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800161 while ((entry = readdir(dir))) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700162 if (doProcessDirectoryEntry(path, pathRemaining, client, noMedia, entry, fileSpot)
163 == MEDIA_SCAN_RESULT_ERROR) {
164 result = MEDIA_SCAN_RESULT_ERROR;
165 break;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800166 }
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700167 }
168 closedir(dir);
169 return result;
170}
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800171
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700172MediaScanResult 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 Block5baa3a62011-12-20 16:23:08 +0000202 ALOGD("stat() failed for %s: %s", path, strerror(errno) );
Mike Lockwoodf30218b2010-06-14 22:57:22 -0700203 }
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700204 }
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 Lockwoodf30218b2010-06-14 22:57:22 -0700211
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700212 // 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 Huberbfb9fb12009-12-03 11:31:19 -0800218 }
219 }
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800220
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700221 // 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 Huberbfb9fb12009-12-03 11:31:19 -0800234 }
235 }
236
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700237 return MEDIA_SCAN_RESULT_OK;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800238}
239
240} // namespace android