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