MtpServer.cpp revision 8065e2056073808716db32136d7acfd50eeab924
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 LOGD("sendObjectAdded %d\n", handle); 249 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED); 250 mEvent.setTransactionID(mRequest.getTransactionID()); 251 mEvent.setParameter(1, handle); 252 int ret = mEvent.write(mFD); 253 LOGD("mEvent.write returned %d\n", ret); 254} 255 256void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 257 LOGD("sendObjectRemoved %d\n", handle); 258 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED); 259 mEvent.setTransactionID(mRequest.getTransactionID()); 260 mEvent.setParameter(1, handle); 261 int ret = mEvent.write(mFD); 262 LOGD("mEvent.write returned %d\n", ret); 263} 264 265void MtpServer::initObjectProperties() { 266 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16)); 267 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16)); 268 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64)); 269 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR)); 270 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32)); 271} 272 273bool MtpServer::handleRequest() { 274 MtpOperationCode operation = mRequest.getOperationCode(); 275 MtpResponseCode response; 276 277 mResponse.reset(); 278 279 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 280 // FIXME - need to delete mSendObjectHandle from the database 281 LOGE("expected SendObject after SendObjectInfo"); 282 mSendObjectHandle = kInvalidObjectHandle; 283 } 284 285 switch (operation) { 286 case MTP_OPERATION_GET_DEVICE_INFO: 287 response = doGetDeviceInfo(); 288 break; 289 case MTP_OPERATION_OPEN_SESSION: 290 response = doOpenSession(); 291 break; 292 case MTP_OPERATION_CLOSE_SESSION: 293 response = doCloseSession(); 294 break; 295 case MTP_OPERATION_GET_STORAGE_IDS: 296 response = doGetStorageIDs(); 297 break; 298 case MTP_OPERATION_GET_STORAGE_INFO: 299 response = doGetStorageInfo(); 300 break; 301 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 302 response = doGetObjectPropsSupported(); 303 break; 304 case MTP_OPERATION_GET_OBJECT_HANDLES: 305 response = doGetObjectHandles(); 306 break; 307 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 308 response = doGetObjectPropValue(); 309 break; 310 case MTP_OPERATION_GET_OBJECT_INFO: 311 response = doGetObjectInfo(); 312 break; 313 case MTP_OPERATION_GET_OBJECT: 314 response = doGetObject(); 315 break; 316 case MTP_OPERATION_SEND_OBJECT_INFO: 317 response = doSendObjectInfo(); 318 break; 319 case MTP_OPERATION_SEND_OBJECT: 320 response = doSendObject(); 321 break; 322 case MTP_OPERATION_DELETE_OBJECT: 323 response = doDeleteObject(); 324 break; 325 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 326 response = doGetObjectPropDesc(); 327 break; 328 default: 329 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 330 break; 331 } 332 333 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 334 return false; 335 mResponse.setResponseCode(response); 336 return true; 337} 338 339MtpResponseCode MtpServer::doGetDeviceInfo() { 340 MtpStringBuffer string; 341 char prop_value[PROPERTY_VALUE_MAX]; 342 343 // fill in device info 344 mData.putUInt16(MTP_STANDARD_VERSION); 345 mData.putUInt32(6); // MTP Vendor Extension ID 346 mData.putUInt16(MTP_STANDARD_VERSION); 347 string.set("microsoft.com: 1.0;"); 348 mData.putString(string); // MTP Extensions 349 mData.putUInt16(0); //Functional Mode 350 mData.putAUInt16(kSupportedOperationCodes, 351 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 352 mData.putAUInt16(kSupportedEventCodes, 353 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported 354 mData.putEmptyArray(); // Device Properties Supported 355 mData.putEmptyArray(); // Capture Formats 356 mData.putAUInt16(kSupportedPlaybackFormats, 357 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats 358 // FIXME 359 string.set("Google, Inc."); 360 mData.putString(string); // Manufacturer 361 362 property_get("ro.product.model", prop_value, "MTP Device"); 363 string.set(prop_value); 364 mData.putString(string); // Model 365 string.set("1.0"); 366 mData.putString(string); // Device Version 367 368 property_get("ro.serialno", prop_value, "????????"); 369 string.set(prop_value); 370 mData.putString(string); // Serial Number 371 372 return MTP_RESPONSE_OK; 373} 374 375MtpResponseCode MtpServer::doOpenSession() { 376 if (mSessionOpen) { 377 mResponse.setParameter(1, mSessionID); 378 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 379 } 380 mSessionID = mRequest.getParameter(1); 381 mSessionOpen = true; 382 return MTP_RESPONSE_OK; 383} 384 385MtpResponseCode MtpServer::doCloseSession() { 386 if (!mSessionOpen) 387 return MTP_RESPONSE_SESSION_NOT_OPEN; 388 mSessionID = 0; 389 mSessionOpen = false; 390 return MTP_RESPONSE_OK; 391} 392 393MtpResponseCode MtpServer::doGetStorageIDs() { 394 if (!mSessionOpen) 395 return MTP_RESPONSE_SESSION_NOT_OPEN; 396 397 int count = mStorages.size(); 398 mData.putUInt32(count); 399 for (int i = 0; i < count; i++) 400 mData.putUInt32(mStorages[i]->getStorageID()); 401 402 return MTP_RESPONSE_OK; 403} 404 405MtpResponseCode MtpServer::doGetStorageInfo() { 406 MtpStringBuffer string; 407 408 if (!mSessionOpen) 409 return MTP_RESPONSE_SESSION_NOT_OPEN; 410 MtpStorageID id = mRequest.getParameter(1); 411 MtpStorage* storage = getStorage(id); 412 if (!storage) 413 return MTP_RESPONSE_INVALID_STORAGE_ID; 414 415 mData.putUInt16(storage->getType()); 416 mData.putUInt16(storage->getFileSystemType()); 417 mData.putUInt16(storage->getAccessCapability()); 418 mData.putUInt64(storage->getMaxCapacity()); 419 mData.putUInt64(storage->getFreeSpace()); 420 mData.putUInt32(1024*1024*1024); // Free Space in Objects 421 string.set(storage->getDescription()); 422 mData.putString(string); 423 mData.putEmptyString(); // Volume Identifier 424 425 return MTP_RESPONSE_OK; 426} 427 428MtpResponseCode MtpServer::doGetObjectPropsSupported() { 429 if (!mSessionOpen) 430 return MTP_RESPONSE_SESSION_NOT_OPEN; 431 MtpObjectFormat format = mRequest.getParameter(1); 432 mData.putAUInt16(kSupportedObjectProperties, 433 sizeof(kSupportedObjectProperties) / sizeof(uint16_t)); 434 return MTP_RESPONSE_OK; 435} 436 437MtpResponseCode MtpServer::doGetObjectHandles() { 438 if (!mSessionOpen) 439 return MTP_RESPONSE_SESSION_NOT_OPEN; 440 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 441 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 442 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 443 // 0x00000000 for all objects? 444 if (parent == 0xFFFFFFFF) 445 parent = 0; 446 447 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 448 mData.putAUInt32(handles); 449 delete handles; 450 return MTP_RESPONSE_OK; 451} 452 453MtpResponseCode MtpServer::doGetObjectPropValue() { 454 MtpObjectHandle handle = mRequest.getParameter(1); 455 MtpObjectProperty property = mRequest.getParameter(2); 456 457 return mDatabase->getObjectProperty(handle, property, mData); 458} 459 460MtpResponseCode MtpServer::doGetObjectInfo() { 461 MtpObjectHandle handle = mRequest.getParameter(1); 462 return mDatabase->getObjectInfo(handle, mData); 463} 464 465MtpResponseCode MtpServer::doGetObject() { 466 MtpObjectHandle handle = mRequest.getParameter(1); 467 MtpString pathBuf; 468 int64_t fileLength; 469 if (!mDatabase->getObjectFilePath(handle, pathBuf, fileLength)) 470 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 471 const char* filePath = (const char *)pathBuf; 472 473 mtp_file_range mfr; 474 mfr.fd = open(filePath, O_RDONLY); 475 if (mfr.fd < 0) { 476 return MTP_RESPONSE_GENERAL_ERROR; 477 } 478 mfr.offset = 0; 479 mfr.length = fileLength; 480 481 // send data header 482 mData.setOperationCode(mRequest.getOperationCode()); 483 mData.setTransactionID(mRequest.getTransactionID()); 484 mData.writeDataHeader(mFD, fileLength); 485 486 // then transfer the file 487 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 488 close(mfr.fd); 489 if (ret < 0) { 490 if (errno == ECANCELED) 491 return MTP_RESPONSE_TRANSACTION_CANCELLED; 492 else 493 return MTP_RESPONSE_GENERAL_ERROR; 494 } 495 return MTP_RESPONSE_OK; 496} 497 498MtpResponseCode MtpServer::doSendObjectInfo() { 499 MtpString path; 500 MtpStorageID storageID = mRequest.getParameter(1); 501 MtpStorage* storage = getStorage(storageID); 502 MtpObjectHandle parent = mRequest.getParameter(2); 503 if (!storage) 504 return MTP_RESPONSE_INVALID_STORAGE_ID; 505 506 // special case the root 507 if (parent == MTP_PARENT_ROOT) { 508 path = storage->getPath(); 509 parent = 0; 510 } else { 511 int64_t dummy; 512 if (!mDatabase->getObjectFilePath(parent, path, dummy)) 513 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 514 } 515 516 // read only the fields we need 517 mData.getUInt32(); // storage ID 518 MtpObjectFormat format = mData.getUInt16(); 519 mData.getUInt16(); // protection status 520 mSendObjectFileSize = mData.getUInt32(); 521 mData.getUInt16(); // thumb format 522 mData.getUInt32(); // thumb compressed size 523 mData.getUInt32(); // thumb pix width 524 mData.getUInt32(); // thumb pix height 525 mData.getUInt32(); // image pix width 526 mData.getUInt32(); // image pix height 527 mData.getUInt32(); // image bit depth 528 mData.getUInt32(); // parent 529 uint16_t associationType = mData.getUInt16(); 530 uint32_t associationDesc = mData.getUInt32(); // association desc 531 mData.getUInt32(); // sequence number 532 MtpStringBuffer name, created, modified; 533 mData.getString(name); // file name 534 mData.getString(created); // date created 535 mData.getString(modified); // date modified 536 // keywords follow 537 538 time_t modifiedTime; 539 if (!parseDateTime(modified, modifiedTime)) 540 modifiedTime = 0; 541 542 if (path[path.size() - 1] != '/') 543 path += "/"; 544 path += (const char *)name; 545 546 mDatabase->beginTransaction(); 547 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 548 format, parent, storageID, mSendObjectFileSize, modifiedTime); 549 if (handle == kInvalidObjectHandle) { 550 mDatabase->rollbackTransaction(); 551 return MTP_RESPONSE_GENERAL_ERROR; 552 } 553 mDatabase->commitTransaction(); 554 555 if (format == MTP_FORMAT_ASSOCIATION) { 556 mode_t mask = umask(0); 557 int ret = mkdir((const char *)path, mDirectoryPermission); 558 umask(mask); 559 if (ret && ret != -EEXIST) 560 return MTP_RESPONSE_GENERAL_ERROR; 561 chown((const char *)path, getuid(), mFileGroup); 562 } else { 563 mSendObjectFilePath = path; 564 // save the handle for the SendObject call, which should follow 565 mSendObjectHandle = handle; 566 mSendObjectFormat = format; 567 } 568 569 mResponse.setParameter(1, storageID); 570 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent)); 571 mResponse.setParameter(3, handle); 572 573 return MTP_RESPONSE_OK; 574} 575 576MtpResponseCode MtpServer::doSendObject() { 577 MtpResponseCode result = MTP_RESPONSE_OK; 578 mode_t mask; 579 int ret; 580 581 if (mSendObjectHandle == kInvalidObjectHandle) { 582 LOGE("Expected SendObjectInfo before SendObject"); 583 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 584 goto done; 585 } 586 587 // read the header 588 ret = mData.readDataHeader(mFD); 589 // FIXME - check for errors here. 590 591 // reset so we don't attempt to send this back 592 mData.reset(); 593 594 mtp_file_range mfr; 595 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 596 if (mfr.fd < 0) { 597 result = MTP_RESPONSE_GENERAL_ERROR; 598 goto done; 599 } 600 fchown(mfr.fd, getuid(), mFileGroup); 601 // set permissions 602 mask = umask(0); 603 fchmod(mfr.fd, mFilePermission); 604 umask(mask); 605 606 mfr.offset = 0; 607 mfr.length = mSendObjectFileSize; 608 609 // transfer the file 610 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 611 close(mfr.fd); 612 613 LOGV("MTP_RECEIVE_FILE returned %d", ret); 614 615 if (ret < 0) { 616 unlink(mSendObjectFilePath); 617 if (errno == ECANCELED) 618 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 619 else 620 result = MTP_RESPONSE_GENERAL_ERROR; 621 } 622 623done: 624 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 625 result == MTP_RESPONSE_OK); 626 mSendObjectHandle = kInvalidObjectHandle; 627 mSendObjectFormat = 0; 628 return result; 629} 630 631MtpResponseCode MtpServer::doDeleteObject() { 632 MtpObjectHandle handle = mRequest.getParameter(1); 633 MtpObjectFormat format = mRequest.getParameter(1); 634 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 635 // FIXME - implement deleting objects by format 636 // FIXME - handle non-empty directories 637 638 MtpString filePath; 639 int64_t fileLength; 640 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength)) 641 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 642 643 LOGV("deleting %s", (const char *)filePath); 644 // one of these should work 645 rmdir((const char *)filePath); 646 unlink((const char *)filePath); 647 648 mDatabase->deleteFile(handle); 649 650 return MTP_RESPONSE_OK; 651} 652 653MtpResponseCode MtpServer::doGetObjectPropDesc() { 654 MtpObjectProperty propCode = mRequest.getParameter(1); 655 MtpObjectFormat format = mRequest.getParameter(2); 656 MtpProperty* property = getObjectProperty(propCode); 657 if (!property) 658 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 659 660 property->write(mData); 661 return MTP_RESPONSE_OK; 662} 663 664} // namespace android 665