MtpServer.cpp revision 40ce1f262cc4edbc8b7c470830325466263acaec
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 // use uint32_t so we can support 0xFFFFFFFF 540 uint32_t format = mRequest.getParameter(2); 541 uint32_t property = mRequest.getParameter(3); 542 int groupCode = mRequest.getParameter(4); 543 int depth = mRequest.getParameter(5); 544 LOGD("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", 545 handle, MtpDebug::getFormatCodeName(format), 546 MtpDebug::getObjectPropCodeName(property), groupCode, depth); 547 548 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); 549} 550 551MtpResponseCode MtpServer::doGetObjectInfo() { 552 MtpObjectHandle handle = mRequest.getParameter(1); 553 return mDatabase->getObjectInfo(handle, mData); 554} 555 556MtpResponseCode MtpServer::doGetObject() { 557 MtpObjectHandle handle = mRequest.getParameter(1); 558 MtpString pathBuf; 559 int64_t fileLength; 560 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength); 561 if (result != MTP_RESPONSE_OK) 562 return result; 563 564 const char* filePath = (const char *)pathBuf; 565 mtp_file_range mfr; 566 mfr.fd = open(filePath, O_RDONLY); 567 if (mfr.fd < 0) { 568 return MTP_RESPONSE_GENERAL_ERROR; 569 } 570 mfr.offset = 0; 571 mfr.length = fileLength; 572 573 // send data header 574 mData.setOperationCode(mRequest.getOperationCode()); 575 mData.setTransactionID(mRequest.getTransactionID()); 576 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE); 577 578 // then transfer the file 579 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 580 close(mfr.fd); 581 if (ret < 0) { 582 if (errno == ECANCELED) 583 return MTP_RESPONSE_TRANSACTION_CANCELLED; 584 else 585 return MTP_RESPONSE_GENERAL_ERROR; 586 } 587 return MTP_RESPONSE_OK; 588} 589 590MtpResponseCode MtpServer::doGetPartialObject() { 591 MtpObjectHandle handle = mRequest.getParameter(1); 592 uint32_t offset = mRequest.getParameter(2); 593 uint32_t length = mRequest.getParameter(3); 594 MtpString pathBuf; 595 int64_t fileLength; 596 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength); 597 if (result != MTP_RESPONSE_OK) 598 return result; 599 if (offset + length > fileLength) 600 length = fileLength - offset; 601 602 const char* filePath = (const char *)pathBuf; 603 mtp_file_range mfr; 604 mfr.fd = open(filePath, O_RDONLY); 605 if (mfr.fd < 0) { 606 return MTP_RESPONSE_GENERAL_ERROR; 607 } 608 mfr.offset = offset; 609 mfr.length = length; 610 mResponse.setParameter(1, length); 611 612 // send data header 613 mData.setOperationCode(mRequest.getOperationCode()); 614 mData.setTransactionID(mRequest.getTransactionID()); 615 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE); 616 617 // then transfer the file 618 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 619 close(mfr.fd); 620 if (ret < 0) { 621 if (errno == ECANCELED) 622 return MTP_RESPONSE_TRANSACTION_CANCELLED; 623 else 624 return MTP_RESPONSE_GENERAL_ERROR; 625 } 626 return MTP_RESPONSE_OK; 627} 628 629MtpResponseCode MtpServer::doSendObjectInfo() { 630 MtpString path; 631 MtpStorageID storageID = mRequest.getParameter(1); 632 MtpStorage* storage = getStorage(storageID); 633 MtpObjectHandle parent = mRequest.getParameter(2); 634 if (!storage) 635 return MTP_RESPONSE_INVALID_STORAGE_ID; 636 637 // special case the root 638 if (parent == MTP_PARENT_ROOT) { 639 path = storage->getPath(); 640 parent = 0; 641 } else { 642 int64_t dummy; 643 int result = mDatabase->getObjectFilePath(parent, path, dummy); 644 if (result != MTP_RESPONSE_OK) 645 return result; 646 } 647 648 // read only the fields we need 649 mData.getUInt32(); // storage ID 650 MtpObjectFormat format = mData.getUInt16(); 651 mData.getUInt16(); // protection status 652 mSendObjectFileSize = mData.getUInt32(); 653 mData.getUInt16(); // thumb format 654 mData.getUInt32(); // thumb compressed size 655 mData.getUInt32(); // thumb pix width 656 mData.getUInt32(); // thumb pix height 657 mData.getUInt32(); // image pix width 658 mData.getUInt32(); // image pix height 659 mData.getUInt32(); // image bit depth 660 mData.getUInt32(); // parent 661 uint16_t associationType = mData.getUInt16(); 662 uint32_t associationDesc = mData.getUInt32(); // association desc 663 mData.getUInt32(); // sequence number 664 MtpStringBuffer name, created, modified; 665 mData.getString(name); // file name 666 mData.getString(created); // date created 667 mData.getString(modified); // date modified 668 // keywords follow 669 670 LOGD("name: %s format: %04X\n", (const char *)name, format); 671 time_t modifiedTime; 672 if (!parseDateTime(modified, modifiedTime)) 673 modifiedTime = 0; 674 675 if (path[path.size() - 1] != '/') 676 path += "/"; 677 path += (const char *)name; 678 679 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 680 format, parent, storageID, mSendObjectFileSize, modifiedTime); 681 if (handle == kInvalidObjectHandle) { 682 return MTP_RESPONSE_GENERAL_ERROR; 683 } 684 685 if (format == MTP_FORMAT_ASSOCIATION) { 686 mode_t mask = umask(0); 687 int ret = mkdir((const char *)path, mDirectoryPermission); 688 umask(mask); 689 if (ret && ret != -EEXIST) 690 return MTP_RESPONSE_GENERAL_ERROR; 691 chown((const char *)path, getuid(), mFileGroup); 692 } else { 693 mSendObjectFilePath = path; 694 // save the handle for the SendObject call, which should follow 695 mSendObjectHandle = handle; 696 mSendObjectFormat = format; 697 } 698 699 mResponse.setParameter(1, storageID); 700 mResponse.setParameter(2, parent); 701 mResponse.setParameter(3, handle); 702 703 return MTP_RESPONSE_OK; 704} 705 706MtpResponseCode MtpServer::doSendObject() { 707 MtpResponseCode result = MTP_RESPONSE_OK; 708 mode_t mask; 709 int ret; 710 uint64_t actualSize = -1; 711 712 if (mSendObjectHandle == kInvalidObjectHandle) { 713 LOGE("Expected SendObjectInfo before SendObject"); 714 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 715 goto done; 716 } 717 718 // read the header 719 ret = mData.readDataHeader(mFD); 720 // FIXME - check for errors here. 721 722 // reset so we don't attempt to send this back 723 mData.reset(); 724 725 mtp_file_range mfr; 726 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 727 if (mfr.fd < 0) { 728 result = MTP_RESPONSE_GENERAL_ERROR; 729 goto done; 730 } 731 fchown(mfr.fd, getuid(), mFileGroup); 732 // set permissions 733 mask = umask(0); 734 fchmod(mfr.fd, mFilePermission); 735 umask(mask); 736 737 mfr.offset = 0; 738 mfr.length = mSendObjectFileSize; 739 740 LOGD("receiving %s\n", (const char *)mSendObjectFilePath); 741 // transfer the file 742 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 743 close(mfr.fd); 744 745 LOGV("MTP_RECEIVE_FILE returned %d", ret); 746 747 if (ret < 0) { 748 unlink(mSendObjectFilePath); 749 if (errno == ECANCELED) 750 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 751 else 752 result = MTP_RESPONSE_GENERAL_ERROR; 753 } else if (mSendObjectFileSize == 0xFFFFFFFF) { 754 // actual size is likely > 4 gig so stat the file to compute actual length 755 struct stat s; 756 if (lstat(mSendObjectFilePath, &s) == 0) { 757 actualSize = s.st_size; 758 LOGD("actualSize: %lld\n", actualSize); 759 } 760 } 761 762done: 763 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 764 actualSize, result == MTP_RESPONSE_OK); 765 mSendObjectHandle = kInvalidObjectHandle; 766 mSendObjectFormat = 0; 767 return result; 768} 769 770static void deleteRecursive(const char* path) { 771 char pathbuf[PATH_MAX]; 772 int pathLength = strlen(path); 773 if (pathLength >= sizeof(pathbuf) - 1) { 774 LOGE("path too long: %s\n", path); 775 } 776 strcpy(pathbuf, path); 777 if (pathbuf[pathLength - 1] != '/') { 778 pathbuf[pathLength++] = '/'; 779 } 780 char* fileSpot = pathbuf + pathLength; 781 int pathRemaining = sizeof(pathbuf) - pathLength - 1; 782 783 DIR* dir = opendir(path); 784 if (!dir) { 785 LOGE("opendir %s failed: %s", path, strerror(errno)); 786 return; 787 } 788 789 struct dirent* entry; 790 while ((entry = readdir(dir))) { 791 const char* name = entry->d_name; 792 793 // ignore "." and ".." 794 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 795 continue; 796 } 797 798 int nameLength = strlen(name); 799 if (nameLength > pathRemaining) { 800 LOGE("path %s/%s too long\n", path, name); 801 continue; 802 } 803 strcpy(fileSpot, name); 804 805 int type = entry->d_type; 806 if (entry->d_type == DT_DIR) { 807 deleteRecursive(pathbuf); 808 rmdir(pathbuf); 809 } else { 810 unlink(pathbuf); 811 } 812 } 813 closedir(dir); 814} 815 816static void deletePath(const char* path) { 817 struct stat statbuf; 818 if (stat(path, &statbuf) == 0) { 819 if (S_ISDIR(statbuf.st_mode)) { 820 deleteRecursive(path); 821 rmdir(path); 822 } else { 823 unlink(path); 824 } 825 } else { 826 LOGE("deletePath stat failed for %s: %s", path, strerror(errno)); 827 } 828} 829 830MtpResponseCode MtpServer::doDeleteObject() { 831 MtpObjectHandle handle = mRequest.getParameter(1); 832 MtpObjectFormat format = mRequest.getParameter(2); 833 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 834 // FIXME - implement deleting objects by format 835 836 MtpString filePath; 837 int64_t fileLength; 838 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength); 839 if (result == MTP_RESPONSE_OK) { 840 LOGV("deleting %s", (const char *)filePath); 841 deletePath((const char *)filePath); 842 return mDatabase->deleteFile(handle); 843 } else { 844 return result; 845 } 846} 847 848MtpResponseCode MtpServer::doGetObjectPropDesc() { 849 MtpObjectProperty propCode = mRequest.getParameter(1); 850 MtpObjectFormat format = mRequest.getParameter(2); 851 LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), 852 MtpDebug::getFormatCodeName(format)); 853 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); 854 if (!property) 855 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 856 property->write(mData); 857 delete property; 858 return MTP_RESPONSE_OK; 859} 860 861MtpResponseCode MtpServer::doGetDevicePropDesc() { 862 MtpDeviceProperty propCode = mRequest.getParameter(1); 863 LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); 864 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); 865 if (!property) 866 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 867 property->write(mData); 868 delete property; 869 return MTP_RESPONSE_OK; 870} 871 872} // namespace android 873