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