MtpServer.cpp revision 7850ef999740f214a1990a9c090d3f3865d435aa
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    // 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}
519
520}  // namespace android
521