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