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