blob: 8f6c75d98c52712814b63ddb431ac2c0c43a7c02 [file] [log] [blame]
Mike Lockwood16864ba2010-05-11 17:16:59 -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
17#include "MtpDatabase.h"
18#include "MtpDataPacket.h"
19#include "SqliteDatabase.h"
20#include "SqliteStatement.h"
21
22#include <stdio.h>
23#include <sqlite3.h>
24
Mike Lockwood7850ef92010-05-14 10:10:36 -040025namespace android {
26
Mike Lockwood16864ba2010-05-11 17:16:59 -040027#define ID_COLUMN 1
28#define PATH_COLUMN 2
29#define FORMAT_COLUMN 3
30#define PARENT_COLUMN 4
31#define STORAGE_COLUMN 5
32#define SIZE_COLUMN 6
33#define CREATED_COLUMN 7
34#define MODIFIED_COLUMN 8
35
36#define TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \
37 "_id INTEGER PRIMARY KEY," \
38 "path TEXT," \
39 "format INTEGER," \
40 "parent INTEGER," \
41 "storage INTEGER," \
42 "size INTEGER," \
43 "date_created INTEGER," \
44 "date_modified INTEGER" \
45 ");"
46
47#define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);"
48
49#define FILE_ID_QUERY "SELECT _id FROM files WHERE path = ?;"
50#define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?"
51
52#define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_created,date_modified FROM files WHERE _id = ?;"
53#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?,?);"
54#define FILE_DELETE "DELETE FROM files WHERE path = ?;"
55
Mike Lockwood16864ba2010-05-11 17:16:59 -040056struct PropertyTableEntry {
57 MtpObjectProperty property;
58 int type;
59 const char* columnName;
60};
61
62static const PropertyTableEntry kPropertyTable[] = {
63 { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" },
64 { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" },
65 { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" },
66 { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" },
67 { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" },
68 { MTP_PROPERTY_DATE_CREATED, MTP_TYPE_STR, "date_created" },
69 { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" },
70};
71
72static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) {
73 int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
74 const PropertyTableEntry* entry = kPropertyTable;
75 for (int i = 0; i < count; i++, entry++) {
76 if (entry->property == property) {
77 type = entry->type;
78 columnName = entry->columnName;
79 return true;
80 }
81 }
82 return false;
83}
84
85
86MtpDatabase::MtpDatabase()
87 : mFileIdQuery(NULL),
88 mObjectInfoQuery(NULL),
89 mFileInserter(NULL),
90 mFileDeleter(NULL)
91{
92}
93
94MtpDatabase::~MtpDatabase() {
95}
96
97bool MtpDatabase::open(const char* path, bool create) {
98 if (!SqliteDatabase::open(path, create))
99 return false;
100
101 // create the table if necessary
102 if (!exec(TABLE_CREATE)) {
103 fprintf(stderr, "could not create table\n");
104 return false;
105 }
106 if (!exec(PATH_INDEX_CREATE)) {
107 fprintf(stderr, "could not path index\n");
108 return false;
109 }
110 return true;
111}
112
113MtpObjectHandle MtpDatabase::addFile(const char* path,
114 MtpObjectFormat format,
115 MtpObjectHandle parent,
116 MtpStorageID storage,
117 uint64_t size,
118 time_t created,
119 time_t modified) {
120
121 // first check to see if the file exists
122 if (mFileIdQuery)
123 mFileIdQuery->reset();
124 else {
125 mFileIdQuery = new SqliteStatement(this);
126 if (!mFileIdQuery->prepare(FILE_ID_QUERY)) {
127 fprintf(stderr, "could not compile FILE_ID_QUERY\n");
128 delete mFileIdQuery;
129 mFileIdQuery = NULL;
130 return kInvalidObjectHandle;
131 }
132 }
133
134 mFileIdQuery->bind(1, path);
135 if (mFileIdQuery->step()) {
136 int row = mFileIdQuery->getColumnInt(0);
137 if (row > 0)
138 return row;
139 }
140
141 if (!mFileInserter) {
142 mFileInserter = new SqliteStatement(this);
143 if (!mFileInserter->prepare(FILE_INSERT)) {
144 fprintf(stderr, "could not compile FILE_INSERT\n");
145 delete mFileInserter;
146 mFileInserter = NULL;
147 return kInvalidObjectHandle;
148 }
149 }
150 mFileInserter->bind(PATH_COLUMN, path);
151 mFileInserter->bind(FORMAT_COLUMN, format);
152 mFileInserter->bind(PARENT_COLUMN, parent);
153 mFileInserter->bind(STORAGE_COLUMN, storage);
154 mFileInserter->bind(SIZE_COLUMN, size);
155 mFileInserter->bind(CREATED_COLUMN, created);
156 mFileInserter->bind(MODIFIED_COLUMN, modified);
157 mFileInserter->step();
158 mFileInserter->reset();
159 int row = lastInsertedRow();
160 return (row > 0 ? row : kInvalidObjectHandle);
161}
162
163MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
164 MtpObjectFormat format,
165 MtpObjectHandle parent) {
166 bool whereStorage = (storageID != 0xFFFFFFFF);
167 bool whereFormat = (format != 0);
168 bool whereParent = (parent != 0);
169 char intBuffer[20];
170
171 MtpString query("SELECT _id FROM files");
172 if (whereStorage || whereFormat || whereParent)
173 query += " WHERE";
174 if (whereStorage) {
175 snprintf(intBuffer, sizeof(intBuffer), "%d", storageID);
176 query += " storage = ";
177 query += intBuffer;
178 }
179 if (whereFormat) {
180 snprintf(intBuffer, sizeof(intBuffer), "%d", format);
181 if (whereStorage)
182 query += " AND";
183 query += " format = ";
184 query += intBuffer;
185 }
186 if (whereParent) {
187 snprintf(intBuffer, sizeof(intBuffer), "%d", parent);
188 if (whereStorage || whereFormat)
189 query += " AND";
190 query += " parent = ";
191 query += intBuffer;
192 }
193 query += ";";
194
195 SqliteStatement stmt(this);
196 printf("%s\n", (const char *)query);
197 stmt.prepare(query);
198
199 MtpObjectHandleList* list = new MtpObjectHandleList();
200 while (!stmt.isDone()) {
201 if (stmt.step()) {
202 int index = stmt.getColumnInt(0);
203 printf("stmt.getColumnInt returned %d\n", index);
204 if (index > 0)
205 list->push(index);
206 }
207 }
208 printf("list size: %d\n", list->size());
209 return list;
210}
211
212MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle,
213 MtpObjectProperty property,
214 MtpDataPacket& packet) {
215 int type;
216 const char* columnName;
217 char intBuffer[20];
218
219 if (!getPropertyInfo(property, type, columnName))
220 return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
221 snprintf(intBuffer, sizeof(intBuffer), "%d", handle);
222
223 MtpString query("SELECT ");
224 query += columnName;
225 query += " FROM files WHERE _id = ";
226 query += intBuffer;
227 query += ";";
228
229 SqliteStatement stmt(this);
230 printf("%s\n", (const char *)query);
231 stmt.prepare(query);
232
233 if (!stmt.step())
234 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
235
236 switch (type) {
237 case MTP_TYPE_INT8:
238 packet.putInt8(stmt.getColumnInt(0));
239 break;
240 case MTP_TYPE_UINT8:
241 packet.putUInt8(stmt.getColumnInt(0));
242 break;
243 case MTP_TYPE_INT16:
244 packet.putInt16(stmt.getColumnInt(0));
245 break;
246 case MTP_TYPE_UINT16:
247 packet.putUInt16(stmt.getColumnInt(0));
248 break;
249 case MTP_TYPE_INT32:
250 packet.putInt32(stmt.getColumnInt(0));
251 break;
252 case MTP_TYPE_UINT32:
253 packet.putUInt32(stmt.getColumnInt(0));
254 break;
255 case MTP_TYPE_INT64:
256 packet.putInt64(stmt.getColumnInt64(0));
257 break;
258 case MTP_TYPE_UINT64:
259 packet.putUInt64(stmt.getColumnInt64(0));
260 break;
261 case MTP_TYPE_STR:
262 packet.putString(stmt.getColumnString(0));
263 break;
264 default:
265 fprintf(stderr, "unsupported object type\n");
266 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
267 }
268 return MTP_RESPONSE_OK;
269}
270
271MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
272 MtpDataPacket& packet) {
273 char date[20];
274
275 if (mObjectInfoQuery)
276 mObjectInfoQuery->reset();
277 else {
278 mObjectInfoQuery = new SqliteStatement(this);
279 if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) {
280 fprintf(stderr, "could not compile FILE_ID_QUERY\n");
281 delete mObjectInfoQuery;
282 mObjectInfoQuery = NULL;
283 return MTP_RESPONSE_GENERAL_ERROR;
284 }
285 }
286
287 mObjectInfoQuery->bind(1, handle);
288 if (!mObjectInfoQuery->step())
289 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
290
291 MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0);
292 MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1);
293 MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2);
294 // extract name from path. do we want a separate database entry for this?
295 const char* name = mObjectInfoQuery->getColumnString(3);
296 const char* lastSlash = strrchr(name, '/');
297 if (lastSlash)
298 name = lastSlash + 1;
299 int64_t size = mObjectInfoQuery->getColumnInt64(4);
300 time_t created = mObjectInfoQuery->getColumnInt(5);
301 time_t modified = mObjectInfoQuery->getColumnInt(6);
302 int associationType = (format == MTP_FORMAT_ASSOCIATION ?
303 MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
304 MTP_ASSOCIATION_TYPE_UNDEFINED);
305
306 printf("storageID: %d, format: %d, parent: %d\n", storageID, format, parent);
307
308 packet.putUInt32(storageID);
309 packet.putUInt16(format);
310 packet.putUInt16(0); // protection status
311 packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
312 packet.putUInt16(0); // thumb format
313 packet.putUInt32(0); // thumb compressed size
314 packet.putUInt32(0); // thumb pix width
315 packet.putUInt32(0); // thumb pix height
316 packet.putUInt32(0); // image pix width
317 packet.putUInt32(0); // image pix height
318 packet.putUInt32(0); // image bit depth
319 packet.putUInt32(parent);
320 packet.putUInt16(associationType);
321 packet.putUInt32(0); // association desc
322 packet.putUInt32(0); // sequence number
323 packet.putString(name); // file name
324 formatDateTime(created, date, sizeof(date));
325 packet.putString(date); // date created
326 formatDateTime(modified, date, sizeof(date));
327 packet.putString(date); // date modified
328 packet.putEmptyString(); // keywords
329
330 return MTP_RESPONSE_OK;
331}
332
333bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
334 MtpString& filePath,
335 int64_t& fileLength) {
336 if (mFilePathQuery)
337 mFilePathQuery->reset();
338 else {
339 mFilePathQuery = new SqliteStatement(this);
340 if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) {
341 fprintf(stderr, "could not compile FILE_ID_QUERY\n");
342 delete mFilePathQuery;
343 mFilePathQuery = NULL;
344 return kInvalidObjectHandle;
345 }
346 }
347
348 mFilePathQuery->bind(1, handle);
349 if (!mFilePathQuery->step())
350 return false;
351
352 const char* path = mFilePathQuery->getColumnString(0);
353 if (!path)
354 return false;
355 filePath = path;
356 fileLength = mFilePathQuery->getColumnInt64(1);
357 return true;
358}
359
360bool MtpDatabase::deleteFile(MtpObjectHandle handle) {
361 if (!mFileDeleter) {
362 mFileDeleter = new SqliteStatement(this);
363 if (!mFileDeleter->prepare(FILE_DELETE)) {
364 fprintf(stderr, "could not compile FILE_DELETE\n");
365 delete mFileDeleter;
366 mFileDeleter = NULL;
367 return false;
368 }
369 }
370printf("deleteFile %d\n", handle);
371 mFileDeleter->bind(1, handle);
372 mFileDeleter->step();
373 mFileDeleter->reset();
374 return true;
375}
376
377/*
378 for getObjectPropDesc
379
380 packet.putUInt16(property);
381 packet.putUInt16(dataType);
382 packet.putUInt8(getSet);
383 // default value DTS
384 packet.putUInt32(groupCode);
385 packet.putUInt8(formFlag);
386 // form, variable
387*/
Mike Lockwood7850ef92010-05-14 10:10:36 -0400388
389} // namespace android