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