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