MtpServer.cpp revision 59d6ae5e339547fea8a350c1d855b52d5ac4f62c
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 LOGV("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 LOGV("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 LOGV("mEvent.write returned %d\n", ret); 213 } 214} 215 216void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 217 if (mSessionOpen) { 218 LOGV("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 LOGV("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 LOGV("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 LOGV("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 LOGV("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 LOGV("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 LOGV("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 LOGV("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 LOGV("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 704 // SendObject does not get sent for directories, so call endSendObject here instead 705 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK); 706 } else { 707 mSendObjectFilePath = path; 708 // save the handle for the SendObject call, which should follow 709 mSendObjectHandle = handle; 710 mSendObjectFormat = format; 711 } 712 713 mResponse.setParameter(1, storageID); 714 mResponse.setParameter(2, parent); 715 mResponse.setParameter(3, handle); 716 717 return MTP_RESPONSE_OK; 718} 719 720MtpResponseCode MtpServer::doSendObject() { 721 MtpResponseCode result = MTP_RESPONSE_OK; 722 mode_t mask; 723 int ret; 724 725 if (mSendObjectHandle == kInvalidObjectHandle) { 726 LOGE("Expected SendObjectInfo before SendObject"); 727 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 728 goto done; 729 } 730 731 // read the header 732 ret = mData.readDataHeader(mFD); 733 // FIXME - check for errors here. 734 735 // reset so we don't attempt to send this back 736 mData.reset(); 737 738 mtp_file_range mfr; 739 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 740 if (mfr.fd < 0) { 741 result = MTP_RESPONSE_GENERAL_ERROR; 742 goto done; 743 } 744 fchown(mfr.fd, getuid(), mFileGroup); 745 // set permissions 746 mask = umask(0); 747 fchmod(mfr.fd, mFilePermission); 748 umask(mask); 749 750 mfr.offset = 0; 751 mfr.length = mSendObjectFileSize; 752 753 LOGV("receiving %s\n", (const char *)mSendObjectFilePath); 754 // transfer the file 755 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 756 close(mfr.fd); 757 758 LOGV("MTP_RECEIVE_FILE returned %d", ret); 759 760 if (ret < 0) { 761 unlink(mSendObjectFilePath); 762 if (errno == ECANCELED) 763 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 764 else 765 result = MTP_RESPONSE_GENERAL_ERROR; 766 } 767 768done: 769 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 770 result == MTP_RESPONSE_OK); 771 mSendObjectHandle = kInvalidObjectHandle; 772 mSendObjectFormat = 0; 773 return result; 774} 775 776static void deleteRecursive(const char* path) { 777 char pathbuf[PATH_MAX]; 778 int pathLength = strlen(path); 779 if (pathLength >= sizeof(pathbuf) - 1) { 780 LOGE("path too long: %s\n", path); 781 } 782 strcpy(pathbuf, path); 783 if (pathbuf[pathLength - 1] != '/') { 784 pathbuf[pathLength++] = '/'; 785 } 786 char* fileSpot = pathbuf + pathLength; 787 int pathRemaining = sizeof(pathbuf) - pathLength - 1; 788 789 DIR* dir = opendir(path); 790 if (!dir) { 791 LOGE("opendir %s failed: %s", path, strerror(errno)); 792 return; 793 } 794 795 struct dirent* entry; 796 while ((entry = readdir(dir))) { 797 const char* name = entry->d_name; 798 799 // ignore "." and ".." 800 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 801 continue; 802 } 803 804 int nameLength = strlen(name); 805 if (nameLength > pathRemaining) { 806 LOGE("path %s/%s too long\n", path, name); 807 continue; 808 } 809 strcpy(fileSpot, name); 810 811 int type = entry->d_type; 812 if (entry->d_type == DT_DIR) { 813 deleteRecursive(pathbuf); 814 rmdir(pathbuf); 815 } else { 816 unlink(pathbuf); 817 } 818 } 819 closedir(dir); 820} 821 822static void deletePath(const char* path) { 823 struct stat statbuf; 824 if (stat(path, &statbuf) == 0) { 825 if (S_ISDIR(statbuf.st_mode)) { 826 deleteRecursive(path); 827 rmdir(path); 828 } else { 829 unlink(path); 830 } 831 } else { 832 LOGE("deletePath stat failed for %s: %s", path, strerror(errno)); 833 } 834} 835 836MtpResponseCode MtpServer::doDeleteObject() { 837 MtpObjectHandle handle = mRequest.getParameter(1); 838 MtpObjectFormat format = mRequest.getParameter(2); 839 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 840 // FIXME - implement deleting objects by format 841 842 MtpString filePath; 843 int64_t fileLength; 844 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format); 845 if (result == MTP_RESPONSE_OK) { 846 LOGV("deleting %s", (const char *)filePath); 847 deletePath((const char *)filePath); 848 return mDatabase->deleteFile(handle); 849 } else { 850 return result; 851 } 852} 853 854MtpResponseCode MtpServer::doGetObjectPropDesc() { 855 MtpObjectProperty propCode = mRequest.getParameter(1); 856 MtpObjectFormat format = mRequest.getParameter(2); 857 LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), 858 MtpDebug::getFormatCodeName(format)); 859 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); 860 if (!property) 861 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 862 property->write(mData); 863 delete property; 864 return MTP_RESPONSE_OK; 865} 866 867MtpResponseCode MtpServer::doGetDevicePropDesc() { 868 MtpDeviceProperty propCode = mRequest.getParameter(1); 869 LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); 870 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); 871 if (!property) 872 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 873 property->write(mData); 874 delete property; 875 return MTP_RESPONSE_OK; 876} 877 878} // namespace android 879