blob: a9adf01f51f4b93a255524a505db58c8968810f0 [file] [log] [blame]
Mike Lockwoodfceef462010-05-14 15:35:17 -04001/*
2 * Copyright (C) 2010 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
Mike Lockwoodb14e5882010-06-29 18:11:52 -040017#define LOG_TAG "MtpMediaScanner"
18
19#include "MtpDebug.h"
Mike Lockwoodfceef462010-05-14 15:35:17 -040020#include "MtpDatabase.h"
21#include "MtpMediaScanner.h"
Mike Lockwood335dd2b2010-05-19 10:33:39 -040022#include "mtp.h"
Mike Lockwoodfceef462010-05-14 15:35:17 -040023
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <sys/statfs.h>
27#include <unistd.h>
28#include <dirent.h>
29#include <errno.h>
30#include <string.h>
31#include <stdio.h>
32#include <limits.h>
33
Mike Lockwoodfceef462010-05-14 15:35:17 -040034namespace android {
35
Mike Lockwoodfceef462010-05-14 15:35:17 -040036MtpMediaScanner::MtpMediaScanner(MtpStorageID id, const char* filePath, MtpDatabase* db)
37 : mStorageID(id),
38 mFilePath(filePath),
39 mDatabase(db),
Mike Lockwoodfceef462010-05-14 15:35:17 -040040 mFileList(NULL),
41 mFileCount(0)
42{
Mike Lockwoodfceef462010-05-14 15:35:17 -040043}
44
45MtpMediaScanner::~MtpMediaScanner() {
46}
47
48bool MtpMediaScanner::scanFiles() {
49 mDatabase->beginTransaction();
50 mFileCount = 0;
51 mFileList = mDatabase->getFileList(mFileCount);
52
53 int ret = scanDirectory(mFilePath, MTP_PARENT_ROOT);
54
55 for (int i = 0; i < mFileCount; i++) {
56 MtpObjectHandle test = mFileList[i];
57 if (! (test & kObjectHandleMarkBit)) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -040058 LOGV("delete missing file %08X", test);
Mike Lockwoodfceef462010-05-14 15:35:17 -040059 mDatabase->deleteFile(test);
60 }
61 }
62
63 delete[] mFileList;
64 mFileCount = 0;
65 mDatabase->commitTransaction();
66 return (ret == 0);
67}
68
69
70static const struct MediaFileTypeEntry
71{
72 const char* extension;
73 MtpObjectFormat format;
Mike Lockwoodfceef462010-05-14 15:35:17 -040074} sFileTypes[] =
75{
Mike Lockwooddda7e2b2010-07-03 14:40:05 -040076 { "MP3", MTP_FORMAT_MP3, },
77 { "M4A", MTP_FORMAT_UNDEFINED_AUDIO, },
78 { "WAV", MTP_FORMAT_WAV, },
79 { "AMR", MTP_FORMAT_UNDEFINED_AUDIO, },
80 { "AWB", MTP_FORMAT_UNDEFINED_AUDIO, },
81 { "WMA", MTP_FORMAT_WMA, },
82 { "OGG", MTP_FORMAT_OGG, },
83 { "OGA", MTP_FORMAT_UNDEFINED_AUDIO, },
84 { "AAC", MTP_FORMAT_AAC, },
85 { "MID", MTP_FORMAT_UNDEFINED_AUDIO, },
86 { "MIDI", MTP_FORMAT_UNDEFINED_AUDIO, },
87 { "XMF", MTP_FORMAT_UNDEFINED_AUDIO, },
88 { "RTTTL", MTP_FORMAT_UNDEFINED_AUDIO, },
89 { "SMF", MTP_FORMAT_UNDEFINED_AUDIO, },
90 { "IMY", MTP_FORMAT_UNDEFINED_AUDIO, },
91 { "RTX", MTP_FORMAT_UNDEFINED_AUDIO, },
92 { "OTA", MTP_FORMAT_UNDEFINED_AUDIO, },
93 { "MPEG", MTP_FORMAT_UNDEFINED_VIDEO, },
94 { "MP4", MTP_FORMAT_UNDEFINED_VIDEO, },
95 { "M4V", MTP_FORMAT_UNDEFINED_VIDEO, },
96 { "3GP", MTP_FORMAT_UNDEFINED_VIDEO, },
97 { "3GPP", MTP_FORMAT_UNDEFINED_VIDEO, },
98 { "3G2", MTP_FORMAT_UNDEFINED_VIDEO, },
99 { "3GPP2", MTP_FORMAT_UNDEFINED_VIDEO, },
100 { "WMV", MTP_FORMAT_UNDEFINED_VIDEO, },
101 { "ASF", MTP_FORMAT_UNDEFINED_VIDEO, },
102 { "JPG", MTP_FORMAT_EXIF_JPEG, },
103 { "JPEG", MTP_FORMAT_EXIF_JPEG, },
104 { "GIF", MTP_FORMAT_GIF, },
105 { "PNG", MTP_FORMAT_PNG, },
106 { "BMP", MTP_FORMAT_BMP, },
107 { "WBMP", MTP_FORMAT_BMP, },
108 { "M3U", MTP_FORMAT_M3U_PLAYLIST, },
109 { "PLS", MTP_FORMAT_PLS_PLAYLIST, },
110 { "WPL", MTP_FORMAT_WPL_PLAYLIST, },
Mike Lockwoodfceef462010-05-14 15:35:17 -0400111};
112
Mike Lockwooddda7e2b2010-07-03 14:40:05 -0400113MtpObjectFormat MtpMediaScanner::getFileFormat(const char* path)
Mike Lockwoodfceef462010-05-14 15:35:17 -0400114{
115 const char* extension = strrchr(path, '.');
116 if (!extension)
117 return MTP_FORMAT_UNDEFINED;
118 extension++; // skip the dot
119
120 for (unsigned i = 0; i < sizeof(sFileTypes) / sizeof(sFileTypes[0]); i++) {
121 if (!strcasecmp(extension, sFileTypes[i].extension)) {
Mike Lockwoodfceef462010-05-14 15:35:17 -0400122 return sFileTypes[i].format;
123 }
124 }
Mike Lockwoodfceef462010-05-14 15:35:17 -0400125 return MTP_FORMAT_UNDEFINED;
126}
127
128int MtpMediaScanner::scanDirectory(const char* path, MtpObjectHandle parent)
129{
130 char buffer[PATH_MAX];
131 struct dirent* entry;
132
133 unsigned length = strlen(path);
134 if (length > sizeof(buffer) + 2) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400135 LOGE("path too long: %s", path);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400136 }
137
138 DIR* dir = opendir(path);
139 if (!dir) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400140 LOGE("opendir %s failed, errno: %d", path, errno);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400141 return -1;
142 }
143
144 strncpy(buffer, path, sizeof(buffer));
145 char* fileStart = buffer + length;
146 // make sure we have a trailing slash
147 if (fileStart[-1] != '/') {
148 *(fileStart++) = '/';
149 }
150 int fileNameLength = sizeof(buffer) + fileStart - buffer;
151
152 while ((entry = readdir(dir))) {
153 const char* name = entry->d_name;
154
155 // ignore "." and "..", as well as any files or directories staring with dot
156 if (name[0] == '.') {
157 continue;
158 }
159 if (strlen(name) + 1 > fileNameLength) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400160 LOGE("path too long for %s", name);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400161 continue;
162 }
163 strcpy(fileStart, name);
164
165 struct stat statbuf;
166 memset(&statbuf, 0, sizeof(statbuf));
167 stat(buffer, &statbuf);
168
Mike Lockwood6084a292010-06-14 23:00:30 -0700169 if (S_ISDIR(statbuf.st_mode)) {
Mike Lockwoodfceef462010-05-14 15:35:17 -0400170 MtpObjectHandle handle = mDatabase->getObjectHandle(buffer);
171 if (handle) {
172 markFile(handle);
173 } else {
174 handle = mDatabase->addFile(buffer, MTP_FORMAT_ASSOCIATION,
175 parent, mStorageID, 0, statbuf.st_mtime);
176 }
177 scanDirectory(buffer, handle);
Mike Lockwood6084a292010-06-14 23:00:30 -0700178 } else if (S_ISREG(statbuf.st_mode)) {
Mike Lockwoodfceef462010-05-14 15:35:17 -0400179 scanFile(buffer, parent, statbuf);
180 }
181 }
182
183 closedir(dir);
184 return 0;
185}
186
187void MtpMediaScanner::scanFile(const char* path, MtpObjectHandle parent, struct stat& statbuf) {
Mike Lockwooddda7e2b2010-07-03 14:40:05 -0400188 MtpObjectFormat format = getFileFormat(path);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400189 // don't scan unknown file types
190 if (format == MTP_FORMAT_UNDEFINED)
191 return;
192 MtpObjectHandle handle = mDatabase->getObjectHandle(path);
193 // fixme - rescan if mod date changed
194 if (handle) {
195 markFile(handle);
196 } else {
197 mDatabase->beginTransaction();
198 handle = mDatabase->addFile(path, format, parent, mStorageID,
199 statbuf.st_size, statbuf.st_mtime);
200 if (handle <= 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400201 LOGE("addFile failed in MtpMediaScanner::scanFile()");
Mike Lockwoodfceef462010-05-14 15:35:17 -0400202 mDatabase->rollbackTransaction();
203 return;
204 }
Mike Lockwoodfceef462010-05-14 15:35:17 -0400205 mDatabase->commitTransaction();
206 }
207}
208
209void MtpMediaScanner::markFile(MtpObjectHandle handle) {
210 if (mFileList) {
211 handle &= kObjectHandleIndexMask;
212 // binary search for the file in mFileList
213 int low = 0;
214 int high = mFileCount;
215 int index;
216
217 while (low < high) {
218 index = (low + high) >> 1;
219 MtpObjectHandle test = (mFileList[index] & kObjectHandleIndexMask);
220 if (handle < test)
221 high = index; // item is less than index
222 else if (handle > test)
223 low = index + 1; // item is greater than index
224 else {
225 mFileList[index] |= kObjectHandleMarkBit;
226 return;
227 }
228 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400229 LOGE("file %d not found in mFileList", handle);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400230 }
231}
232
233} // namespace android