blob: 57b84ac390cdc5bb0bd864fae1e479ca8c795158 [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 <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
24
25#include "MtpDebug.h"
26#include "MtpServer.h"
27#include "MtpStorage.h"
28#include "MtpStringBuffer.h"
29#include "MtpDatabase.h"
30
31#include "f_mtp.h"
32
33static const MtpOperationCode kSupportedOperationCodes[] = {
34 MTP_OPERATION_GET_DEVICE_INFO,
35 MTP_OPERATION_OPEN_SESSION,
36 MTP_OPERATION_CLOSE_SESSION,
37 MTP_OPERATION_GET_STORAGE_IDS,
38 MTP_OPERATION_GET_STORAGE_INFO,
39 MTP_OPERATION_GET_NUM_OBJECTS,
40 MTP_OPERATION_GET_OBJECT_HANDLES,
41 MTP_OPERATION_GET_OBJECT_INFO,
42 MTP_OPERATION_GET_OBJECT,
43// MTP_OPERATION_GET_THUMB,
44 MTP_OPERATION_DELETE_OBJECT,
45 MTP_OPERATION_SEND_OBJECT_INFO,
46 MTP_OPERATION_SEND_OBJECT,
47// MTP_OPERATION_INITIATE_CAPTURE,
48// MTP_OPERATION_FORMAT_STORE,
49// MTP_OPERATION_RESET_DEVICE,
50// MTP_OPERATION_SELF_TEST,
51// MTP_OPERATION_SET_OBJECT_PROTECTION,
52// MTP_OPERATION_POWER_DOWN,
53 MTP_OPERATION_GET_DEVICE_PROP_DESC,
54 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
55 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
56 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
57// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
58// MTP_OPERATION_MOVE_OBJECT,
59// MTP_OPERATION_COPY_OBJECT,
60// MTP_OPERATION_GET_PARTIAL_OBJECT,
61// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
62 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
63// MTP_OPERATION_GET_OBJECT_PROP_DESC,
64 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
65 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
66// MTP_OPERATION_GET_OBJECT_REFERENCES,
67// MTP_OPERATION_SET_OBJECT_REFERENCES,
68// MTP_OPERATION_SKIP,
69};
70
71static const MtpObjectProperty kSupportedObjectProperties[] = {
72 MTP_PROPERTY_STORAGE_ID,
73 MTP_PROPERTY_OBJECT_FORMAT,
74 MTP_PROPERTY_OBJECT_SIZE,
75 MTP_PROPERTY_OBJECT_FILE_NAME,
76 MTP_PROPERTY_PARENT_OBJECT,
77};
78
79static const MtpObjectFormat kSupportedPlaybackFormats[] = {
80 // FIXME - fill this out later
81 MTP_FORMAT_ASSOCIATION,
82 MTP_FORMAT_MP3,
83};
84
85MtpServer::MtpServer(int fd, const char* databasePath)
86 : mFD(fd),
87 mDatabasePath(databasePath),
88 mDatabase(NULL),
89 mSessionID(0),
90 mSessionOpen(false),
91 mSendObjectHandle(kInvalidObjectHandle),
92 mSendObjectFileSize(0)
93{
94 mDatabase = new MtpDatabase();
95 mDatabase->open(databasePath, true);
96}
97
98MtpServer::~MtpServer() {
99}
100
101void MtpServer::addStorage(const char* filePath) {
102 int index = mStorages.size() + 1;
103 index |= index << 16; // set high and low part to our index
104 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
105 addStorage(storage);
106}
107
108MtpStorage* MtpServer::getStorage(MtpStorageID id) {
109 for (int i = 0; i < mStorages.size(); i++) {
110 MtpStorage* storage = mStorages[i];
111 if (storage->getStorageID() == id)
112 return storage;
113 }
114 return NULL;
115}
116
117void MtpServer::scanStorage() {
118 for (int i = 0; i < mStorages.size(); i++) {
119 MtpStorage* storage = mStorages[i];
120 storage->scanFiles();
121 }
122}
123
124void MtpServer::run() {
125 int fd = mFD;
126
127 printf("MtpServer::run fd: %d\n", fd);
128
129 while (1) {
130 int ret = mRequest.read(fd);
131 if (ret < 0) {
132 fprintf(stderr, "request read returned %d, errno: %d\n", ret, errno);
133 break;
134 }
135 MtpOperationCode operation = mRequest.getOperationCode();
136 MtpTransactionID transaction = mRequest.getTransactionID();
137
138 printf("operation: %s\n", MtpDebug::getOperationCodeName(operation));
139 mRequest.dump();
140
141 // FIXME need to generalize this
142 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
143 if (dataIn) {
144 int ret = mData.read(fd);
145 if (ret < 0) {
146 fprintf(stderr, "data read returned %d, errno: %d\n", ret, errno);
147 break;
148 }
149 printf("received data:\n");
150 mData.dump();
151 } else {
152 mData.reset();
153 }
154
155 handleRequest();
156
157 if (!dataIn && mData.hasData()) {
158 mData.setOperationCode(operation);
159 mData.setTransactionID(transaction);
160 printf("sending data:\n");
161 mData.dump();
162 ret = mData.write(fd);
163 if (ret < 0) {
164 fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno);
165 break;
166 }
167 }
168
169 mResponse.setTransactionID(transaction);
170 ret = mResponse.write(fd);
171 if (ret < 0) {
172 fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno);
173 break;
174 }
175 }
176}
177
178void MtpServer::handleRequest() {
179 MtpOperationCode operation = mRequest.getOperationCode();
180 MtpResponseCode response;
181
182 mResponse.reset();
183
184 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
185 // FIXME - need to delete mSendObjectHandle from the database
186 fprintf(stderr, "expected SendObject after SendObjectInfo\n");
187 mSendObjectHandle = kInvalidObjectHandle;
188 }
189
190 switch (operation) {
191 case MTP_OPERATION_GET_DEVICE_INFO:
192 response = doGetDeviceInfo();
193 break;
194 case MTP_OPERATION_OPEN_SESSION:
195 response = doOpenSession();
196 break;
197 case MTP_OPERATION_CLOSE_SESSION:
198 response = doCloseSession();
199 break;
200 case MTP_OPERATION_GET_STORAGE_IDS:
201 response = doGetStorageIDs();
202 break;
203 case MTP_OPERATION_GET_STORAGE_INFO:
204 response = doGetStorageInfo();
205 break;
206 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
207 response = doGetObjectPropsSupported();
208 break;
209 case MTP_OPERATION_GET_OBJECT_HANDLES:
210 response = doGetObjectHandles();
211 break;
212 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
213 response = doGetObjectPropValue();
214 break;
215 case MTP_OPERATION_GET_OBJECT_INFO:
216 response = doGetObjectInfo();
217 break;
218 case MTP_OPERATION_GET_OBJECT:
219 response = doGetObject();
220 break;
221 case MTP_OPERATION_SEND_OBJECT_INFO:
222 response = doSendObjectInfo();
223 break;
224 case MTP_OPERATION_SEND_OBJECT:
225 response = doSendObject();
226 break;
227 case MTP_OPERATION_DELETE_OBJECT:
228 response = doDeleteObject();
229 break;
230 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
231 default:
232 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
233 break;
234 }
235
236 mResponse.setResponseCode(response);
237}
238
239MtpResponseCode MtpServer::doGetDeviceInfo() {
240 MtpStringBuffer string;
241
242 // fill in device info
243 mData.putUInt16(MTP_STANDARD_VERSION);
244 mData.putUInt32(6); // MTP Vendor Extension ID
245 mData.putUInt16(MTP_STANDARD_VERSION);
246 string.set("microsoft.com: 1.0;");
247 mData.putString(string); // MTP Extensions
248 mData.putUInt16(0); //Functional Mode
249 mData.putAUInt16(kSupportedOperationCodes,
250 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
251 mData.putEmptyArray(); // Events Supported
252 mData.putEmptyArray(); // Device Properties Supported
253 mData.putEmptyArray(); // Capture Formats
254 mData.putAUInt16(kSupportedPlaybackFormats,
255 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
256 // FIXME
257 string.set("Google, Inc.");
258 mData.putString(string); // Manufacturer
259 string.set("Just an Ordinary MTP Device");
260 mData.putString(string); // Model
261 string.set("1.0");
262 mData.putString(string); // Device Version
263 string.set("123456789012345678AA");
264 mData.putString(string); // Serial Number
265
266 return MTP_RESPONSE_OK;
267}
268
269MtpResponseCode MtpServer::doOpenSession() {
270 if (mSessionOpen) {
271 mResponse.setParameter(1, mSessionID);
272 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
273 }
274 mSessionID = mRequest.getParameter(1);
275 mSessionOpen = true;
276 return MTP_RESPONSE_OK;
277}
278
279MtpResponseCode MtpServer::doCloseSession() {
280 if (!mSessionOpen)
281 return MTP_RESPONSE_SESSION_NOT_OPEN;
282 mSessionID = 0;
283 mSessionOpen = false;
284 return MTP_RESPONSE_OK;
285}
286
287MtpResponseCode MtpServer::doGetStorageIDs() {
288 if (!mSessionOpen)
289 return MTP_RESPONSE_SESSION_NOT_OPEN;
290
291 int count = mStorages.size();
292 mData.putUInt32(count);
293 for (int i = 0; i < count; i++)
294 mData.putUInt32(mStorages[i]->getStorageID());
295
296 return MTP_RESPONSE_OK;
297}
298
299MtpResponseCode MtpServer::doGetStorageInfo() {
300 MtpStringBuffer string;
301
302 if (!mSessionOpen)
303 return MTP_RESPONSE_SESSION_NOT_OPEN;
304 MtpStorageID id = mRequest.getParameter(1);
305 MtpStorage* storage = getStorage(id);
306 if (!storage)
307 return MTP_RESPONSE_INVALID_STORAGE_ID;
308
309 mData.putUInt16(storage->getType());
310 mData.putUInt16(storage->getFileSystemType());
311 mData.putUInt16(storage->getAccessCapability());
312 mData.putUInt64(storage->getMaxCapacity());
313 mData.putUInt64(storage->getFreeSpace());
314 mData.putUInt32(1024*1024*1024); // Free Space in Objects
315 string.set(storage->getDescription());
316 mData.putString(string);
317 mData.putEmptyString(); // Volume Identifier
318
319 return MTP_RESPONSE_OK;
320}
321
322MtpResponseCode MtpServer::doGetObjectPropsSupported() {
323 if (!mSessionOpen)
324 return MTP_RESPONSE_SESSION_NOT_OPEN;
325 MtpObjectFormat format = mRequest.getParameter(1);
326 mData.putAUInt16(kSupportedObjectProperties,
327 sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
328 return MTP_RESPONSE_OK;
329}
330
331MtpResponseCode MtpServer::doGetObjectHandles() {
332 if (!mSessionOpen)
333 return MTP_RESPONSE_SESSION_NOT_OPEN;
334 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
335 MtpObjectFormat format = mRequest.getParameter(2); // 0x00000000 for all formats
336 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
337 // 0x00000000 for all objects?
338
339 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
340 mData.putAUInt32(handles);
341 delete handles;
342 return MTP_RESPONSE_OK;
343}
344
345MtpResponseCode MtpServer::doGetObjectPropValue() {
346 MtpObjectHandle handle = mRequest.getParameter(1);
347 MtpObjectProperty property = mRequest.getParameter(2);
348
349 return mDatabase->getObjectProperty(handle, property, mData);
350}
351
352MtpResponseCode MtpServer::doGetObjectInfo() {
353 MtpObjectHandle handle = mRequest.getParameter(1);
354 return mDatabase->getObjectInfo(handle, mData);
355}
356
357MtpResponseCode MtpServer::doGetObject() {
358 MtpObjectHandle handle = mRequest.getParameter(1);
359 MtpString filePath;
360 int64_t fileLength;
361 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
362 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
363
364 mtp_file_range mfr;
365 mfr.path = filePath;
366 mfr.path_length = strlen(mfr.path);
367 mfr.offset = 0;
368 mfr.length = fileLength;
369
370 // send data header
371 mData.setOperationCode(mRequest.getOperationCode());
372 mData.setTransactionID(mRequest.getTransactionID());
373 mData.writeDataHeader(mFD, fileLength);
374
375 // then transfer the file
376 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
377 // FIXME - check for errors here
378 printf("MTP_SEND_FILE returned %d\n", ret);
379 return MTP_RESPONSE_OK;
380}
381
382MtpResponseCode MtpServer::doSendObjectInfo() {
383 MtpString path;
384 MtpStorageID storageID = mRequest.getParameter(1);
385 MtpStorage* storage = getStorage(storageID);
386 MtpObjectHandle parent = mRequest.getParameter(2);
387 if (!storage)
388 return MTP_RESPONSE_INVALID_STORAGE_ID;
389
390 // special case the root
391 if (parent == MTP_PARENT_ROOT)
392 path = storage->getPath();
393 else {
394 int64_t dummy;
395 if (!mDatabase->getObjectFilePath(parent, path, dummy))
396 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
397 }
398
399 // read only the fields we need
400 mData.getUInt32(); // storage ID
401 MtpObjectFormat format = mData.getUInt16();
402 mData.getUInt16(); // protection status
403 mSendObjectFileSize = mData.getUInt32();
404 mData.getUInt16(); // thumb format
405 mData.getUInt32(); // thumb compressed size
406 mData.getUInt32(); // thumb pix width
407 mData.getUInt32(); // thumb pix height
408 mData.getUInt32(); // image pix width
409 mData.getUInt32(); // image pix height
410 mData.getUInt32(); // image bit depth
411 mData.getUInt32(); // parent
412 uint16_t associationType = mData.getUInt16();
413 uint32_t associationDesc = mData.getUInt32(); // association desc
414 mData.getUInt32(); // sequence number
415 MtpStringBuffer name, created, modified;
416 mData.getString(name); // file name
417 mData.getString(created); // date created
418 mData.getString(modified); // date modified
419 // keywords follow
420
421 time_t createdTime, modifiedTime;
422 if (!parseDateTime(created, createdTime))
423 createdTime = 0;
424 if (!parseDateTime(modified, modifiedTime))
425 modifiedTime = 0;
426printf("SendObjectInfo format: %04X size: %d name: %s, created: %s, modified: %s\n",
427format, mSendObjectFileSize, (const char*)name, (const char*)created, (const char*)modified);
428
429 if (path[path.size() - 1] != '/')
430 path += "/";
431 path += (const char *)name;
432
433 MtpObjectHandle handle = mDatabase->addFile((const char*)path,
434 format, parent, storageID, mSendObjectFileSize,
435 createdTime, modifiedTime);
436 if (handle == kInvalidObjectHandle)
437 return MTP_RESPONSE_GENERAL_ERROR;
438
439 if (format == MTP_FORMAT_ASSOCIATION) {
440 mode_t mask = umask(0);
441 int ret = mkdir((const char *)path, S_IRWXU | S_IRWXG | S_IRWXO);
442 umask(mask);
443 if (ret && ret != -EEXIST)
444 return MTP_RESPONSE_GENERAL_ERROR;
445 } else {
446 mSendObjectFilePath = path;
447 // save the handle for the SendObject call, which should follow
448 mSendObjectHandle = handle;
449 }
450
451 mResponse.setParameter(1, storageID);
452 mResponse.setParameter(2, parent);
453 mResponse.setParameter(3, handle);
454
455 return MTP_RESPONSE_OK;
456}
457
458MtpResponseCode MtpServer::doSendObject() {
459 if (mSendObjectHandle == kInvalidObjectHandle) {
460 fprintf(stderr, "Expected SendObjectInfo before SendObject\n");
461 return MTP_RESPONSE_NO_VALID_OBJECT_INFO;
462 }
463
464 // read the header
465 int ret = mData.readDataHeader(mFD);
466 // FIXME - check for errors here.
467
468 // reset so we don't attempt to send this back
469 mData.reset();
470
471 mtp_file_range mfr;
472 mfr.path = (const char*)mSendObjectFilePath;
473 mfr.path_length = strlen(mfr.path);
474 mfr.offset = 0;
475 mfr.length = mSendObjectFileSize;
476
477 // transfer the file
478 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
479 // FIXME - check for errors here.
480 // we need to return a reasonable response and delete
481 // mSendObjectHandle from the database if this fails.
482 printf("MTP_RECEIVE_FILE returned %d\n", ret);
483
484 mSendObjectHandle = kInvalidObjectHandle;
485
486 return MTP_RESPONSE_OK;
487}
488
489MtpResponseCode MtpServer::doDeleteObject() {
490 MtpObjectHandle handle = mRequest.getParameter(1);
491 MtpObjectFormat format = mRequest.getParameter(1);
492 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
493 // FIXME - implement deleting objects by format
494 // FIXME - handle non-empty directories
495
496 MtpString filePath;
497 int64_t fileLength;
498 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
499 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
500
501printf("deleting %s\n", (const char *)filePath);
502 // one of these should work
503 rmdir((const char *)filePath);
504 unlink((const char *)filePath);
505
506 mDatabase->deleteFile(handle);
507
508 return MTP_RESPONSE_OK;
509}
510
511MtpResponseCode MtpServer::doGetObjectPropDesc() {
512 MtpObjectProperty property = mRequest.getParameter(1);
513 MtpObjectFormat format = mRequest.getParameter(2);
514
515 return -1;
516}