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