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