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