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#include <sys/stat.h>
25#include <dirent.h>
26
27#include <cutils/properties.h>
28
29#define LOG_TAG "MtpServer"
30
31#include "MtpDebug.h"
32#include "MtpDatabase.h"
33#include "MtpObjectInfo.h"
34#include "MtpProperty.h"
35#include "MtpServer.h"
36#include "MtpStorage.h"
37#include "MtpStringBuffer.h"
38
39#include <linux/usb/f_mtp.h>
40
41namespace android {
42
43static const MtpOperationCode kSupportedOperationCodes[] = {
44    MTP_OPERATION_GET_DEVICE_INFO,
45    MTP_OPERATION_OPEN_SESSION,
46    MTP_OPERATION_CLOSE_SESSION,
47    MTP_OPERATION_GET_STORAGE_IDS,
48    MTP_OPERATION_GET_STORAGE_INFO,
49    MTP_OPERATION_GET_NUM_OBJECTS,
50    MTP_OPERATION_GET_OBJECT_HANDLES,
51    MTP_OPERATION_GET_OBJECT_INFO,
52    MTP_OPERATION_GET_OBJECT,
53    MTP_OPERATION_GET_THUMB,
54    MTP_OPERATION_DELETE_OBJECT,
55    MTP_OPERATION_SEND_OBJECT_INFO,
56    MTP_OPERATION_SEND_OBJECT,
57//    MTP_OPERATION_INITIATE_CAPTURE,
58//    MTP_OPERATION_FORMAT_STORE,
59//    MTP_OPERATION_RESET_DEVICE,
60//    MTP_OPERATION_SELF_TEST,
61//    MTP_OPERATION_SET_OBJECT_PROTECTION,
62//    MTP_OPERATION_POWER_DOWN,
63    MTP_OPERATION_GET_DEVICE_PROP_DESC,
64    MTP_OPERATION_GET_DEVICE_PROP_VALUE,
65    MTP_OPERATION_SET_DEVICE_PROP_VALUE,
66    MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
67//    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
68//    MTP_OPERATION_MOVE_OBJECT,
69//    MTP_OPERATION_COPY_OBJECT,
70    MTP_OPERATION_GET_PARTIAL_OBJECT,
71//    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
72    MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
73    MTP_OPERATION_GET_OBJECT_PROP_DESC,
74    MTP_OPERATION_GET_OBJECT_PROP_VALUE,
75    MTP_OPERATION_SET_OBJECT_PROP_VALUE,
76    MTP_OPERATION_GET_OBJECT_PROP_LIST,
77//    MTP_OPERATION_SET_OBJECT_PROP_LIST,
78//    MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
79//    MTP_OPERATION_SEND_OBJECT_PROP_LIST,
80    MTP_OPERATION_GET_OBJECT_REFERENCES,
81    MTP_OPERATION_SET_OBJECT_REFERENCES,
82//    MTP_OPERATION_SKIP,
83    // Android extension for direct file IO
84    MTP_OPERATION_GET_PARTIAL_OBJECT_64,
85    MTP_OPERATION_SEND_PARTIAL_OBJECT,
86    MTP_OPERATION_TRUNCATE_OBJECT,
87    MTP_OPERATION_BEGIN_EDIT_OBJECT,
88    MTP_OPERATION_END_EDIT_OBJECT,
89};
90
91static const MtpEventCode kSupportedEventCodes[] = {
92    MTP_EVENT_OBJECT_ADDED,
93    MTP_EVENT_OBJECT_REMOVED,
94    MTP_EVENT_STORE_ADDED,
95    MTP_EVENT_STORE_REMOVED,
96};
97
98MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
99                    int fileGroup, int filePerm, int directoryPerm)
100    :   mFD(fd),
101        mDatabase(database),
102        mPtp(ptp),
103        mFileGroup(fileGroup),
104        mFilePermission(filePerm),
105        mDirectoryPermission(directoryPerm),
106        mSessionID(0),
107        mSessionOpen(false),
108        mSendObjectHandle(kInvalidObjectHandle),
109        mSendObjectFormat(0),
110        mSendObjectFileSize(0)
111{
112}
113
114MtpServer::~MtpServer() {
115}
116
117void MtpServer::addStorage(MtpStorage* storage) {
118    Mutex::Autolock autoLock(mMutex);
119
120    mStorages.push(storage);
121    sendStoreAdded(storage->getStorageID());
122}
123
124void MtpServer::removeStorage(MtpStorage* storage) {
125    Mutex::Autolock autoLock(mMutex);
126
127    for (int i = 0; i < mStorages.size(); i++) {
128        if (mStorages[i] == storage) {
129            mStorages.removeAt(i);
130            sendStoreRemoved(storage->getStorageID());
131            break;
132        }
133    }
134}
135
136MtpStorage* MtpServer::getStorage(MtpStorageID id) {
137    if (id == 0)
138        return mStorages[0];
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
147bool MtpServer::hasStorage(MtpStorageID id) {
148    if (id == 0 || id == 0xFFFFFFFF)
149        return mStorages.size() > 0;
150    return (getStorage(id) != NULL);
151}
152
153void MtpServer::run() {
154    int fd = mFD;
155
156    ALOGV("MtpServer::run fd: %d\n", fd);
157
158    while (1) {
159        int ret = mRequest.read(fd);
160        if (ret < 0) {
161            ALOGV("request read returned %d, errno: %d", ret, errno);
162            if (errno == ECANCELED) {
163                // return to top of loop and wait for next command
164                continue;
165            }
166            break;
167        }
168        MtpOperationCode operation = mRequest.getOperationCode();
169        MtpTransactionID transaction = mRequest.getTransactionID();
170
171        ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
172        mRequest.dump();
173
174        // FIXME need to generalize this
175        bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
176                    || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
177                    || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
178                    || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
179        if (dataIn) {
180            int ret = mData.read(fd);
181            if (ret < 0) {
182                ALOGE("data read returned %d, errno: %d", ret, errno);
183                if (errno == ECANCELED) {
184                    // return to top of loop and wait for next command
185                    continue;
186                }
187                break;
188            }
189            ALOGV("received data:");
190            mData.dump();
191        } else {
192            mData.reset();
193        }
194
195        if (handleRequest()) {
196            if (!dataIn && mData.hasData()) {
197                mData.setOperationCode(operation);
198                mData.setTransactionID(transaction);
199                ALOGV("sending data:");
200                mData.dump();
201                ret = mData.write(fd);
202                if (ret < 0) {
203                    ALOGE("request write returned %d, errno: %d", ret, errno);
204                    if (errno == ECANCELED) {
205                        // return to top of loop and wait for next command
206                        continue;
207                    }
208                    break;
209                }
210            }
211
212            mResponse.setTransactionID(transaction);
213            ALOGV("sending response %04X", mResponse.getResponseCode());
214            ret = mResponse.write(fd);
215            mResponse.dump();
216            if (ret < 0) {
217                ALOGE("request write returned %d, errno: %d", ret, errno);
218                if (errno == ECANCELED) {
219                    // return to top of loop and wait for next command
220                    continue;
221                }
222                break;
223            }
224        } else {
225            ALOGV("skipping response\n");
226        }
227    }
228
229    // commit any open edits
230    int count = mObjectEditList.size();
231    for (int i = 0; i < count; i++) {
232        ObjectEdit* edit = mObjectEditList[i];
233        commitEdit(edit);
234        delete edit;
235    }
236    mObjectEditList.clear();
237
238    if (mSessionOpen)
239        mDatabase->sessionEnded();
240    close(fd);
241    mFD = -1;
242}
243
244void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
245    ALOGV("sendObjectAdded %d\n", handle);
246    sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
247}
248
249void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
250    ALOGV("sendObjectRemoved %d\n", handle);
251    sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
252}
253
254void MtpServer::sendStoreAdded(MtpStorageID id) {
255    ALOGV("sendStoreAdded %08X\n", id);
256    sendEvent(MTP_EVENT_STORE_ADDED, id);
257}
258
259void MtpServer::sendStoreRemoved(MtpStorageID id) {
260    ALOGV("sendStoreRemoved %08X\n", id);
261    sendEvent(MTP_EVENT_STORE_REMOVED, id);
262}
263
264void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
265    if (mSessionOpen) {
266        mEvent.setEventCode(code);
267        mEvent.setTransactionID(mRequest.getTransactionID());
268        mEvent.setParameter(1, param1);
269        int ret = mEvent.write(mFD);
270        ALOGV("mEvent.write returned %d\n", ret);
271    }
272}
273
274void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
275        uint64_t size, MtpObjectFormat format, int fd) {
276    ObjectEdit*  edit = new ObjectEdit(handle, path, size, format, fd);
277    mObjectEditList.add(edit);
278}
279
280MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
281    int count = mObjectEditList.size();
282    for (int i = 0; i < count; i++) {
283        ObjectEdit* edit = mObjectEditList[i];
284        if (edit->mHandle == handle) return edit;
285    }
286    return NULL;
287}
288
289void MtpServer::removeEditObject(MtpObjectHandle handle) {
290    int count = mObjectEditList.size();
291    for (int i = 0; i < count; i++) {
292        ObjectEdit* edit = mObjectEditList[i];
293        if (edit->mHandle == handle) {
294            delete edit;
295            mObjectEditList.removeAt(i);
296            return;
297        }
298    }
299    ALOGE("ObjectEdit not found in removeEditObject");
300}
301
302void MtpServer::commitEdit(ObjectEdit* edit) {
303    mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
304}
305
306
307bool MtpServer::handleRequest() {
308    Mutex::Autolock autoLock(mMutex);
309
310    MtpOperationCode operation = mRequest.getOperationCode();
311    MtpResponseCode response;
312
313    mResponse.reset();
314
315    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
316        // FIXME - need to delete mSendObjectHandle from the database
317        ALOGE("expected SendObject after SendObjectInfo");
318        mSendObjectHandle = kInvalidObjectHandle;
319    }
320
321    switch (operation) {
322        case MTP_OPERATION_GET_DEVICE_INFO:
323            response = doGetDeviceInfo();
324            break;
325        case MTP_OPERATION_OPEN_SESSION:
326            response = doOpenSession();
327            break;
328        case MTP_OPERATION_CLOSE_SESSION:
329            response = doCloseSession();
330            break;
331        case MTP_OPERATION_GET_STORAGE_IDS:
332            response = doGetStorageIDs();
333            break;
334         case MTP_OPERATION_GET_STORAGE_INFO:
335            response = doGetStorageInfo();
336            break;
337        case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
338            response = doGetObjectPropsSupported();
339            break;
340        case MTP_OPERATION_GET_OBJECT_HANDLES:
341            response = doGetObjectHandles();
342            break;
343        case MTP_OPERATION_GET_NUM_OBJECTS:
344            response = doGetNumObjects();
345            break;
346        case MTP_OPERATION_GET_OBJECT_REFERENCES:
347            response = doGetObjectReferences();
348            break;
349        case MTP_OPERATION_SET_OBJECT_REFERENCES:
350            response = doSetObjectReferences();
351            break;
352        case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
353            response = doGetObjectPropValue();
354            break;
355        case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
356            response = doSetObjectPropValue();
357            break;
358        case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
359            response = doGetDevicePropValue();
360            break;
361        case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
362            response = doSetDevicePropValue();
363            break;
364        case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
365            response = doResetDevicePropValue();
366            break;
367        case MTP_OPERATION_GET_OBJECT_PROP_LIST:
368            response = doGetObjectPropList();
369            break;
370        case MTP_OPERATION_GET_OBJECT_INFO:
371            response = doGetObjectInfo();
372            break;
373        case MTP_OPERATION_GET_OBJECT:
374            response = doGetObject();
375            break;
376        case MTP_OPERATION_GET_THUMB:
377            response = doGetThumb();
378            break;
379        case MTP_OPERATION_GET_PARTIAL_OBJECT:
380        case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
381            response = doGetPartialObject(operation);
382            break;
383        case MTP_OPERATION_SEND_OBJECT_INFO:
384            response = doSendObjectInfo();
385            break;
386        case MTP_OPERATION_SEND_OBJECT:
387            response = doSendObject();
388            break;
389        case MTP_OPERATION_DELETE_OBJECT:
390            response = doDeleteObject();
391            break;
392        case MTP_OPERATION_GET_OBJECT_PROP_DESC:
393            response = doGetObjectPropDesc();
394            break;
395        case MTP_OPERATION_GET_DEVICE_PROP_DESC:
396            response = doGetDevicePropDesc();
397            break;
398        case MTP_OPERATION_SEND_PARTIAL_OBJECT:
399            response = doSendPartialObject();
400            break;
401        case MTP_OPERATION_TRUNCATE_OBJECT:
402            response = doTruncateObject();
403            break;
404        case MTP_OPERATION_BEGIN_EDIT_OBJECT:
405            response = doBeginEditObject();
406            break;
407        case MTP_OPERATION_END_EDIT_OBJECT:
408            response = doEndEditObject();
409            break;
410        default:
411            ALOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
412            response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
413            break;
414    }
415
416    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
417        return false;
418    mResponse.setResponseCode(response);
419    return true;
420}
421
422MtpResponseCode MtpServer::doGetDeviceInfo() {
423    MtpStringBuffer   string;
424    char prop_value[PROPERTY_VALUE_MAX];
425
426    MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
427    MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
428    MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
429
430    // fill in device info
431    mData.putUInt16(MTP_STANDARD_VERSION);
432    if (mPtp) {
433        mData.putUInt32(0);
434    } else {
435        // MTP Vendor Extension ID
436        mData.putUInt32(6);
437    }
438    mData.putUInt16(MTP_STANDARD_VERSION);
439    if (mPtp) {
440        // no extensions
441        string.set("");
442    } else {
443        // MTP extensions
444        string.set("microsoft.com: 1.0; android.com: 1.0;");
445    }
446    mData.putString(string); // MTP Extensions
447    mData.putUInt16(0); //Functional Mode
448    mData.putAUInt16(kSupportedOperationCodes,
449            sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
450    mData.putAUInt16(kSupportedEventCodes,
451            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
452    mData.putAUInt16(deviceProperties); // Device Properties Supported
453    mData.putAUInt16(captureFormats); // Capture Formats
454    mData.putAUInt16(playbackFormats);  // Playback Formats
455
456    property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
457    string.set(prop_value);
458    mData.putString(string);   // Manufacturer
459
460    property_get("ro.product.model", prop_value, "MTP Device");
461    string.set(prop_value);
462    mData.putString(string);   // Model
463    string.set("1.0");
464    mData.putString(string);   // Device Version
465
466    property_get("ro.serialno", prop_value, "????????");
467    string.set(prop_value);
468    mData.putString(string);   // Serial Number
469
470    delete playbackFormats;
471    delete captureFormats;
472    delete deviceProperties;
473
474    return MTP_RESPONSE_OK;
475}
476
477MtpResponseCode MtpServer::doOpenSession() {
478    if (mSessionOpen) {
479        mResponse.setParameter(1, mSessionID);
480        return MTP_RESPONSE_SESSION_ALREADY_OPEN;
481    }
482    mSessionID = mRequest.getParameter(1);
483    mSessionOpen = true;
484
485    mDatabase->sessionStarted();
486
487    return MTP_RESPONSE_OK;
488}
489
490MtpResponseCode MtpServer::doCloseSession() {
491    if (!mSessionOpen)
492        return MTP_RESPONSE_SESSION_NOT_OPEN;
493    mSessionID = 0;
494    mSessionOpen = false;
495    mDatabase->sessionEnded();
496    return MTP_RESPONSE_OK;
497}
498
499MtpResponseCode MtpServer::doGetStorageIDs() {
500    if (!mSessionOpen)
501        return MTP_RESPONSE_SESSION_NOT_OPEN;
502
503    int count = mStorages.size();
504    mData.putUInt32(count);
505    for (int i = 0; i < count; i++)
506        mData.putUInt32(mStorages[i]->getStorageID());
507
508    return MTP_RESPONSE_OK;
509}
510
511MtpResponseCode MtpServer::doGetStorageInfo() {
512    MtpStringBuffer   string;
513
514    if (!mSessionOpen)
515        return MTP_RESPONSE_SESSION_NOT_OPEN;
516    MtpStorageID id = mRequest.getParameter(1);
517    MtpStorage* storage = getStorage(id);
518    if (!storage)
519        return MTP_RESPONSE_INVALID_STORAGE_ID;
520
521    mData.putUInt16(storage->getType());
522    mData.putUInt16(storage->getFileSystemType());
523    mData.putUInt16(storage->getAccessCapability());
524    mData.putUInt64(storage->getMaxCapacity());
525    mData.putUInt64(storage->getFreeSpace());
526    mData.putUInt32(1024*1024*1024); // Free Space in Objects
527    string.set(storage->getDescription());
528    mData.putString(string);
529    mData.putEmptyString();   // Volume Identifier
530
531    return MTP_RESPONSE_OK;
532}
533
534MtpResponseCode MtpServer::doGetObjectPropsSupported() {
535    if (!mSessionOpen)
536        return MTP_RESPONSE_SESSION_NOT_OPEN;
537    MtpObjectFormat format = mRequest.getParameter(1);
538    MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
539    mData.putAUInt16(properties);
540    delete properties;
541    return MTP_RESPONSE_OK;
542}
543
544MtpResponseCode MtpServer::doGetObjectHandles() {
545    if (!mSessionOpen)
546        return MTP_RESPONSE_SESSION_NOT_OPEN;
547    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
548    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
549    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
550                                                            // 0x00000000 for all objects
551
552    if (!hasStorage(storageID))
553        return MTP_RESPONSE_INVALID_STORAGE_ID;
554
555    MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
556    mData.putAUInt32(handles);
557    delete handles;
558    return MTP_RESPONSE_OK;
559}
560
561MtpResponseCode MtpServer::doGetNumObjects() {
562    if (!mSessionOpen)
563        return MTP_RESPONSE_SESSION_NOT_OPEN;
564    MtpStorageID storageID = mRequest.getParameter(1);      // 0xFFFFFFFF for all storage
565    MtpObjectFormat format = mRequest.getParameter(2);      // 0 for all formats
566    MtpObjectHandle parent = mRequest.getParameter(3);      // 0xFFFFFFFF for objects with no parent
567                                                            // 0x00000000 for all objects
568    if (!hasStorage(storageID))
569        return MTP_RESPONSE_INVALID_STORAGE_ID;
570
571    int count = mDatabase->getNumObjects(storageID, format, parent);
572    if (count >= 0) {
573        mResponse.setParameter(1, count);
574        return MTP_RESPONSE_OK;
575    } else {
576        mResponse.setParameter(1, 0);
577        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
578    }
579}
580
581MtpResponseCode MtpServer::doGetObjectReferences() {
582    if (!mSessionOpen)
583        return MTP_RESPONSE_SESSION_NOT_OPEN;
584    if (!hasStorage())
585        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
586    MtpObjectHandle handle = mRequest.getParameter(1);
587
588    // FIXME - check for invalid object handle
589    MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
590    if (handles) {
591        mData.putAUInt32(handles);
592        delete handles;
593    } else {
594        mData.putEmptyArray();
595    }
596    return MTP_RESPONSE_OK;
597}
598
599MtpResponseCode MtpServer::doSetObjectReferences() {
600    if (!mSessionOpen)
601        return MTP_RESPONSE_SESSION_NOT_OPEN;
602    if (!hasStorage())
603        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
604    MtpStorageID handle = mRequest.getParameter(1);
605
606    MtpObjectHandleList* references = mData.getAUInt32();
607    MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
608    delete references;
609    return result;
610}
611
612MtpResponseCode MtpServer::doGetObjectPropValue() {
613    if (!hasStorage())
614        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
615    MtpObjectHandle handle = mRequest.getParameter(1);
616    MtpObjectProperty property = mRequest.getParameter(2);
617    ALOGV("GetObjectPropValue %d %s\n", handle,
618            MtpDebug::getObjectPropCodeName(property));
619
620    return mDatabase->getObjectPropertyValue(handle, property, mData);
621}
622
623MtpResponseCode MtpServer::doSetObjectPropValue() {
624    if (!hasStorage())
625        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
626    MtpObjectHandle handle = mRequest.getParameter(1);
627    MtpObjectProperty property = mRequest.getParameter(2);
628    ALOGV("SetObjectPropValue %d %s\n", handle,
629            MtpDebug::getObjectPropCodeName(property));
630
631    return mDatabase->setObjectPropertyValue(handle, property, mData);
632}
633
634MtpResponseCode MtpServer::doGetDevicePropValue() {
635    MtpDeviceProperty property = mRequest.getParameter(1);
636    ALOGV("GetDevicePropValue %s\n",
637            MtpDebug::getDevicePropCodeName(property));
638
639    return mDatabase->getDevicePropertyValue(property, mData);
640}
641
642MtpResponseCode MtpServer::doSetDevicePropValue() {
643    MtpDeviceProperty property = mRequest.getParameter(1);
644    ALOGV("SetDevicePropValue %s\n",
645            MtpDebug::getDevicePropCodeName(property));
646
647    return mDatabase->setDevicePropertyValue(property, mData);
648}
649
650MtpResponseCode MtpServer::doResetDevicePropValue() {
651    MtpDeviceProperty property = mRequest.getParameter(1);
652    ALOGV("ResetDevicePropValue %s\n",
653            MtpDebug::getDevicePropCodeName(property));
654
655    return mDatabase->resetDeviceProperty(property);
656}
657
658MtpResponseCode MtpServer::doGetObjectPropList() {
659    if (!hasStorage())
660        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
661
662    MtpObjectHandle handle = mRequest.getParameter(1);
663    // use uint32_t so we can support 0xFFFFFFFF
664    uint32_t format = mRequest.getParameter(2);
665    uint32_t property = mRequest.getParameter(3);
666    int groupCode = mRequest.getParameter(4);
667    int depth = mRequest.getParameter(5);
668   ALOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
669            handle, MtpDebug::getFormatCodeName(format),
670            MtpDebug::getObjectPropCodeName(property), groupCode, depth);
671
672    return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
673}
674
675MtpResponseCode MtpServer::doGetObjectInfo() {
676    if (!hasStorage())
677        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
678    MtpObjectHandle handle = mRequest.getParameter(1);
679    MtpObjectInfo info(handle);
680    MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
681    if (result == MTP_RESPONSE_OK) {
682        char    date[20];
683
684        mData.putUInt32(info.mStorageID);
685        mData.putUInt16(info.mFormat);
686        mData.putUInt16(info.mProtectionStatus);
687
688        // if object is being edited the database size may be out of date
689        uint32_t size = info.mCompressedSize;
690        ObjectEdit* edit = getEditObject(handle);
691        if (edit)
692            size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
693        mData.putUInt32(size);
694
695        mData.putUInt16(info.mThumbFormat);
696        mData.putUInt32(info.mThumbCompressedSize);
697        mData.putUInt32(info.mThumbPixWidth);
698        mData.putUInt32(info.mThumbPixHeight);
699        mData.putUInt32(info.mImagePixWidth);
700        mData.putUInt32(info.mImagePixHeight);
701        mData.putUInt32(info.mImagePixDepth);
702        mData.putUInt32(info.mParent);
703        mData.putUInt16(info.mAssociationType);
704        mData.putUInt32(info.mAssociationDesc);
705        mData.putUInt32(info.mSequenceNumber);
706        mData.putString(info.mName);
707        mData.putEmptyString();    // date created
708        formatDateTime(info.mDateModified, date, sizeof(date));
709        mData.putString(date);   // date modified
710        mData.putEmptyString();   // keywords
711    }
712    return result;
713}
714
715MtpResponseCode MtpServer::doGetObject() {
716    if (!hasStorage())
717        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
718    MtpObjectHandle handle = mRequest.getParameter(1);
719    MtpString pathBuf;
720    int64_t fileLength;
721    MtpObjectFormat format;
722    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
723    if (result != MTP_RESPONSE_OK)
724        return result;
725
726    const char* filePath = (const char *)pathBuf;
727    mtp_file_range  mfr;
728    mfr.fd = open(filePath, O_RDONLY);
729    if (mfr.fd < 0) {
730        return MTP_RESPONSE_GENERAL_ERROR;
731    }
732    mfr.offset = 0;
733    mfr.length = fileLength;
734    mfr.command = mRequest.getOperationCode();
735    mfr.transaction_id = mRequest.getTransactionID();
736
737    // then transfer the file
738    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
739    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
740    close(mfr.fd);
741    if (ret < 0) {
742        if (errno == ECANCELED)
743            return MTP_RESPONSE_TRANSACTION_CANCELLED;
744        else
745            return MTP_RESPONSE_GENERAL_ERROR;
746    }
747    return MTP_RESPONSE_OK;
748}
749
750MtpResponseCode MtpServer::doGetThumb() {
751    MtpObjectHandle handle = mRequest.getParameter(1);
752    size_t thumbSize;
753    void* thumb = mDatabase->getThumbnail(handle, thumbSize);
754    if (thumb) {
755        // send data
756        mData.setOperationCode(mRequest.getOperationCode());
757        mData.setTransactionID(mRequest.getTransactionID());
758        mData.writeData(mFD, thumb, thumbSize);
759        free(thumb);
760        return MTP_RESPONSE_OK;
761    } else {
762        return MTP_RESPONSE_GENERAL_ERROR;
763    }
764}
765
766MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
767    if (!hasStorage())
768        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
769    MtpObjectHandle handle = mRequest.getParameter(1);
770    uint64_t offset;
771    uint32_t length;
772    offset = mRequest.getParameter(2);
773    if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
774        // android extension with 64 bit offset
775        uint64_t offset2 = mRequest.getParameter(3);
776        offset = offset | (offset2 << 32);
777        length = mRequest.getParameter(4);
778    } else {
779        // standard GetPartialObject
780        length = mRequest.getParameter(3);
781    }
782    MtpString pathBuf;
783    int64_t fileLength;
784    MtpObjectFormat format;
785    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
786    if (result != MTP_RESPONSE_OK)
787        return result;
788    if (offset + length > fileLength)
789        length = fileLength - offset;
790
791    const char* filePath = (const char *)pathBuf;
792    mtp_file_range  mfr;
793    mfr.fd = open(filePath, O_RDONLY);
794    if (mfr.fd < 0) {
795        return MTP_RESPONSE_GENERAL_ERROR;
796    }
797    mfr.offset = offset;
798    mfr.length = length;
799    mfr.command = mRequest.getOperationCode();
800    mfr.transaction_id = mRequest.getTransactionID();
801    mResponse.setParameter(1, length);
802
803    // transfer the file
804    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
805    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
806    close(mfr.fd);
807    if (ret < 0) {
808        if (errno == ECANCELED)
809            return MTP_RESPONSE_TRANSACTION_CANCELLED;
810        else
811            return MTP_RESPONSE_GENERAL_ERROR;
812    }
813    return MTP_RESPONSE_OK;
814}
815
816MtpResponseCode MtpServer::doSendObjectInfo() {
817    MtpString path;
818    MtpStorageID storageID = mRequest.getParameter(1);
819    MtpStorage* storage = getStorage(storageID);
820    MtpObjectHandle parent = mRequest.getParameter(2);
821    if (!storage)
822        return MTP_RESPONSE_INVALID_STORAGE_ID;
823
824    // special case the root
825    if (parent == MTP_PARENT_ROOT) {
826        path = storage->getPath();
827        parent = 0;
828    } else {
829        int64_t length;
830        MtpObjectFormat format;
831        int result = mDatabase->getObjectFilePath(parent, path, length, format);
832        if (result != MTP_RESPONSE_OK)
833            return result;
834        if (format != MTP_FORMAT_ASSOCIATION)
835            return MTP_RESPONSE_INVALID_PARENT_OBJECT;
836    }
837
838    // read only the fields we need
839    mData.getUInt32();  // storage ID
840    MtpObjectFormat format = mData.getUInt16();
841    mData.getUInt16();  // protection status
842    mSendObjectFileSize = mData.getUInt32();
843    mData.getUInt16();  // thumb format
844    mData.getUInt32();  // thumb compressed size
845    mData.getUInt32();  // thumb pix width
846    mData.getUInt32();  // thumb pix height
847    mData.getUInt32();  // image pix width
848    mData.getUInt32();  // image pix height
849    mData.getUInt32();  // image bit depth
850    mData.getUInt32();  // parent
851    uint16_t associationType = mData.getUInt16();
852    uint32_t associationDesc = mData.getUInt32();   // association desc
853    mData.getUInt32();  // sequence number
854    MtpStringBuffer name, created, modified;
855    mData.getString(name);    // file name
856    mData.getString(created);      // date created
857    mData.getString(modified);     // date modified
858    // keywords follow
859
860    ALOGV("name: %s format: %04X\n", (const char *)name, format);
861    time_t modifiedTime;
862    if (!parseDateTime(modified, modifiedTime))
863        modifiedTime = 0;
864
865    if (path[path.size() - 1] != '/')
866        path += "/";
867    path += (const char *)name;
868
869    // check space first
870    if (mSendObjectFileSize > storage->getFreeSpace())
871        return MTP_RESPONSE_STORAGE_FULL;
872    uint64_t maxFileSize = storage->getMaxFileSize();
873    // check storage max file size
874    if (maxFileSize != 0) {
875        // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
876        // is >= 0xFFFFFFFF
877        if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
878            return MTP_RESPONSE_OBJECT_TOO_LARGE;
879    }
880
881    ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
882    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
883            format, parent, storageID, mSendObjectFileSize, modifiedTime);
884    if (handle == kInvalidObjectHandle) {
885        return MTP_RESPONSE_GENERAL_ERROR;
886    }
887
888  if (format == MTP_FORMAT_ASSOCIATION) {
889        mode_t mask = umask(0);
890        int ret = mkdir((const char *)path, mDirectoryPermission);
891        umask(mask);
892        if (ret && ret != -EEXIST)
893            return MTP_RESPONSE_GENERAL_ERROR;
894        chown((const char *)path, getuid(), mFileGroup);
895
896        // SendObject does not get sent for directories, so call endSendObject here instead
897        mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
898    } else {
899        mSendObjectFilePath = path;
900        // save the handle for the SendObject call, which should follow
901        mSendObjectHandle = handle;
902        mSendObjectFormat = format;
903    }
904
905    mResponse.setParameter(1, storageID);
906    mResponse.setParameter(2, parent);
907    mResponse.setParameter(3, handle);
908
909    return MTP_RESPONSE_OK;
910}
911
912MtpResponseCode MtpServer::doSendObject() {
913    if (!hasStorage())
914        return MTP_RESPONSE_GENERAL_ERROR;
915    MtpResponseCode result = MTP_RESPONSE_OK;
916    mode_t mask;
917    int ret, initialData;
918
919    if (mSendObjectHandle == kInvalidObjectHandle) {
920        ALOGE("Expected SendObjectInfo before SendObject");
921        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
922        goto done;
923    }
924
925    // read the header, and possibly some data
926    ret = mData.read(mFD);
927    if (ret < MTP_CONTAINER_HEADER_SIZE) {
928        result = MTP_RESPONSE_GENERAL_ERROR;
929        goto done;
930    }
931    initialData = ret - MTP_CONTAINER_HEADER_SIZE;
932
933    mtp_file_range  mfr;
934    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
935    if (mfr.fd < 0) {
936        result = MTP_RESPONSE_GENERAL_ERROR;
937        goto done;
938    }
939    fchown(mfr.fd, getuid(), mFileGroup);
940    // set permissions
941    mask = umask(0);
942    fchmod(mfr.fd, mFilePermission);
943    umask(mask);
944
945    if (initialData > 0)
946        ret = write(mfr.fd, mData.getData(), initialData);
947
948    if (mSendObjectFileSize - initialData > 0) {
949        mfr.offset = initialData;
950        if (mSendObjectFileSize == 0xFFFFFFFF) {
951            // tell driver to read until it receives a short packet
952            mfr.length = 0xFFFFFFFF;
953        } else {
954            mfr.length = mSendObjectFileSize - initialData;
955        }
956
957        ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
958        // transfer the file
959        ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
960        ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
961    }
962    close(mfr.fd);
963
964    if (ret < 0) {
965        unlink(mSendObjectFilePath);
966        if (errno == ECANCELED)
967            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
968        else
969            result = MTP_RESPONSE_GENERAL_ERROR;
970    }
971
972done:
973    // reset so we don't attempt to send the data back
974    mData.reset();
975
976    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
977            result == MTP_RESPONSE_OK);
978    mSendObjectHandle = kInvalidObjectHandle;
979    mSendObjectFormat = 0;
980    return result;
981}
982
983static void deleteRecursive(const char* path) {
984    char pathbuf[PATH_MAX];
985    int pathLength = strlen(path);
986    if (pathLength >= sizeof(pathbuf) - 1) {
987        ALOGE("path too long: %s\n", path);
988    }
989    strcpy(pathbuf, path);
990    if (pathbuf[pathLength - 1] != '/') {
991        pathbuf[pathLength++] = '/';
992    }
993    char* fileSpot = pathbuf + pathLength;
994    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
995
996    DIR* dir = opendir(path);
997    if (!dir) {
998        ALOGE("opendir %s failed: %s", path, strerror(errno));
999        return;
1000    }
1001
1002    struct dirent* entry;
1003    while ((entry = readdir(dir))) {
1004        const char* name = entry->d_name;
1005
1006        // ignore "." and ".."
1007        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1008            continue;
1009        }
1010
1011        int nameLength = strlen(name);
1012        if (nameLength > pathRemaining) {
1013            ALOGE("path %s/%s too long\n", path, name);
1014            continue;
1015        }
1016        strcpy(fileSpot, name);
1017
1018        int type = entry->d_type;
1019        if (entry->d_type == DT_DIR) {
1020            deleteRecursive(pathbuf);
1021            rmdir(pathbuf);
1022        } else {
1023            unlink(pathbuf);
1024        }
1025    }
1026    closedir(dir);
1027}
1028
1029static void deletePath(const char* path) {
1030    struct stat statbuf;
1031    if (stat(path, &statbuf) == 0) {
1032        if (S_ISDIR(statbuf.st_mode)) {
1033            deleteRecursive(path);
1034            rmdir(path);
1035        } else {
1036            unlink(path);
1037        }
1038    } else {
1039        ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
1040    }
1041}
1042
1043MtpResponseCode MtpServer::doDeleteObject() {
1044    if (!hasStorage())
1045        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1046    MtpObjectHandle handle = mRequest.getParameter(1);
1047    MtpObjectFormat format = mRequest.getParameter(2);
1048    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1049    // FIXME - implement deleting objects by format
1050
1051    MtpString filePath;
1052    int64_t fileLength;
1053    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
1054    if (result == MTP_RESPONSE_OK) {
1055        ALOGV("deleting %s", (const char *)filePath);
1056        result = mDatabase->deleteFile(handle);
1057        // Don't delete the actual files unless the database deletion is allowed
1058        if (result == MTP_RESPONSE_OK) {
1059            deletePath((const char *)filePath);
1060        }
1061    }
1062
1063    return result;
1064}
1065
1066MtpResponseCode MtpServer::doGetObjectPropDesc() {
1067    MtpObjectProperty propCode = mRequest.getParameter(1);
1068    MtpObjectFormat format = mRequest.getParameter(2);
1069    ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
1070                                        MtpDebug::getFormatCodeName(format));
1071    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
1072    if (!property)
1073        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
1074    property->write(mData);
1075    delete property;
1076    return MTP_RESPONSE_OK;
1077}
1078
1079MtpResponseCode MtpServer::doGetDevicePropDesc() {
1080    MtpDeviceProperty propCode = mRequest.getParameter(1);
1081    ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
1082    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1083    if (!property)
1084        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1085    property->write(mData);
1086    delete property;
1087    return MTP_RESPONSE_OK;
1088}
1089
1090MtpResponseCode MtpServer::doSendPartialObject() {
1091    if (!hasStorage())
1092        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1093    MtpObjectHandle handle = mRequest.getParameter(1);
1094    uint64_t offset = mRequest.getParameter(2);
1095    uint64_t offset2 = mRequest.getParameter(3);
1096    offset = offset | (offset2 << 32);
1097    uint32_t length = mRequest.getParameter(4);
1098
1099    ObjectEdit* edit = getEditObject(handle);
1100    if (!edit) {
1101        ALOGE("object not open for edit in doSendPartialObject");
1102        return MTP_RESPONSE_GENERAL_ERROR;
1103    }
1104
1105    // can't start writing past the end of the file
1106    if (offset > edit->mSize) {
1107        ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
1108        return MTP_RESPONSE_GENERAL_ERROR;
1109    }
1110
1111    const char* filePath = (const char *)edit->mPath;
1112    ALOGV("receiving partial %s %lld %lld\n", filePath, offset, length);
1113
1114    // read the header, and possibly some data
1115    int ret = mData.read(mFD);
1116    if (ret < MTP_CONTAINER_HEADER_SIZE)
1117        return MTP_RESPONSE_GENERAL_ERROR;
1118    int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1119
1120    if (initialData > 0) {
1121        ret = write(edit->mFD, mData.getData(), initialData);
1122        offset += initialData;
1123        length -= initialData;
1124    }
1125
1126    if (length > 0) {
1127        mtp_file_range  mfr;
1128        mfr.fd = edit->mFD;
1129        mfr.offset = offset;
1130        mfr.length = length;
1131
1132        // transfer the file
1133        ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1134        ALOGV("MTP_RECEIVE_FILE returned %d", ret);
1135    }
1136    if (ret < 0) {
1137        mResponse.setParameter(1, 0);
1138        if (errno == ECANCELED)
1139            return MTP_RESPONSE_TRANSACTION_CANCELLED;
1140        else
1141            return MTP_RESPONSE_GENERAL_ERROR;
1142    }
1143
1144    // reset so we don't attempt to send this back
1145    mData.reset();
1146    mResponse.setParameter(1, length);
1147    uint64_t end = offset + length;
1148    if (end > edit->mSize) {
1149        edit->mSize = end;
1150    }
1151    return MTP_RESPONSE_OK;
1152}
1153
1154MtpResponseCode MtpServer::doTruncateObject() {
1155    MtpObjectHandle handle = mRequest.getParameter(1);
1156    ObjectEdit* edit = getEditObject(handle);
1157    if (!edit) {
1158        ALOGE("object not open for edit in doTruncateObject");
1159        return MTP_RESPONSE_GENERAL_ERROR;
1160    }
1161
1162    uint64_t offset = mRequest.getParameter(2);
1163    uint64_t offset2 = mRequest.getParameter(3);
1164    offset |= (offset2 << 32);
1165    if (ftruncate(edit->mFD, offset) != 0) {
1166        return MTP_RESPONSE_GENERAL_ERROR;
1167    } else {
1168        edit->mSize = offset;
1169        return MTP_RESPONSE_OK;
1170    }
1171}
1172
1173MtpResponseCode MtpServer::doBeginEditObject() {
1174    MtpObjectHandle handle = mRequest.getParameter(1);
1175    if (getEditObject(handle)) {
1176        ALOGE("object already open for edit in doBeginEditObject");
1177        return MTP_RESPONSE_GENERAL_ERROR;
1178    }
1179
1180    MtpString path;
1181    int64_t fileLength;
1182    MtpObjectFormat format;
1183    int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1184    if (result != MTP_RESPONSE_OK)
1185        return result;
1186
1187    int fd = open((const char *)path, O_RDWR | O_EXCL);
1188    if (fd < 0) {
1189        ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1190        return MTP_RESPONSE_GENERAL_ERROR;
1191    }
1192
1193    addEditObject(handle, path, fileLength, format, fd);
1194    return MTP_RESPONSE_OK;
1195}
1196
1197MtpResponseCode MtpServer::doEndEditObject() {
1198    MtpObjectHandle handle = mRequest.getParameter(1);
1199    ObjectEdit* edit = getEditObject(handle);
1200    if (!edit) {
1201        ALOGE("object not open for edit in doEndEditObject");
1202        return MTP_RESPONSE_GENERAL_ERROR;
1203    }
1204
1205    commitEdit(edit);
1206    removeEditObject(handle);
1207    return MTP_RESPONSE_OK;
1208}
1209
1210}  // namespace android
1211