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