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