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