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