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        formatDateTime(info.mDateCreated, date, sizeof(date));
708        mData.putString(date);   // date created
709        formatDateTime(info.mDateModified, date, sizeof(date));
710        mData.putString(date);   // date modified
711        mData.putEmptyString();   // keywords
712    }
713    return result;
714}
715
716MtpResponseCode MtpServer::doGetObject() {
717    if (!hasStorage())
718        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
719    MtpObjectHandle handle = mRequest.getParameter(1);
720    MtpString pathBuf;
721    int64_t fileLength;
722    MtpObjectFormat format;
723    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
724    if (result != MTP_RESPONSE_OK)
725        return result;
726
727    const char* filePath = (const char *)pathBuf;
728    mtp_file_range  mfr;
729    mfr.fd = open(filePath, O_RDONLY);
730    if (mfr.fd < 0) {
731        return MTP_RESPONSE_GENERAL_ERROR;
732    }
733    mfr.offset = 0;
734    mfr.length = fileLength;
735    mfr.command = mRequest.getOperationCode();
736    mfr.transaction_id = mRequest.getTransactionID();
737
738    // then transfer the file
739    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
740    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
741    close(mfr.fd);
742    if (ret < 0) {
743        if (errno == ECANCELED)
744            return MTP_RESPONSE_TRANSACTION_CANCELLED;
745        else
746            return MTP_RESPONSE_GENERAL_ERROR;
747    }
748    return MTP_RESPONSE_OK;
749}
750
751MtpResponseCode MtpServer::doGetThumb() {
752    MtpObjectHandle handle = mRequest.getParameter(1);
753    size_t thumbSize;
754    void* thumb = mDatabase->getThumbnail(handle, thumbSize);
755    if (thumb) {
756        // send data
757        mData.setOperationCode(mRequest.getOperationCode());
758        mData.setTransactionID(mRequest.getTransactionID());
759        mData.writeData(mFD, thumb, thumbSize);
760        free(thumb);
761        return MTP_RESPONSE_OK;
762    } else {
763        return MTP_RESPONSE_GENERAL_ERROR;
764    }
765}
766
767MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
768    if (!hasStorage())
769        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
770    MtpObjectHandle handle = mRequest.getParameter(1);
771    uint64_t offset;
772    uint32_t length;
773    offset = mRequest.getParameter(2);
774    if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
775        // android extension with 64 bit offset
776        uint64_t offset2 = mRequest.getParameter(3);
777        offset = offset | (offset2 << 32);
778        length = mRequest.getParameter(4);
779    } else {
780        // standard GetPartialObject
781        length = mRequest.getParameter(3);
782    }
783    MtpString pathBuf;
784    int64_t fileLength;
785    MtpObjectFormat format;
786    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
787    if (result != MTP_RESPONSE_OK)
788        return result;
789    if (offset + length > fileLength)
790        length = fileLength - offset;
791
792    const char* filePath = (const char *)pathBuf;
793    mtp_file_range  mfr;
794    mfr.fd = open(filePath, O_RDONLY);
795    if (mfr.fd < 0) {
796        return MTP_RESPONSE_GENERAL_ERROR;
797    }
798    mfr.offset = offset;
799    mfr.length = length;
800    mfr.command = mRequest.getOperationCode();
801    mfr.transaction_id = mRequest.getTransactionID();
802    mResponse.setParameter(1, length);
803
804    // transfer the file
805    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
806    ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
807    close(mfr.fd);
808    if (ret < 0) {
809        if (errno == ECANCELED)
810            return MTP_RESPONSE_TRANSACTION_CANCELLED;
811        else
812            return MTP_RESPONSE_GENERAL_ERROR;
813    }
814    return MTP_RESPONSE_OK;
815}
816
817MtpResponseCode MtpServer::doSendObjectInfo() {
818    MtpString path;
819    MtpStorageID storageID = mRequest.getParameter(1);
820    MtpStorage* storage = getStorage(storageID);
821    MtpObjectHandle parent = mRequest.getParameter(2);
822    if (!storage)
823        return MTP_RESPONSE_INVALID_STORAGE_ID;
824
825    // special case the root
826    if (parent == MTP_PARENT_ROOT) {
827        path = storage->getPath();
828        parent = 0;
829    } else {
830        int64_t length;
831        MtpObjectFormat format;
832        int result = mDatabase->getObjectFilePath(parent, path, length, format);
833        if (result != MTP_RESPONSE_OK)
834            return result;
835        if (format != MTP_FORMAT_ASSOCIATION)
836            return MTP_RESPONSE_INVALID_PARENT_OBJECT;
837    }
838
839    // read only the fields we need
840    mData.getUInt32();  // storage ID
841    MtpObjectFormat format = mData.getUInt16();
842    mData.getUInt16();  // protection status
843    mSendObjectFileSize = mData.getUInt32();
844    mData.getUInt16();  // thumb format
845    mData.getUInt32();  // thumb compressed size
846    mData.getUInt32();  // thumb pix width
847    mData.getUInt32();  // thumb pix height
848    mData.getUInt32();  // image pix width
849    mData.getUInt32();  // image pix height
850    mData.getUInt32();  // image bit depth
851    mData.getUInt32();  // parent
852    uint16_t associationType = mData.getUInt16();
853    uint32_t associationDesc = mData.getUInt32();   // association desc
854    mData.getUInt32();  // sequence number
855    MtpStringBuffer name, created, modified;
856    mData.getString(name);    // file name
857    mData.getString(created);      // date created
858    mData.getString(modified);     // date modified
859    // keywords follow
860
861    ALOGV("name: %s format: %04X\n", (const char *)name, format);
862    time_t modifiedTime;
863    if (!parseDateTime(modified, modifiedTime))
864        modifiedTime = 0;
865
866    if (path[path.size() - 1] != '/')
867        path += "/";
868    path += (const char *)name;
869
870    // check space first
871    if (mSendObjectFileSize > storage->getFreeSpace())
872        return MTP_RESPONSE_STORAGE_FULL;
873    uint64_t maxFileSize = storage->getMaxFileSize();
874    // check storage max file size
875    if (maxFileSize != 0) {
876        // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
877        // is >= 0xFFFFFFFF
878        if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
879            return MTP_RESPONSE_OBJECT_TOO_LARGE;
880    }
881
882    ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
883    MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
884            format, parent, storageID, mSendObjectFileSize, modifiedTime);
885    if (handle == kInvalidObjectHandle) {
886        return MTP_RESPONSE_GENERAL_ERROR;
887    }
888
889  if (format == MTP_FORMAT_ASSOCIATION) {
890        mode_t mask = umask(0);
891        int ret = mkdir((const char *)path, mDirectoryPermission);
892        umask(mask);
893        if (ret && ret != -EEXIST)
894            return MTP_RESPONSE_GENERAL_ERROR;
895        chown((const char *)path, getuid(), mFileGroup);
896
897        // SendObject does not get sent for directories, so call endSendObject here instead
898        mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
899    } else {
900        mSendObjectFilePath = path;
901        // save the handle for the SendObject call, which should follow
902        mSendObjectHandle = handle;
903        mSendObjectFormat = format;
904    }
905
906    mResponse.setParameter(1, storageID);
907    mResponse.setParameter(2, parent);
908    mResponse.setParameter(3, handle);
909
910    return MTP_RESPONSE_OK;
911}
912
913MtpResponseCode MtpServer::doSendObject() {
914    if (!hasStorage())
915        return MTP_RESPONSE_GENERAL_ERROR;
916    MtpResponseCode result = MTP_RESPONSE_OK;
917    mode_t mask;
918    int ret, initialData;
919
920    if (mSendObjectHandle == kInvalidObjectHandle) {
921        ALOGE("Expected SendObjectInfo before SendObject");
922        result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
923        goto done;
924    }
925
926    // read the header, and possibly some data
927    ret = mData.read(mFD);
928    if (ret < MTP_CONTAINER_HEADER_SIZE) {
929        result = MTP_RESPONSE_GENERAL_ERROR;
930        goto done;
931    }
932    initialData = ret - MTP_CONTAINER_HEADER_SIZE;
933
934    mtp_file_range  mfr;
935    mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
936    if (mfr.fd < 0) {
937        result = MTP_RESPONSE_GENERAL_ERROR;
938        goto done;
939    }
940    fchown(mfr.fd, getuid(), mFileGroup);
941    // set permissions
942    mask = umask(0);
943    fchmod(mfr.fd, mFilePermission);
944    umask(mask);
945
946    if (initialData > 0)
947        ret = write(mfr.fd, mData.getData(), initialData);
948
949    if (mSendObjectFileSize - initialData > 0) {
950        mfr.offset = initialData;
951        if (mSendObjectFileSize == 0xFFFFFFFF) {
952            // tell driver to read until it receives a short packet
953            mfr.length = 0xFFFFFFFF;
954        } else {
955            mfr.length = mSendObjectFileSize - initialData;
956        }
957
958        ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
959        // transfer the file
960        ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
961        ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
962    }
963    close(mfr.fd);
964
965    if (ret < 0) {
966        unlink(mSendObjectFilePath);
967        if (errno == ECANCELED)
968            result = MTP_RESPONSE_TRANSACTION_CANCELLED;
969        else
970            result = MTP_RESPONSE_GENERAL_ERROR;
971    }
972
973done:
974    // reset so we don't attempt to send the data back
975    mData.reset();
976
977    mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
978            result == MTP_RESPONSE_OK);
979    mSendObjectHandle = kInvalidObjectHandle;
980    mSendObjectFormat = 0;
981    return result;
982}
983
984static void deleteRecursive(const char* path) {
985    char pathbuf[PATH_MAX];
986    int pathLength = strlen(path);
987    if (pathLength >= sizeof(pathbuf) - 1) {
988        ALOGE("path too long: %s\n", path);
989    }
990    strcpy(pathbuf, path);
991    if (pathbuf[pathLength - 1] != '/') {
992        pathbuf[pathLength++] = '/';
993    }
994    char* fileSpot = pathbuf + pathLength;
995    int pathRemaining = sizeof(pathbuf) - pathLength - 1;
996
997    DIR* dir = opendir(path);
998    if (!dir) {
999        ALOGE("opendir %s failed: %s", path, strerror(errno));
1000        return;
1001    }
1002
1003    struct dirent* entry;
1004    while ((entry = readdir(dir))) {
1005        const char* name = entry->d_name;
1006
1007        // ignore "." and ".."
1008        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1009            continue;
1010        }
1011
1012        int nameLength = strlen(name);
1013        if (nameLength > pathRemaining) {
1014            ALOGE("path %s/%s too long\n", path, name);
1015            continue;
1016        }
1017        strcpy(fileSpot, name);
1018
1019        int type = entry->d_type;
1020        if (entry->d_type == DT_DIR) {
1021            deleteRecursive(pathbuf);
1022            rmdir(pathbuf);
1023        } else {
1024            unlink(pathbuf);
1025        }
1026    }
1027    closedir(dir);
1028}
1029
1030static void deletePath(const char* path) {
1031    struct stat statbuf;
1032    if (stat(path, &statbuf) == 0) {
1033        if (S_ISDIR(statbuf.st_mode)) {
1034            deleteRecursive(path);
1035            rmdir(path);
1036        } else {
1037            unlink(path);
1038        }
1039    } else {
1040        ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
1041    }
1042}
1043
1044MtpResponseCode MtpServer::doDeleteObject() {
1045    if (!hasStorage())
1046        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1047    MtpObjectHandle handle = mRequest.getParameter(1);
1048    MtpObjectFormat format = mRequest.getParameter(2);
1049    // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1050    // FIXME - implement deleting objects by format
1051
1052    MtpString filePath;
1053    int64_t fileLength;
1054    int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
1055    if (result == MTP_RESPONSE_OK) {
1056        ALOGV("deleting %s", (const char *)filePath);
1057        result = mDatabase->deleteFile(handle);
1058        // Don't delete the actual files unless the database deletion is allowed
1059        if (result == MTP_RESPONSE_OK) {
1060            deletePath((const char *)filePath);
1061        }
1062    }
1063
1064    return result;
1065}
1066
1067MtpResponseCode MtpServer::doGetObjectPropDesc() {
1068    MtpObjectProperty propCode = mRequest.getParameter(1);
1069    MtpObjectFormat format = mRequest.getParameter(2);
1070    ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
1071                                        MtpDebug::getFormatCodeName(format));
1072    MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
1073    if (!property)
1074        return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
1075    property->write(mData);
1076    delete property;
1077    return MTP_RESPONSE_OK;
1078}
1079
1080MtpResponseCode MtpServer::doGetDevicePropDesc() {
1081    MtpDeviceProperty propCode = mRequest.getParameter(1);
1082    ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
1083    MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1084    if (!property)
1085        return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1086    property->write(mData);
1087    delete property;
1088    return MTP_RESPONSE_OK;
1089}
1090
1091MtpResponseCode MtpServer::doSendPartialObject() {
1092    if (!hasStorage())
1093        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1094    MtpObjectHandle handle = mRequest.getParameter(1);
1095    uint64_t offset = mRequest.getParameter(2);
1096    uint64_t offset2 = mRequest.getParameter(3);
1097    offset = offset | (offset2 << 32);
1098    uint32_t length = mRequest.getParameter(4);
1099
1100    ObjectEdit* edit = getEditObject(handle);
1101    if (!edit) {
1102        ALOGE("object not open for edit in doSendPartialObject");
1103        return MTP_RESPONSE_GENERAL_ERROR;
1104    }
1105
1106    // can't start writing past the end of the file
1107    if (offset > edit->mSize) {
1108        ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
1109        return MTP_RESPONSE_GENERAL_ERROR;
1110    }
1111
1112    const char* filePath = (const char *)edit->mPath;
1113    ALOGV("receiving partial %s %lld %lld\n", filePath, offset, length);
1114
1115    // read the header, and possibly some data
1116    int ret = mData.read(mFD);
1117    if (ret < MTP_CONTAINER_HEADER_SIZE)
1118        return MTP_RESPONSE_GENERAL_ERROR;
1119    int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1120
1121    if (initialData > 0) {
1122        ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
1123        offset += initialData;
1124        length -= initialData;
1125    }
1126
1127    if (length > 0) {
1128        mtp_file_range  mfr;
1129        mfr.fd = edit->mFD;
1130        mfr.offset = offset;
1131        mfr.length = length;
1132
1133        // transfer the file
1134        ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1135        ALOGV("MTP_RECEIVE_FILE returned %d", ret);
1136    }
1137    if (ret < 0) {
1138        mResponse.setParameter(1, 0);
1139        if (errno == ECANCELED)
1140            return MTP_RESPONSE_TRANSACTION_CANCELLED;
1141        else
1142            return MTP_RESPONSE_GENERAL_ERROR;
1143    }
1144
1145    // reset so we don't attempt to send this back
1146    mData.reset();
1147    mResponse.setParameter(1, length);
1148    uint64_t end = offset + length;
1149    if (end > edit->mSize) {
1150        edit->mSize = end;
1151    }
1152    return MTP_RESPONSE_OK;
1153}
1154
1155MtpResponseCode MtpServer::doTruncateObject() {
1156    MtpObjectHandle handle = mRequest.getParameter(1);
1157    ObjectEdit* edit = getEditObject(handle);
1158    if (!edit) {
1159        ALOGE("object not open for edit in doTruncateObject");
1160        return MTP_RESPONSE_GENERAL_ERROR;
1161    }
1162
1163    uint64_t offset = mRequest.getParameter(2);
1164    uint64_t offset2 = mRequest.getParameter(3);
1165    offset |= (offset2 << 32);
1166    if (ftruncate(edit->mFD, offset) != 0) {
1167        return MTP_RESPONSE_GENERAL_ERROR;
1168    } else {
1169        edit->mSize = offset;
1170        return MTP_RESPONSE_OK;
1171    }
1172}
1173
1174MtpResponseCode MtpServer::doBeginEditObject() {
1175    MtpObjectHandle handle = mRequest.getParameter(1);
1176    if (getEditObject(handle)) {
1177        ALOGE("object already open for edit in doBeginEditObject");
1178        return MTP_RESPONSE_GENERAL_ERROR;
1179    }
1180
1181    MtpString path;
1182    int64_t fileLength;
1183    MtpObjectFormat format;
1184    int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1185    if (result != MTP_RESPONSE_OK)
1186        return result;
1187
1188    int fd = open((const char *)path, O_RDWR | O_EXCL);
1189    if (fd < 0) {
1190        ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1191        return MTP_RESPONSE_GENERAL_ERROR;
1192    }
1193
1194    addEditObject(handle, path, fileLength, format, fd);
1195    return MTP_RESPONSE_OK;
1196}
1197
1198MtpResponseCode MtpServer::doEndEditObject() {
1199    MtpObjectHandle handle = mRequest.getParameter(1);
1200    ObjectEdit* edit = getEditObject(handle);
1201    if (!edit) {
1202        ALOGE("object not open for edit in doEndEditObject");
1203        return MTP_RESPONSE_GENERAL_ERROR;
1204    }
1205
1206    commitEdit(edit);
1207    removeEditObject(handle);
1208    return MTP_RESPONSE_OK;
1209}
1210
1211}  // namespace android
1212