MtpServer.cpp revision 73ecd23cc2ebadb8e1fae1cc21ac559524c6b2bb
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 25#include <cutils/properties.h> 26 27#include "MtpDebug.h" 28#include "MtpDatabase.h" 29#include "MtpProperty.h" 30#include "MtpServer.h" 31#include "MtpStorage.h" 32#include "MtpStringBuffer.h" 33 34#include <linux/usb/f_mtp.h> 35 36namespace android { 37 38static const MtpOperationCode kSupportedOperationCodes[] = { 39 MTP_OPERATION_GET_DEVICE_INFO, 40 MTP_OPERATION_OPEN_SESSION, 41 MTP_OPERATION_CLOSE_SESSION, 42 MTP_OPERATION_GET_STORAGE_IDS, 43 MTP_OPERATION_GET_STORAGE_INFO, 44 MTP_OPERATION_GET_NUM_OBJECTS, 45 MTP_OPERATION_GET_OBJECT_HANDLES, 46 MTP_OPERATION_GET_OBJECT_INFO, 47 MTP_OPERATION_GET_OBJECT, 48// MTP_OPERATION_GET_THUMB, 49 MTP_OPERATION_DELETE_OBJECT, 50 MTP_OPERATION_SEND_OBJECT_INFO, 51 MTP_OPERATION_SEND_OBJECT, 52// MTP_OPERATION_INITIATE_CAPTURE, 53// MTP_OPERATION_FORMAT_STORE, 54// MTP_OPERATION_RESET_DEVICE, 55// MTP_OPERATION_SELF_TEST, 56// MTP_OPERATION_SET_OBJECT_PROTECTION, 57// MTP_OPERATION_POWER_DOWN, 58 MTP_OPERATION_GET_DEVICE_PROP_DESC, 59 MTP_OPERATION_GET_DEVICE_PROP_VALUE, 60 MTP_OPERATION_SET_DEVICE_PROP_VALUE, 61 MTP_OPERATION_RESET_DEVICE_PROP_VALUE, 62// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, 63// MTP_OPERATION_MOVE_OBJECT, 64// MTP_OPERATION_COPY_OBJECT, 65// MTP_OPERATION_GET_PARTIAL_OBJECT, 66// MTP_OPERATION_INITIATE_OPEN_CAPTURE, 67 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, 68// MTP_OPERATION_GET_OBJECT_PROP_DESC, 69 MTP_OPERATION_GET_OBJECT_PROP_VALUE, 70 MTP_OPERATION_SET_OBJECT_PROP_VALUE, 71// MTP_OPERATION_GET_OBJECT_REFERENCES, 72// MTP_OPERATION_SET_OBJECT_REFERENCES, 73// MTP_OPERATION_SKIP, 74}; 75 76static const MtpEventCode kSupportedEventCodes[] = { 77 MTP_EVENT_OBJECT_ADDED, 78 MTP_EVENT_OBJECT_REMOVED, 79}; 80 81static const MtpObjectProperty kSupportedObjectProperties[] = { 82 MTP_PROPERTY_STORAGE_ID, 83 MTP_PROPERTY_OBJECT_FORMAT, 84 MTP_PROPERTY_OBJECT_SIZE, 85 MTP_PROPERTY_OBJECT_FILE_NAME, 86 MTP_PROPERTY_PARENT_OBJECT, 87}; 88 89static const MtpObjectFormat kSupportedPlaybackFormats[] = { 90 // MTP_FORMAT_UNDEFINED, 91 MTP_FORMAT_ASSOCIATION, 92 // MTP_FORMAT_TEXT, 93 // MTP_FORMAT_HTML, 94 MTP_FORMAT_MP3, 95 //MTP_FORMAT_AVI, 96 MTP_FORMAT_MPEG, 97 // MTP_FORMAT_ASF, 98 MTP_FORMAT_EXIF_JPEG, 99 MTP_FORMAT_TIFF_EP, 100 // MTP_FORMAT_BMP, 101 MTP_FORMAT_GIF, 102 MTP_FORMAT_JFIF, 103 MTP_FORMAT_PNG, 104 MTP_FORMAT_TIFF, 105 MTP_FORMAT_WMA, 106 MTP_FORMAT_OGG, 107 MTP_FORMAT_AAC, 108 // MTP_FORMAT_FLAC, 109 // MTP_FORMAT_WMV, 110 MTP_FORMAT_MP4_CONTAINER, 111 MTP_FORMAT_MP2, 112 MTP_FORMAT_3GP_CONTAINER, 113 // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM, 114 // MTP_FORMAT_ABSTRACT_AV_PLAYLIST, 115 // MTP_FORMAT_WPL_PLAYLIST, 116 // MTP_FORMAT_M3U_PLAYLIST, 117 // MTP_FORMAT_MPL_PLAYLIST, 118 // MTP_FORMAT_PLS_PLAYLIST, 119}; 120 121MtpServer::MtpServer(int fd, MtpDatabase* database, 122 int fileGroup, int filePerm, int directoryPerm) 123 : mFD(fd), 124 mDatabase(database), 125 mFileGroup(fileGroup), 126 mFilePermission(filePerm), 127 mDirectoryPermission(directoryPerm), 128 mSessionID(0), 129 mSessionOpen(false), 130 mSendObjectHandle(kInvalidObjectHandle), 131 mSendObjectFormat(0), 132 mSendObjectFileSize(0) 133{ 134 initObjectProperties(); 135} 136 137MtpServer::~MtpServer() { 138} 139 140void MtpServer::addStorage(const char* filePath) { 141 int index = mStorages.size() + 1; 142 index |= index << 16; // set high and low part to our index 143 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase); 144 addStorage(storage); 145} 146 147MtpStorage* MtpServer::getStorage(MtpStorageID id) { 148 for (int i = 0; i < mStorages.size(); i++) { 149 MtpStorage* storage = mStorages[i]; 150 if (storage->getStorageID() == id) 151 return storage; 152 } 153 return NULL; 154} 155 156void MtpServer::run() { 157 int fd = mFD; 158 159 LOGV("MtpServer::run fd: %d\n", fd); 160 161 while (1) { 162 int ret = mRequest.read(fd); 163 if (ret < 0) { 164 LOGE("request read returned %d, errno: %d", ret, errno); 165 if (errno == ECANCELED) { 166 // return to top of loop and wait for next command 167 continue; 168 } 169 break; 170 } 171 MtpOperationCode operation = mRequest.getOperationCode(); 172 MtpTransactionID transaction = mRequest.getTransactionID(); 173 174 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation)); 175 mRequest.dump(); 176 177 // FIXME need to generalize this 178 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO); 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 if (ret < 0) { 216 LOGE("request write returned %d, errno: %d", ret, errno); 217 if (errno == ECANCELED) { 218 // return to top of loop and wait for next command 219 continue; 220 } 221 break; 222 } 223 } else { 224 LOGV("skipping response\n"); 225 } 226 } 227} 228 229MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) { 230 for (int i = 0; i < mObjectProperties.size(); i++) { 231 MtpProperty* property = mObjectProperties[i]; 232 if (property->getPropertyCode() == propCode) 233 return property; 234 } 235 return NULL; 236} 237 238MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) { 239 for (int i = 0; i < mDeviceProperties.size(); i++) { 240 MtpProperty* property = mDeviceProperties[i]; 241 if (property->getPropertyCode() == propCode) 242 return property; 243 } 244 return NULL; 245} 246 247void MtpServer::sendObjectAdded(MtpObjectHandle handle) { 248 if (mSessionOpen) { 249 LOGD("sendObjectAdded %d\n", handle); 250 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED); 251 mEvent.setTransactionID(mRequest.getTransactionID()); 252 mEvent.setParameter(1, handle); 253 int ret = mEvent.write(mFD); 254 LOGD("mEvent.write returned %d\n", ret); 255 } 256} 257 258void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 259 if (mSessionOpen) { 260 LOGD("sendObjectRemoved %d\n", handle); 261 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED); 262 mEvent.setTransactionID(mRequest.getTransactionID()); 263 mEvent.setParameter(1, handle); 264 int ret = mEvent.write(mFD); 265 LOGD("mEvent.write returned %d\n", ret); 266 } 267} 268 269void MtpServer::initObjectProperties() { 270 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16)); 271 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16)); 272 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64)); 273 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR)); 274 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32)); 275} 276 277bool MtpServer::handleRequest() { 278 MtpOperationCode operation = mRequest.getOperationCode(); 279 MtpResponseCode response; 280 281 mResponse.reset(); 282 283 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 284 // FIXME - need to delete mSendObjectHandle from the database 285 LOGE("expected SendObject after SendObjectInfo"); 286 mSendObjectHandle = kInvalidObjectHandle; 287 } 288 289 switch (operation) { 290 case MTP_OPERATION_GET_DEVICE_INFO: 291 response = doGetDeviceInfo(); 292 break; 293 case MTP_OPERATION_OPEN_SESSION: 294 response = doOpenSession(); 295 break; 296 case MTP_OPERATION_CLOSE_SESSION: 297 response = doCloseSession(); 298 break; 299 case MTP_OPERATION_GET_STORAGE_IDS: 300 response = doGetStorageIDs(); 301 break; 302 case MTP_OPERATION_GET_STORAGE_INFO: 303 response = doGetStorageInfo(); 304 break; 305 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 306 response = doGetObjectPropsSupported(); 307 break; 308 case MTP_OPERATION_GET_OBJECT_HANDLES: 309 response = doGetObjectHandles(); 310 break; 311 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 312 response = doGetObjectPropValue(); 313 break; 314 case MTP_OPERATION_GET_OBJECT_INFO: 315 response = doGetObjectInfo(); 316 break; 317 case MTP_OPERATION_GET_OBJECT: 318 response = doGetObject(); 319 break; 320 case MTP_OPERATION_SEND_OBJECT_INFO: 321 response = doSendObjectInfo(); 322 break; 323 case MTP_OPERATION_SEND_OBJECT: 324 response = doSendObject(); 325 break; 326 case MTP_OPERATION_DELETE_OBJECT: 327 response = doDeleteObject(); 328 break; 329 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 330 response = doGetObjectPropDesc(); 331 break; 332 default: 333 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 334 break; 335 } 336 337 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 338 return false; 339 mResponse.setResponseCode(response); 340 return true; 341} 342 343MtpResponseCode MtpServer::doGetDeviceInfo() { 344 MtpStringBuffer string; 345 char prop_value[PROPERTY_VALUE_MAX]; 346 347 // fill in device info 348 mData.putUInt16(MTP_STANDARD_VERSION); 349 mData.putUInt32(6); // MTP Vendor Extension ID 350 mData.putUInt16(MTP_STANDARD_VERSION); 351 string.set("microsoft.com: 1.0;"); 352 mData.putString(string); // MTP Extensions 353 mData.putUInt16(0); //Functional Mode 354 mData.putAUInt16(kSupportedOperationCodes, 355 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 356 mData.putAUInt16(kSupportedEventCodes, 357 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported 358 mData.putEmptyArray(); // Device Properties Supported 359 mData.putEmptyArray(); // Capture Formats 360 mData.putAUInt16(kSupportedPlaybackFormats, 361 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats 362 // FIXME 363 string.set("Google, Inc."); 364 mData.putString(string); // Manufacturer 365 366 property_get("ro.product.model", prop_value, "MTP Device"); 367 string.set(prop_value); 368 mData.putString(string); // Model 369 string.set("1.0"); 370 mData.putString(string); // Device Version 371 372 property_get("ro.serialno", prop_value, "????????"); 373 string.set(prop_value); 374 mData.putString(string); // Serial Number 375 376 return MTP_RESPONSE_OK; 377} 378 379MtpResponseCode MtpServer::doOpenSession() { 380 if (mSessionOpen) { 381 mResponse.setParameter(1, mSessionID); 382 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 383 } 384 mSessionID = mRequest.getParameter(1); 385 mSessionOpen = true; 386 return MTP_RESPONSE_OK; 387} 388 389MtpResponseCode MtpServer::doCloseSession() { 390 if (!mSessionOpen) 391 return MTP_RESPONSE_SESSION_NOT_OPEN; 392 mSessionID = 0; 393 mSessionOpen = false; 394 return MTP_RESPONSE_OK; 395} 396 397MtpResponseCode MtpServer::doGetStorageIDs() { 398 if (!mSessionOpen) 399 return MTP_RESPONSE_SESSION_NOT_OPEN; 400 401 int count = mStorages.size(); 402 mData.putUInt32(count); 403 for (int i = 0; i < count; i++) 404 mData.putUInt32(mStorages[i]->getStorageID()); 405 406 return MTP_RESPONSE_OK; 407} 408 409MtpResponseCode MtpServer::doGetStorageInfo() { 410 MtpStringBuffer string; 411 412 if (!mSessionOpen) 413 return MTP_RESPONSE_SESSION_NOT_OPEN; 414 MtpStorageID id = mRequest.getParameter(1); 415 MtpStorage* storage = getStorage(id); 416 if (!storage) 417 return MTP_RESPONSE_INVALID_STORAGE_ID; 418 419 mData.putUInt16(storage->getType()); 420 mData.putUInt16(storage->getFileSystemType()); 421 mData.putUInt16(storage->getAccessCapability()); 422 mData.putUInt64(storage->getMaxCapacity()); 423 mData.putUInt64(storage->getFreeSpace()); 424 mData.putUInt32(1024*1024*1024); // Free Space in Objects 425 string.set(storage->getDescription()); 426 mData.putString(string); 427 mData.putEmptyString(); // Volume Identifier 428 429 return MTP_RESPONSE_OK; 430} 431 432MtpResponseCode MtpServer::doGetObjectPropsSupported() { 433 if (!mSessionOpen) 434 return MTP_RESPONSE_SESSION_NOT_OPEN; 435 MtpObjectFormat format = mRequest.getParameter(1); 436 mData.putAUInt16(kSupportedObjectProperties, 437 sizeof(kSupportedObjectProperties) / sizeof(uint16_t)); 438 return MTP_RESPONSE_OK; 439} 440 441MtpResponseCode MtpServer::doGetObjectHandles() { 442 if (!mSessionOpen) 443 return MTP_RESPONSE_SESSION_NOT_OPEN; 444 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 445 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 446 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 447 // 0x00000000 for all objects? 448 if (parent == 0xFFFFFFFF) 449 parent = 0; 450 451 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 452 mData.putAUInt32(handles); 453 delete handles; 454 return MTP_RESPONSE_OK; 455} 456 457MtpResponseCode MtpServer::doGetObjectPropValue() { 458 MtpObjectHandle handle = mRequest.getParameter(1); 459 MtpObjectProperty property = mRequest.getParameter(2); 460 461 return mDatabase->getObjectProperty(handle, property, mData); 462} 463 464MtpResponseCode MtpServer::doGetObjectInfo() { 465 MtpObjectHandle handle = mRequest.getParameter(1); 466 return mDatabase->getObjectInfo(handle, mData); 467} 468 469MtpResponseCode MtpServer::doGetObject() { 470 MtpObjectHandle handle = mRequest.getParameter(1); 471 MtpString pathBuf; 472 int64_t fileLength; 473 if (!mDatabase->getObjectFilePath(handle, pathBuf, fileLength)) 474 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 475 const char* filePath = (const char *)pathBuf; 476 477 mtp_file_range mfr; 478 mfr.fd = open(filePath, O_RDONLY); 479 if (mfr.fd < 0) { 480 return MTP_RESPONSE_GENERAL_ERROR; 481 } 482 mfr.offset = 0; 483 mfr.length = fileLength; 484 485 // send data header 486 mData.setOperationCode(mRequest.getOperationCode()); 487 mData.setTransactionID(mRequest.getTransactionID()); 488 mData.writeDataHeader(mFD, fileLength); 489 490 // then transfer the file 491 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 492 close(mfr.fd); 493 if (ret < 0) { 494 if (errno == ECANCELED) 495 return MTP_RESPONSE_TRANSACTION_CANCELLED; 496 else 497 return MTP_RESPONSE_GENERAL_ERROR; 498 } 499 return MTP_RESPONSE_OK; 500} 501 502MtpResponseCode MtpServer::doSendObjectInfo() { 503 MtpString path; 504 MtpStorageID storageID = mRequest.getParameter(1); 505 MtpStorage* storage = getStorage(storageID); 506 MtpObjectHandle parent = mRequest.getParameter(2); 507 if (!storage) 508 return MTP_RESPONSE_INVALID_STORAGE_ID; 509 510 // special case the root 511 if (parent == MTP_PARENT_ROOT) { 512 path = storage->getPath(); 513 parent = 0; 514 } else { 515 int64_t dummy; 516 if (!mDatabase->getObjectFilePath(parent, path, dummy)) 517 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 518 } 519 520 // read only the fields we need 521 mData.getUInt32(); // storage ID 522 MtpObjectFormat format = mData.getUInt16(); 523 mData.getUInt16(); // protection status 524 mSendObjectFileSize = mData.getUInt32(); 525 mData.getUInt16(); // thumb format 526 mData.getUInt32(); // thumb compressed size 527 mData.getUInt32(); // thumb pix width 528 mData.getUInt32(); // thumb pix height 529 mData.getUInt32(); // image pix width 530 mData.getUInt32(); // image pix height 531 mData.getUInt32(); // image bit depth 532 mData.getUInt32(); // parent 533 uint16_t associationType = mData.getUInt16(); 534 uint32_t associationDesc = mData.getUInt32(); // association desc 535 mData.getUInt32(); // sequence number 536 MtpStringBuffer name, created, modified; 537 mData.getString(name); // file name 538 mData.getString(created); // date created 539 mData.getString(modified); // date modified 540 // keywords follow 541 542 time_t modifiedTime; 543 if (!parseDateTime(modified, modifiedTime)) 544 modifiedTime = 0; 545 546 if (path[path.size() - 1] != '/') 547 path += "/"; 548 path += (const char *)name; 549 550 mDatabase->beginTransaction(); 551 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 552 format, parent, storageID, mSendObjectFileSize, modifiedTime); 553 if (handle == kInvalidObjectHandle) { 554 mDatabase->rollbackTransaction(); 555 return MTP_RESPONSE_GENERAL_ERROR; 556 } 557 mDatabase->commitTransaction(); 558 559 if (format == MTP_FORMAT_ASSOCIATION) { 560 mode_t mask = umask(0); 561 int ret = mkdir((const char *)path, mDirectoryPermission); 562 umask(mask); 563 if (ret && ret != -EEXIST) 564 return MTP_RESPONSE_GENERAL_ERROR; 565 chown((const char *)path, getuid(), mFileGroup); 566 } else { 567 mSendObjectFilePath = path; 568 // save the handle for the SendObject call, which should follow 569 mSendObjectHandle = handle; 570 mSendObjectFormat = format; 571 } 572 573 mResponse.setParameter(1, storageID); 574 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent)); 575 mResponse.setParameter(3, handle); 576 577 return MTP_RESPONSE_OK; 578} 579 580MtpResponseCode MtpServer::doSendObject() { 581 MtpResponseCode result = MTP_RESPONSE_OK; 582 mode_t mask; 583 int ret; 584 585 if (mSendObjectHandle == kInvalidObjectHandle) { 586 LOGE("Expected SendObjectInfo before SendObject"); 587 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 588 goto done; 589 } 590 591 // read the header 592 ret = mData.readDataHeader(mFD); 593 // FIXME - check for errors here. 594 595 // reset so we don't attempt to send this back 596 mData.reset(); 597 598 mtp_file_range mfr; 599 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 600 if (mfr.fd < 0) { 601 result = MTP_RESPONSE_GENERAL_ERROR; 602 goto done; 603 } 604 fchown(mfr.fd, getuid(), mFileGroup); 605 // set permissions 606 mask = umask(0); 607 fchmod(mfr.fd, mFilePermission); 608 umask(mask); 609 610 mfr.offset = 0; 611 mfr.length = mSendObjectFileSize; 612 613 // transfer the file 614 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 615 close(mfr.fd); 616 617 LOGV("MTP_RECEIVE_FILE returned %d", ret); 618 619 if (ret < 0) { 620 unlink(mSendObjectFilePath); 621 if (errno == ECANCELED) 622 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 623 else 624 result = MTP_RESPONSE_GENERAL_ERROR; 625 } 626 627done: 628 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 629 result == MTP_RESPONSE_OK); 630 mSendObjectHandle = kInvalidObjectHandle; 631 mSendObjectFormat = 0; 632 return result; 633} 634 635MtpResponseCode MtpServer::doDeleteObject() { 636 MtpObjectHandle handle = mRequest.getParameter(1); 637 MtpObjectFormat format = mRequest.getParameter(1); 638 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 639 // FIXME - implement deleting objects by format 640 // FIXME - handle non-empty directories 641 642 MtpString filePath; 643 int64_t fileLength; 644 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength)) 645 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 646 647 LOGV("deleting %s", (const char *)filePath); 648 // one of these should work 649 rmdir((const char *)filePath); 650 unlink((const char *)filePath); 651 652 mDatabase->deleteFile(handle); 653 654 return MTP_RESPONSE_OK; 655} 656 657MtpResponseCode MtpServer::doGetObjectPropDesc() { 658 MtpObjectProperty propCode = mRequest.getParameter(1); 659 MtpObjectFormat format = mRequest.getParameter(2); 660 MtpProperty* property = getObjectProperty(propCode); 661 if (!property) 662 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 663 664 property->write(mData); 665 return MTP_RESPONSE_OK; 666} 667 668} // namespace android 669