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