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