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