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