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