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