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