MtpServer.cpp revision d81ce3cf2e6479915658a0829eced062e3655320
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 "MtpProperty.h" 34#include "MtpServer.h" 35#include "MtpStorage.h" 36#include "MtpStringBuffer.h" 37 38#include <linux/usb/f_mtp.h> 39 40namespace android { 41 42static const MtpOperationCode kSupportedOperationCodes[] = { 43 MTP_OPERATION_GET_DEVICE_INFO, 44 MTP_OPERATION_OPEN_SESSION, 45 MTP_OPERATION_CLOSE_SESSION, 46 MTP_OPERATION_GET_STORAGE_IDS, 47 MTP_OPERATION_GET_STORAGE_INFO, 48 MTP_OPERATION_GET_NUM_OBJECTS, 49 MTP_OPERATION_GET_OBJECT_HANDLES, 50 MTP_OPERATION_GET_OBJECT_INFO, 51 MTP_OPERATION_GET_OBJECT, 52// MTP_OPERATION_GET_THUMB, 53 MTP_OPERATION_DELETE_OBJECT, 54 MTP_OPERATION_SEND_OBJECT_INFO, 55 MTP_OPERATION_SEND_OBJECT, 56// MTP_OPERATION_INITIATE_CAPTURE, 57// MTP_OPERATION_FORMAT_STORE, 58// MTP_OPERATION_RESET_DEVICE, 59// MTP_OPERATION_SELF_TEST, 60// MTP_OPERATION_SET_OBJECT_PROTECTION, 61// MTP_OPERATION_POWER_DOWN, 62 MTP_OPERATION_GET_DEVICE_PROP_DESC, 63 MTP_OPERATION_GET_DEVICE_PROP_VALUE, 64 MTP_OPERATION_SET_DEVICE_PROP_VALUE, 65 MTP_OPERATION_RESET_DEVICE_PROP_VALUE, 66// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, 67// MTP_OPERATION_MOVE_OBJECT, 68// MTP_OPERATION_COPY_OBJECT, 69 MTP_OPERATION_GET_PARTIAL_OBJECT, 70// MTP_OPERATION_INITIATE_OPEN_CAPTURE, 71 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, 72 MTP_OPERATION_GET_OBJECT_PROP_DESC, 73 MTP_OPERATION_GET_OBJECT_PROP_VALUE, 74 MTP_OPERATION_SET_OBJECT_PROP_VALUE, 75 MTP_OPERATION_GET_OBJECT_PROP_LIST, 76// MTP_OPERATION_SET_OBJECT_PROP_LIST, 77// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, 78// MTP_OPERATION_SEND_OBJECT_PROP_LIST, 79 MTP_OPERATION_GET_OBJECT_REFERENCES, 80 MTP_OPERATION_SET_OBJECT_REFERENCES, 81// MTP_OPERATION_SKIP, 82}; 83 84static const MtpEventCode kSupportedEventCodes[] = { 85 MTP_EVENT_OBJECT_ADDED, 86 MTP_EVENT_OBJECT_REMOVED, 87}; 88 89MtpServer::MtpServer(int fd, MtpDatabase* database, 90 int fileGroup, int filePerm, int directoryPerm) 91 : mFD(fd), 92 mDatabase(database), 93 mFileGroup(fileGroup), 94 mFilePermission(filePerm), 95 mDirectoryPermission(directoryPerm), 96 mSessionID(0), 97 mSessionOpen(false), 98 mSendObjectHandle(kInvalidObjectHandle), 99 mSendObjectFormat(0), 100 mSendObjectFileSize(0) 101{ 102} 103 104MtpServer::~MtpServer() { 105} 106 107void MtpServer::addStorage(const char* filePath) { 108 int index = mStorages.size() + 1; 109 index |= index << 16; // set high and low part to our index 110 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase); 111 addStorage(storage); 112} 113 114MtpStorage* MtpServer::getStorage(MtpStorageID id) { 115 for (int i = 0; i < mStorages.size(); i++) { 116 MtpStorage* storage = mStorages[i]; 117 if (storage->getStorageID() == id) 118 return storage; 119 } 120 return NULL; 121} 122 123void MtpServer::run() { 124 int fd = mFD; 125 126 LOGV("MtpServer::run fd: %d\n", fd); 127 128 while (1) { 129 int ret = mRequest.read(fd); 130 if (ret < 0) { 131 LOGE("request read returned %d, errno: %d", ret, errno); 132 if (errno == ECANCELED) { 133 // return to top of loop and wait for next command 134 continue; 135 } 136 break; 137 } 138 MtpOperationCode operation = mRequest.getOperationCode(); 139 MtpTransactionID transaction = mRequest.getTransactionID(); 140 141 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation)); 142 mRequest.dump(); 143 144 // FIXME need to generalize this 145 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO 146 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES 147 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE 148 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE); 149 if (dataIn) { 150 int ret = mData.read(fd); 151 if (ret < 0) { 152 LOGE("data read returned %d, errno: %d", ret, errno); 153 if (errno == ECANCELED) { 154 // return to top of loop and wait for next command 155 continue; 156 } 157 break; 158 } 159 LOGV("received data:"); 160 mData.dump(); 161 } else { 162 mData.reset(); 163 } 164 165 if (handleRequest()) { 166 if (!dataIn && mData.hasData()) { 167 mData.setOperationCode(operation); 168 mData.setTransactionID(transaction); 169 LOGV("sending data:"); 170 mData.dump(); 171 ret = mData.write(fd); 172 if (ret < 0) { 173 LOGE("request write returned %d, errno: %d", ret, errno); 174 if (errno == ECANCELED) { 175 // return to top of loop and wait for next command 176 continue; 177 } 178 break; 179 } 180 } 181 182 mResponse.setTransactionID(transaction); 183 LOGV("sending response %04X", mResponse.getResponseCode()); 184 ret = mResponse.write(fd); 185 mResponse.dump(); 186 if (ret < 0) { 187 LOGE("request write returned %d, errno: %d", ret, errno); 188 if (errno == ECANCELED) { 189 // return to top of loop and wait for next command 190 continue; 191 } 192 break; 193 } 194 } else { 195 LOGV("skipping response\n"); 196 } 197 } 198 199 if (mSessionOpen) 200 mDatabase->sessionEnded(); 201} 202 203void MtpServer::sendObjectAdded(MtpObjectHandle handle) { 204 if (mSessionOpen) { 205 LOGD("sendObjectAdded %d\n", handle); 206 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED); 207 mEvent.setTransactionID(mRequest.getTransactionID()); 208 mEvent.setParameter(1, handle); 209 int ret = mEvent.write(mFD); 210 LOGD("mEvent.write returned %d\n", ret); 211 } 212} 213 214void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 215 if (mSessionOpen) { 216 LOGD("sendObjectRemoved %d\n", handle); 217 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED); 218 mEvent.setTransactionID(mRequest.getTransactionID()); 219 mEvent.setParameter(1, handle); 220 int ret = mEvent.write(mFD); 221 LOGD("mEvent.write returned %d\n", ret); 222 } 223} 224 225bool MtpServer::handleRequest() { 226 MtpOperationCode operation = mRequest.getOperationCode(); 227 MtpResponseCode response; 228 229 mResponse.reset(); 230 231 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 232 // FIXME - need to delete mSendObjectHandle from the database 233 LOGE("expected SendObject after SendObjectInfo"); 234 mSendObjectHandle = kInvalidObjectHandle; 235 } 236 237 switch (operation) { 238 case MTP_OPERATION_GET_DEVICE_INFO: 239 response = doGetDeviceInfo(); 240 break; 241 case MTP_OPERATION_OPEN_SESSION: 242 response = doOpenSession(); 243 break; 244 case MTP_OPERATION_CLOSE_SESSION: 245 response = doCloseSession(); 246 break; 247 case MTP_OPERATION_GET_STORAGE_IDS: 248 response = doGetStorageIDs(); 249 break; 250 case MTP_OPERATION_GET_STORAGE_INFO: 251 response = doGetStorageInfo(); 252 break; 253 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 254 response = doGetObjectPropsSupported(); 255 break; 256 case MTP_OPERATION_GET_OBJECT_HANDLES: 257 response = doGetObjectHandles(); 258 break; 259 case MTP_OPERATION_GET_NUM_OBJECTS: 260 response = doGetNumObjects(); 261 break; 262 case MTP_OPERATION_GET_OBJECT_REFERENCES: 263 response = doGetObjectReferences(); 264 break; 265 case MTP_OPERATION_SET_OBJECT_REFERENCES: 266 response = doSetObjectReferences(); 267 break; 268 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 269 response = doGetObjectPropValue(); 270 break; 271 case MTP_OPERATION_SET_OBJECT_PROP_VALUE: 272 response = doSetObjectPropValue(); 273 break; 274 case MTP_OPERATION_GET_DEVICE_PROP_VALUE: 275 response = doGetDevicePropValue(); 276 break; 277 case MTP_OPERATION_SET_DEVICE_PROP_VALUE: 278 response = doSetDevicePropValue(); 279 break; 280 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: 281 response = doResetDevicePropValue(); 282 break; 283 case MTP_OPERATION_GET_OBJECT_PROP_LIST: 284 response = doGetObjectPropList(); 285 break; 286 case MTP_OPERATION_GET_OBJECT_INFO: 287 response = doGetObjectInfo(); 288 break; 289 case MTP_OPERATION_GET_OBJECT: 290 response = doGetObject(); 291 break; 292 case MTP_OPERATION_GET_PARTIAL_OBJECT: 293 response = doGetPartialObject(); 294 break; 295 case MTP_OPERATION_SEND_OBJECT_INFO: 296 response = doSendObjectInfo(); 297 break; 298 case MTP_OPERATION_SEND_OBJECT: 299 response = doSendObject(); 300 break; 301 case MTP_OPERATION_DELETE_OBJECT: 302 response = doDeleteObject(); 303 break; 304 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 305 response = doGetObjectPropDesc(); 306 break; 307 case MTP_OPERATION_GET_DEVICE_PROP_DESC: 308 response = doGetDevicePropDesc(); 309 break; 310 default: 311 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); 312 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 313 break; 314 } 315 316 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 317 return false; 318 mResponse.setResponseCode(response); 319 return true; 320} 321 322MtpResponseCode MtpServer::doGetDeviceInfo() { 323 MtpStringBuffer string; 324 char prop_value[PROPERTY_VALUE_MAX]; 325 326 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats(); 327 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats(); 328 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties(); 329 330 // fill in device info 331 mData.putUInt16(MTP_STANDARD_VERSION); 332 mData.putUInt32(6); // MTP Vendor Extension ID 333 mData.putUInt16(MTP_STANDARD_VERSION); 334 string.set("microsoft.com: 1.0;"); 335 mData.putString(string); // MTP Extensions 336 mData.putUInt16(0); //Functional Mode 337 mData.putAUInt16(kSupportedOperationCodes, 338 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 339 mData.putAUInt16(kSupportedEventCodes, 340 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported 341 mData.putAUInt16(deviceProperties); // Device Properties Supported 342 mData.putAUInt16(captureFormats); // Capture Formats 343 mData.putAUInt16(playbackFormats); // Playback Formats 344 // FIXME 345 string.set("Google, Inc."); 346 mData.putString(string); // Manufacturer 347 348 property_get("ro.product.model", prop_value, "MTP Device"); 349 string.set(prop_value); 350 mData.putString(string); // Model 351 string.set("1.0"); 352 mData.putString(string); // Device Version 353 354 property_get("ro.serialno", prop_value, "????????"); 355 string.set(prop_value); 356 mData.putString(string); // Serial Number 357 358 delete playbackFormats; 359 delete captureFormats; 360 delete deviceProperties; 361 362 return MTP_RESPONSE_OK; 363} 364 365MtpResponseCode MtpServer::doOpenSession() { 366 if (mSessionOpen) { 367 mResponse.setParameter(1, mSessionID); 368 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 369 } 370 mSessionID = mRequest.getParameter(1); 371 mSessionOpen = true; 372 373 mDatabase->sessionStarted(); 374 375 return MTP_RESPONSE_OK; 376} 377 378MtpResponseCode MtpServer::doCloseSession() { 379 if (!mSessionOpen) 380 return MTP_RESPONSE_SESSION_NOT_OPEN; 381 mSessionID = 0; 382 mSessionOpen = false; 383 mDatabase->sessionEnded(); 384 return MTP_RESPONSE_OK; 385} 386 387MtpResponseCode MtpServer::doGetStorageIDs() { 388 if (!mSessionOpen) 389 return MTP_RESPONSE_SESSION_NOT_OPEN; 390 391 int count = mStorages.size(); 392 mData.putUInt32(count); 393 for (int i = 0; i < count; i++) 394 mData.putUInt32(mStorages[i]->getStorageID()); 395 396 return MTP_RESPONSE_OK; 397} 398 399MtpResponseCode MtpServer::doGetStorageInfo() { 400 MtpStringBuffer string; 401 402 if (!mSessionOpen) 403 return MTP_RESPONSE_SESSION_NOT_OPEN; 404 MtpStorageID id = mRequest.getParameter(1); 405 MtpStorage* storage = getStorage(id); 406 if (!storage) 407 return MTP_RESPONSE_INVALID_STORAGE_ID; 408 409 mData.putUInt16(storage->getType()); 410 mData.putUInt16(storage->getFileSystemType()); 411 mData.putUInt16(storage->getAccessCapability()); 412 mData.putUInt64(storage->getMaxCapacity()); 413 mData.putUInt64(storage->getFreeSpace()); 414 mData.putUInt32(1024*1024*1024); // Free Space in Objects 415 string.set(storage->getDescription()); 416 mData.putString(string); 417 mData.putEmptyString(); // Volume Identifier 418 419 return MTP_RESPONSE_OK; 420} 421 422MtpResponseCode MtpServer::doGetObjectPropsSupported() { 423 if (!mSessionOpen) 424 return MTP_RESPONSE_SESSION_NOT_OPEN; 425 MtpObjectFormat format = mRequest.getParameter(1); 426 MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format); 427 mData.putAUInt16(properties); 428 delete properties; 429 return MTP_RESPONSE_OK; 430} 431 432MtpResponseCode MtpServer::doGetObjectHandles() { 433 if (!mSessionOpen) 434 return MTP_RESPONSE_SESSION_NOT_OPEN; 435 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 436 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 437 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 438 // 0x00000000 for all objects? 439 if (parent == 0xFFFFFFFF) 440 parent = 0; 441 442 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 443 mData.putAUInt32(handles); 444 delete handles; 445 return MTP_RESPONSE_OK; 446} 447 448MtpResponseCode MtpServer::doGetNumObjects() { 449 if (!mSessionOpen) 450 return MTP_RESPONSE_SESSION_NOT_OPEN; 451 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 452 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 453 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 454 // 0x00000000 for all objects? 455 if (parent == 0xFFFFFFFF) 456 parent = 0; 457 458 int count = mDatabase->getNumObjects(storageID, format, parent); 459 if (count >= 0) { 460 mResponse.setParameter(1, count); 461 return MTP_RESPONSE_OK; 462 } else { 463 mResponse.setParameter(1, 0); 464 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 465 } 466} 467 468MtpResponseCode MtpServer::doGetObjectReferences() { 469 if (!mSessionOpen) 470 return MTP_RESPONSE_SESSION_NOT_OPEN; 471 MtpStorageID handle = mRequest.getParameter(1); 472 473 // FIXME - check for invalid object handle 474 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle); 475 if (handles) { 476 mData.putAUInt32(handles); 477 delete handles; 478 } else { 479 mData.putEmptyArray(); 480 } 481 return MTP_RESPONSE_OK; 482} 483 484MtpResponseCode MtpServer::doSetObjectReferences() { 485 if (!mSessionOpen) 486 return MTP_RESPONSE_SESSION_NOT_OPEN; 487 MtpStorageID handle = mRequest.getParameter(1); 488 MtpObjectHandleList* references = mData.getAUInt32(); 489 MtpResponseCode result = mDatabase->setObjectReferences(handle, references); 490 delete references; 491 return result; 492} 493 494MtpResponseCode MtpServer::doGetObjectPropValue() { 495 MtpObjectHandle handle = mRequest.getParameter(1); 496 MtpObjectProperty property = mRequest.getParameter(2); 497 LOGD("GetObjectPropValue %d %s\n", handle, 498 MtpDebug::getObjectPropCodeName(property)); 499 500 return mDatabase->getObjectPropertyValue(handle, property, mData); 501} 502 503MtpResponseCode MtpServer::doSetObjectPropValue() { 504 MtpObjectHandle handle = mRequest.getParameter(1); 505 MtpObjectProperty property = mRequest.getParameter(2); 506 LOGD("SetObjectPropValue %d %s\n", handle, 507 MtpDebug::getObjectPropCodeName(property)); 508 509 return mDatabase->setObjectPropertyValue(handle, property, mData); 510} 511 512MtpResponseCode MtpServer::doGetDevicePropValue() { 513 MtpDeviceProperty property = mRequest.getParameter(1); 514 LOGD("GetDevicePropValue %s\n", 515 MtpDebug::getDevicePropCodeName(property)); 516 517 return mDatabase->getDevicePropertyValue(property, mData); 518} 519 520MtpResponseCode MtpServer::doSetDevicePropValue() { 521 MtpDeviceProperty property = mRequest.getParameter(1); 522 LOGD("SetDevicePropValue %s\n", 523 MtpDebug::getDevicePropCodeName(property)); 524 525 return mDatabase->setDevicePropertyValue(property, mData); 526} 527 528MtpResponseCode MtpServer::doResetDevicePropValue() { 529 MtpDeviceProperty property = mRequest.getParameter(1); 530 LOGD("ResetDevicePropValue %s\n", 531 MtpDebug::getDevicePropCodeName(property)); 532 533 return mDatabase->resetDeviceProperty(property); 534} 535 536MtpResponseCode MtpServer::doGetObjectPropList() { 537 538 MtpObjectHandle handle = mRequest.getParameter(1); 539 MtpObjectFormat format = mRequest.getParameter(2); 540 MtpDeviceProperty property = mRequest.getParameter(3); 541 int groupCode = mRequest.getParameter(4); 542 int depth = mRequest.getParameter(4); 543 LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", 544 handle, MtpDebug::getFormatCodeName(format), 545 MtpDebug::getObjectPropCodeName(property), groupCode, depth); 546 547 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); 548} 549 550MtpResponseCode MtpServer::doGetObjectInfo() { 551 MtpObjectHandle handle = mRequest.getParameter(1); 552 return mDatabase->getObjectInfo(handle, mData); 553} 554 555MtpResponseCode MtpServer::doGetObject() { 556 MtpObjectHandle handle = mRequest.getParameter(1); 557 MtpString pathBuf; 558 int64_t fileLength; 559 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength); 560 if (result != MTP_RESPONSE_OK) 561 return result; 562 563 const char* filePath = (const char *)pathBuf; 564 mtp_file_range mfr; 565 mfr.fd = open(filePath, O_RDONLY); 566 if (mfr.fd < 0) { 567 return MTP_RESPONSE_GENERAL_ERROR; 568 } 569 mfr.offset = 0; 570 mfr.length = fileLength; 571 572 // send data header 573 mData.setOperationCode(mRequest.getOperationCode()); 574 mData.setTransactionID(mRequest.getTransactionID()); 575 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE); 576 577 // then transfer the file 578 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 579 close(mfr.fd); 580 if (ret < 0) { 581 if (errno == ECANCELED) 582 return MTP_RESPONSE_TRANSACTION_CANCELLED; 583 else 584 return MTP_RESPONSE_GENERAL_ERROR; 585 } 586 return MTP_RESPONSE_OK; 587} 588 589MtpResponseCode MtpServer::doGetPartialObject() { 590 MtpObjectHandle handle = mRequest.getParameter(1); 591 uint32_t offset = mRequest.getParameter(2); 592 uint32_t length = mRequest.getParameter(3); 593 MtpString pathBuf; 594 int64_t fileLength; 595 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength); 596 if (result != MTP_RESPONSE_OK) 597 return result; 598 if (offset + length > fileLength) 599 length = fileLength - offset; 600 601 const char* filePath = (const char *)pathBuf; 602 mtp_file_range mfr; 603 mfr.fd = open(filePath, O_RDONLY); 604 if (mfr.fd < 0) { 605 return MTP_RESPONSE_GENERAL_ERROR; 606 } 607 mfr.offset = offset; 608 mfr.length = length; 609 mResponse.setParameter(1, length); 610 611 // send data header 612 mData.setOperationCode(mRequest.getOperationCode()); 613 mData.setTransactionID(mRequest.getTransactionID()); 614 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE); 615 616 // then transfer the file 617 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 618 close(mfr.fd); 619 if (ret < 0) { 620 if (errno == ECANCELED) 621 return MTP_RESPONSE_TRANSACTION_CANCELLED; 622 else 623 return MTP_RESPONSE_GENERAL_ERROR; 624 } 625 return MTP_RESPONSE_OK; 626} 627 628MtpResponseCode MtpServer::doSendObjectInfo() { 629 MtpString path; 630 MtpStorageID storageID = mRequest.getParameter(1); 631 MtpStorage* storage = getStorage(storageID); 632 MtpObjectHandle parent = mRequest.getParameter(2); 633 if (!storage) 634 return MTP_RESPONSE_INVALID_STORAGE_ID; 635 636 // special case the root 637 if (parent == MTP_PARENT_ROOT) { 638 path = storage->getPath(); 639 parent = 0; 640 } else { 641 int64_t dummy; 642 int result = mDatabase->getObjectFilePath(parent, path, dummy); 643 if (result != MTP_RESPONSE_OK) 644 return result; 645 } 646 647 // read only the fields we need 648 mData.getUInt32(); // storage ID 649 MtpObjectFormat format = mData.getUInt16(); 650 mData.getUInt16(); // protection status 651 mSendObjectFileSize = mData.getUInt32(); 652 mData.getUInt16(); // thumb format 653 mData.getUInt32(); // thumb compressed size 654 mData.getUInt32(); // thumb pix width 655 mData.getUInt32(); // thumb pix height 656 mData.getUInt32(); // image pix width 657 mData.getUInt32(); // image pix height 658 mData.getUInt32(); // image bit depth 659 mData.getUInt32(); // parent 660 uint16_t associationType = mData.getUInt16(); 661 uint32_t associationDesc = mData.getUInt32(); // association desc 662 mData.getUInt32(); // sequence number 663 MtpStringBuffer name, created, modified; 664 mData.getString(name); // file name 665 mData.getString(created); // date created 666 mData.getString(modified); // date modified 667 // keywords follow 668 669 LOGD("name: %s format: %04X\n", (const char *)name, format); 670 time_t modifiedTime; 671 if (!parseDateTime(modified, modifiedTime)) 672 modifiedTime = 0; 673 674 if (path[path.size() - 1] != '/') 675 path += "/"; 676 path += (const char *)name; 677 678 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 679 format, parent, storageID, mSendObjectFileSize, modifiedTime); 680 if (handle == kInvalidObjectHandle) { 681 return MTP_RESPONSE_GENERAL_ERROR; 682 } 683 684 if (format == MTP_FORMAT_ASSOCIATION) { 685 mode_t mask = umask(0); 686 int ret = mkdir((const char *)path, mDirectoryPermission); 687 umask(mask); 688 if (ret && ret != -EEXIST) 689 return MTP_RESPONSE_GENERAL_ERROR; 690 chown((const char *)path, getuid(), mFileGroup); 691 } else { 692 mSendObjectFilePath = path; 693 // save the handle for the SendObject call, which should follow 694 mSendObjectHandle = handle; 695 mSendObjectFormat = format; 696 } 697 698 mResponse.setParameter(1, storageID); 699 mResponse.setParameter(2, parent); 700 mResponse.setParameter(3, handle); 701 702 return MTP_RESPONSE_OK; 703} 704 705MtpResponseCode MtpServer::doSendObject() { 706 MtpResponseCode result = MTP_RESPONSE_OK; 707 mode_t mask; 708 int ret; 709 uint64_t actualSize = -1; 710 711 if (mSendObjectHandle == kInvalidObjectHandle) { 712 LOGE("Expected SendObjectInfo before SendObject"); 713 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 714 goto done; 715 } 716 717 // read the header 718 ret = mData.readDataHeader(mFD); 719 // FIXME - check for errors here. 720 721 // reset so we don't attempt to send this back 722 mData.reset(); 723 724 mtp_file_range mfr; 725 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 726 if (mfr.fd < 0) { 727 result = MTP_RESPONSE_GENERAL_ERROR; 728 goto done; 729 } 730 fchown(mfr.fd, getuid(), mFileGroup); 731 // set permissions 732 mask = umask(0); 733 fchmod(mfr.fd, mFilePermission); 734 umask(mask); 735 736 mfr.offset = 0; 737 mfr.length = mSendObjectFileSize; 738 739 LOGD("receiving %s\n", (const char *)mSendObjectFilePath); 740 // transfer the file 741 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 742 close(mfr.fd); 743 744 LOGV("MTP_RECEIVE_FILE returned %d", ret); 745 746 if (ret < 0) { 747 unlink(mSendObjectFilePath); 748 if (errno == ECANCELED) 749 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 750 else 751 result = MTP_RESPONSE_GENERAL_ERROR; 752 } else if (mSendObjectFileSize == 0xFFFFFFFF) { 753 // actual size is likely > 4 gig so stat the file to compute actual length 754 struct stat s; 755 if (lstat(mSendObjectFilePath, &s) == 0) { 756 actualSize = s.st_size; 757 LOGD("actualSize: %lld\n", actualSize); 758 } 759 } 760 761done: 762 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 763 actualSize, result == MTP_RESPONSE_OK); 764 mSendObjectHandle = kInvalidObjectHandle; 765 mSendObjectFormat = 0; 766 return result; 767} 768 769static void deleteRecursive(const char* path) { 770 char pathbuf[PATH_MAX]; 771 int pathLength = strlen(path); 772 if (pathLength >= sizeof(pathbuf) - 1) { 773 LOGE("path too long: %s\n", path); 774 } 775 strcpy(pathbuf, path); 776 if (pathbuf[pathLength - 1] != '/') { 777 pathbuf[pathLength++] = '/'; 778 } 779 char* fileSpot = pathbuf + pathLength; 780 int pathRemaining = sizeof(pathbuf) - pathLength - 1; 781 782 DIR* dir = opendir(path); 783 if (!dir) { 784 LOGE("opendir %s failed: %s", path, strerror(errno)); 785 return; 786 } 787 788 struct dirent* entry; 789 while ((entry = readdir(dir))) { 790 const char* name = entry->d_name; 791 792 // ignore "." and ".." 793 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 794 continue; 795 } 796 797 int nameLength = strlen(name); 798 if (nameLength > pathRemaining) { 799 LOGE("path %s/%s too long\n", path, name); 800 continue; 801 } 802 strcpy(fileSpot, name); 803 804 int type = entry->d_type; 805 if (entry->d_type == DT_DIR) { 806 deleteRecursive(pathbuf); 807 rmdir(pathbuf); 808 } else { 809 unlink(pathbuf); 810 } 811 } 812 closedir(dir); 813} 814 815static void deletePath(const char* path) { 816 struct stat statbuf; 817 if (stat(path, &statbuf) == 0) { 818 if (S_ISDIR(statbuf.st_mode)) { 819 deleteRecursive(path); 820 rmdir(path); 821 } else { 822 unlink(path); 823 } 824 } else { 825 LOGE("deletePath stat failed for %s: %s", path, strerror(errno)); 826 } 827} 828 829MtpResponseCode MtpServer::doDeleteObject() { 830 MtpObjectHandle handle = mRequest.getParameter(1); 831 MtpObjectFormat format = mRequest.getParameter(2); 832 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 833 // FIXME - implement deleting objects by format 834 835 MtpString filePath; 836 int64_t fileLength; 837 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength); 838 if (result == MTP_RESPONSE_OK) { 839 LOGV("deleting %s", (const char *)filePath); 840 deletePath((const char *)filePath); 841 return mDatabase->deleteFile(handle); 842 } else { 843 return result; 844 } 845} 846 847MtpResponseCode MtpServer::doGetObjectPropDesc() { 848 MtpObjectProperty propCode = mRequest.getParameter(1); 849 MtpObjectFormat format = mRequest.getParameter(2); 850 LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), 851 MtpDebug::getFormatCodeName(format)); 852 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); 853 if (!property) 854 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 855 property->write(mData); 856 delete property; 857 return MTP_RESPONSE_OK; 858} 859 860MtpResponseCode MtpServer::doGetDevicePropDesc() { 861 MtpDeviceProperty propCode = mRequest.getParameter(1); 862 LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); 863 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); 864 if (!property) 865 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 866 property->write(mData); 867 delete property; 868 return MTP_RESPONSE_OK; 869} 870 871} // namespace android 872