MtpServer.cpp revision 0fa848d780cf990a2860637f40432d28594c85a3
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 "MtpObjectInfo.h" 34#include "MtpProperty.h" 35#include "MtpServer.h" 36#include "MtpStorage.h" 37#include "MtpStringBuffer.h" 38 39#include <linux/usb/f_mtp.h> 40 41namespace android { 42 43static const MtpOperationCode kSupportedOperationCodes[] = { 44 MTP_OPERATION_GET_DEVICE_INFO, 45 MTP_OPERATION_OPEN_SESSION, 46 MTP_OPERATION_CLOSE_SESSION, 47 MTP_OPERATION_GET_STORAGE_IDS, 48 MTP_OPERATION_GET_STORAGE_INFO, 49 MTP_OPERATION_GET_NUM_OBJECTS, 50 MTP_OPERATION_GET_OBJECT_HANDLES, 51 MTP_OPERATION_GET_OBJECT_INFO, 52 MTP_OPERATION_GET_OBJECT, 53 MTP_OPERATION_GET_THUMB, 54 MTP_OPERATION_DELETE_OBJECT, 55 MTP_OPERATION_SEND_OBJECT_INFO, 56 MTP_OPERATION_SEND_OBJECT, 57// MTP_OPERATION_INITIATE_CAPTURE, 58// MTP_OPERATION_FORMAT_STORE, 59// MTP_OPERATION_RESET_DEVICE, 60// MTP_OPERATION_SELF_TEST, 61// MTP_OPERATION_SET_OBJECT_PROTECTION, 62// MTP_OPERATION_POWER_DOWN, 63 MTP_OPERATION_GET_DEVICE_PROP_DESC, 64 MTP_OPERATION_GET_DEVICE_PROP_VALUE, 65 MTP_OPERATION_SET_DEVICE_PROP_VALUE, 66 MTP_OPERATION_RESET_DEVICE_PROP_VALUE, 67// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, 68// MTP_OPERATION_MOVE_OBJECT, 69// MTP_OPERATION_COPY_OBJECT, 70 MTP_OPERATION_GET_PARTIAL_OBJECT, 71// MTP_OPERATION_INITIATE_OPEN_CAPTURE, 72 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, 73 MTP_OPERATION_GET_OBJECT_PROP_DESC, 74 MTP_OPERATION_GET_OBJECT_PROP_VALUE, 75 MTP_OPERATION_SET_OBJECT_PROP_VALUE, 76 MTP_OPERATION_GET_OBJECT_PROP_LIST, 77// MTP_OPERATION_SET_OBJECT_PROP_LIST, 78// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, 79// MTP_OPERATION_SEND_OBJECT_PROP_LIST, 80 MTP_OPERATION_GET_OBJECT_REFERENCES, 81 MTP_OPERATION_SET_OBJECT_REFERENCES, 82// MTP_OPERATION_SKIP, 83 // Android extension for direct file IO 84 MTP_OPERATION_GET_PARTIAL_OBJECT_64, 85 MTP_OPERATION_SEND_PARTIAL_OBJECT, 86 MTP_OPERATION_TRUNCATE_OBJECT, 87 MTP_OPERATION_BEGIN_EDIT_OBJECT, 88 MTP_OPERATION_END_EDIT_OBJECT, 89}; 90 91static const MtpEventCode kSupportedEventCodes[] = { 92 MTP_EVENT_OBJECT_ADDED, 93 MTP_EVENT_OBJECT_REMOVED, 94 MTP_EVENT_STORE_ADDED, 95 MTP_EVENT_STORE_REMOVED, 96 MTP_EVENT_DEVICE_PROP_CHANGED, 97}; 98 99MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp, 100 int fileGroup, int filePerm, int directoryPerm) 101 : mFD(fd), 102 mDatabase(database), 103 mPtp(ptp), 104 mFileGroup(fileGroup), 105 mFilePermission(filePerm), 106 mDirectoryPermission(directoryPerm), 107 mSessionID(0), 108 mSessionOpen(false), 109 mSendObjectHandle(kInvalidObjectHandle), 110 mSendObjectFormat(0), 111 mSendObjectFileSize(0) 112{ 113} 114 115MtpServer::~MtpServer() { 116} 117 118void MtpServer::addStorage(MtpStorage* storage) { 119 Mutex::Autolock autoLock(mMutex); 120 121 mStorages.push(storage); 122 sendStoreAdded(storage->getStorageID()); 123} 124 125void MtpServer::removeStorage(MtpStorage* storage) { 126 Mutex::Autolock autoLock(mMutex); 127 128 for (int i = 0; i < mStorages.size(); i++) { 129 if (mStorages[i] == storage) { 130 mStorages.removeAt(i); 131 sendStoreRemoved(storage->getStorageID()); 132 break; 133 } 134 } 135} 136 137MtpStorage* MtpServer::getStorage(MtpStorageID id) { 138 if (id == 0) 139 return mStorages[0]; 140 for (int i = 0; i < mStorages.size(); i++) { 141 MtpStorage* storage = mStorages[i]; 142 if (storage->getStorageID() == id) 143 return storage; 144 } 145 return NULL; 146} 147 148bool MtpServer::hasStorage(MtpStorageID id) { 149 if (id == 0 || id == 0xFFFFFFFF) 150 return mStorages.size() > 0; 151 return (getStorage(id) != NULL); 152} 153 154void MtpServer::run() { 155 int fd = mFD; 156 157 ALOGV("MtpServer::run fd: %d\n", fd); 158 159 while (1) { 160 int ret = mRequest.read(fd); 161 if (ret < 0) { 162 ALOGV("request read returned %d, errno: %d", ret, errno); 163 if (errno == ECANCELED) { 164 // return to top of loop and wait for next command 165 continue; 166 } 167 break; 168 } 169 MtpOperationCode operation = mRequest.getOperationCode(); 170 MtpTransactionID transaction = mRequest.getTransactionID(); 171 172 ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation)); 173 mRequest.dump(); 174 175 // FIXME need to generalize this 176 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO 177 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES 178 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE 179 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE); 180 if (dataIn) { 181 int ret = mData.read(fd); 182 if (ret < 0) { 183 ALOGE("data read returned %d, errno: %d", ret, errno); 184 if (errno == ECANCELED) { 185 // return to top of loop and wait for next command 186 continue; 187 } 188 break; 189 } 190 ALOGV("received data:"); 191 mData.dump(); 192 } else { 193 mData.reset(); 194 } 195 196 if (handleRequest()) { 197 if (!dataIn && mData.hasData()) { 198 mData.setOperationCode(operation); 199 mData.setTransactionID(transaction); 200 ALOGV("sending data:"); 201 mData.dump(); 202 ret = mData.write(fd); 203 if (ret < 0) { 204 ALOGE("request write returned %d, errno: %d", ret, errno); 205 if (errno == ECANCELED) { 206 // return to top of loop and wait for next command 207 continue; 208 } 209 break; 210 } 211 } 212 213 mResponse.setTransactionID(transaction); 214 ALOGV("sending response %04X", mResponse.getResponseCode()); 215 ret = mResponse.write(fd); 216 mResponse.dump(); 217 if (ret < 0) { 218 ALOGE("request write returned %d, errno: %d", ret, errno); 219 if (errno == ECANCELED) { 220 // return to top of loop and wait for next command 221 continue; 222 } 223 break; 224 } 225 } else { 226 ALOGV("skipping response\n"); 227 } 228 } 229 230 // commit any open edits 231 int count = mObjectEditList.size(); 232 for (int i = 0; i < count; i++) { 233 ObjectEdit* edit = mObjectEditList[i]; 234 commitEdit(edit); 235 delete edit; 236 } 237 mObjectEditList.clear(); 238 239 if (mSessionOpen) 240 mDatabase->sessionEnded(); 241 close(fd); 242 mFD = -1; 243} 244 245void MtpServer::sendObjectAdded(MtpObjectHandle handle) { 246 ALOGV("sendObjectAdded %d\n", handle); 247 sendEvent(MTP_EVENT_OBJECT_ADDED, handle); 248} 249 250void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 251 ALOGV("sendObjectRemoved %d\n", handle); 252 sendEvent(MTP_EVENT_OBJECT_REMOVED, handle); 253} 254 255void MtpServer::sendStoreAdded(MtpStorageID id) { 256 ALOGV("sendStoreAdded %08X\n", id); 257 sendEvent(MTP_EVENT_STORE_ADDED, id); 258} 259 260void MtpServer::sendStoreRemoved(MtpStorageID id) { 261 ALOGV("sendStoreRemoved %08X\n", id); 262 sendEvent(MTP_EVENT_STORE_REMOVED, id); 263} 264 265void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) { 266 ALOGV("sendDevicePropertyChanged %d\n", property); 267 sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property); 268} 269 270void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) { 271 if (mSessionOpen) { 272 mEvent.setEventCode(code); 273 mEvent.setTransactionID(mRequest.getTransactionID()); 274 mEvent.setParameter(1, param1); 275 int ret = mEvent.write(mFD); 276 ALOGV("mEvent.write returned %d\n", ret); 277 } 278} 279 280void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path, 281 uint64_t size, MtpObjectFormat format, int fd) { 282 ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd); 283 mObjectEditList.add(edit); 284} 285 286MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) { 287 int count = mObjectEditList.size(); 288 for (int i = 0; i < count; i++) { 289 ObjectEdit* edit = mObjectEditList[i]; 290 if (edit->mHandle == handle) return edit; 291 } 292 return NULL; 293} 294 295void MtpServer::removeEditObject(MtpObjectHandle handle) { 296 int count = mObjectEditList.size(); 297 for (int i = 0; i < count; i++) { 298 ObjectEdit* edit = mObjectEditList[i]; 299 if (edit->mHandle == handle) { 300 delete edit; 301 mObjectEditList.removeAt(i); 302 return; 303 } 304 } 305 ALOGE("ObjectEdit not found in removeEditObject"); 306} 307 308void MtpServer::commitEdit(ObjectEdit* edit) { 309 mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true); 310} 311 312 313bool MtpServer::handleRequest() { 314 Mutex::Autolock autoLock(mMutex); 315 316 MtpOperationCode operation = mRequest.getOperationCode(); 317 MtpResponseCode response; 318 319 mResponse.reset(); 320 321 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 322 // FIXME - need to delete mSendObjectHandle from the database 323 ALOGE("expected SendObject after SendObjectInfo"); 324 mSendObjectHandle = kInvalidObjectHandle; 325 } 326 327 switch (operation) { 328 case MTP_OPERATION_GET_DEVICE_INFO: 329 response = doGetDeviceInfo(); 330 break; 331 case MTP_OPERATION_OPEN_SESSION: 332 response = doOpenSession(); 333 break; 334 case MTP_OPERATION_CLOSE_SESSION: 335 response = doCloseSession(); 336 break; 337 case MTP_OPERATION_GET_STORAGE_IDS: 338 response = doGetStorageIDs(); 339 break; 340 case MTP_OPERATION_GET_STORAGE_INFO: 341 response = doGetStorageInfo(); 342 break; 343 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 344 response = doGetObjectPropsSupported(); 345 break; 346 case MTP_OPERATION_GET_OBJECT_HANDLES: 347 response = doGetObjectHandles(); 348 break; 349 case MTP_OPERATION_GET_NUM_OBJECTS: 350 response = doGetNumObjects(); 351 break; 352 case MTP_OPERATION_GET_OBJECT_REFERENCES: 353 response = doGetObjectReferences(); 354 break; 355 case MTP_OPERATION_SET_OBJECT_REFERENCES: 356 response = doSetObjectReferences(); 357 break; 358 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 359 response = doGetObjectPropValue(); 360 break; 361 case MTP_OPERATION_SET_OBJECT_PROP_VALUE: 362 response = doSetObjectPropValue(); 363 break; 364 case MTP_OPERATION_GET_DEVICE_PROP_VALUE: 365 response = doGetDevicePropValue(); 366 break; 367 case MTP_OPERATION_SET_DEVICE_PROP_VALUE: 368 response = doSetDevicePropValue(); 369 break; 370 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: 371 response = doResetDevicePropValue(); 372 break; 373 case MTP_OPERATION_GET_OBJECT_PROP_LIST: 374 response = doGetObjectPropList(); 375 break; 376 case MTP_OPERATION_GET_OBJECT_INFO: 377 response = doGetObjectInfo(); 378 break; 379 case MTP_OPERATION_GET_OBJECT: 380 response = doGetObject(); 381 break; 382 case MTP_OPERATION_GET_THUMB: 383 response = doGetThumb(); 384 break; 385 case MTP_OPERATION_GET_PARTIAL_OBJECT: 386 case MTP_OPERATION_GET_PARTIAL_OBJECT_64: 387 response = doGetPartialObject(operation); 388 break; 389 case MTP_OPERATION_SEND_OBJECT_INFO: 390 response = doSendObjectInfo(); 391 break; 392 case MTP_OPERATION_SEND_OBJECT: 393 response = doSendObject(); 394 break; 395 case MTP_OPERATION_DELETE_OBJECT: 396 response = doDeleteObject(); 397 break; 398 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 399 response = doGetObjectPropDesc(); 400 break; 401 case MTP_OPERATION_GET_DEVICE_PROP_DESC: 402 response = doGetDevicePropDesc(); 403 break; 404 case MTP_OPERATION_SEND_PARTIAL_OBJECT: 405 response = doSendPartialObject(); 406 break; 407 case MTP_OPERATION_TRUNCATE_OBJECT: 408 response = doTruncateObject(); 409 break; 410 case MTP_OPERATION_BEGIN_EDIT_OBJECT: 411 response = doBeginEditObject(); 412 break; 413 case MTP_OPERATION_END_EDIT_OBJECT: 414 response = doEndEditObject(); 415 break; 416 default: 417 ALOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); 418 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 419 break; 420 } 421 422 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 423 return false; 424 mResponse.setResponseCode(response); 425 return true; 426} 427 428MtpResponseCode MtpServer::doGetDeviceInfo() { 429 MtpStringBuffer string; 430 char prop_value[PROPERTY_VALUE_MAX]; 431 432 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats(); 433 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats(); 434 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties(); 435 436 // fill in device info 437 mData.putUInt16(MTP_STANDARD_VERSION); 438 if (mPtp) { 439 mData.putUInt32(0); 440 } else { 441 // MTP Vendor Extension ID 442 mData.putUInt32(6); 443 } 444 mData.putUInt16(MTP_STANDARD_VERSION); 445 if (mPtp) { 446 // no extensions 447 string.set(""); 448 } else { 449 // MTP extensions 450 string.set("microsoft.com: 1.0; android.com: 1.0;"); 451 } 452 mData.putString(string); // MTP Extensions 453 mData.putUInt16(0); //Functional Mode 454 mData.putAUInt16(kSupportedOperationCodes, 455 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 456 mData.putAUInt16(kSupportedEventCodes, 457 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported 458 mData.putAUInt16(deviceProperties); // Device Properties Supported 459 mData.putAUInt16(captureFormats); // Capture Formats 460 mData.putAUInt16(playbackFormats); // Playback Formats 461 462 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer"); 463 string.set(prop_value); 464 mData.putString(string); // Manufacturer 465 466 property_get("ro.product.model", prop_value, "MTP Device"); 467 string.set(prop_value); 468 mData.putString(string); // Model 469 string.set("1.0"); 470 mData.putString(string); // Device Version 471 472 property_get("ro.serialno", prop_value, "????????"); 473 string.set(prop_value); 474 mData.putString(string); // Serial Number 475 476 delete playbackFormats; 477 delete captureFormats; 478 delete deviceProperties; 479 480 return MTP_RESPONSE_OK; 481} 482 483MtpResponseCode MtpServer::doOpenSession() { 484 if (mSessionOpen) { 485 mResponse.setParameter(1, mSessionID); 486 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 487 } 488 mSessionID = mRequest.getParameter(1); 489 mSessionOpen = true; 490 491 mDatabase->sessionStarted(); 492 493 return MTP_RESPONSE_OK; 494} 495 496MtpResponseCode MtpServer::doCloseSession() { 497 if (!mSessionOpen) 498 return MTP_RESPONSE_SESSION_NOT_OPEN; 499 mSessionID = 0; 500 mSessionOpen = false; 501 mDatabase->sessionEnded(); 502 return MTP_RESPONSE_OK; 503} 504 505MtpResponseCode MtpServer::doGetStorageIDs() { 506 if (!mSessionOpen) 507 return MTP_RESPONSE_SESSION_NOT_OPEN; 508 509 int count = mStorages.size(); 510 mData.putUInt32(count); 511 for (int i = 0; i < count; i++) 512 mData.putUInt32(mStorages[i]->getStorageID()); 513 514 return MTP_RESPONSE_OK; 515} 516 517MtpResponseCode MtpServer::doGetStorageInfo() { 518 MtpStringBuffer string; 519 520 if (!mSessionOpen) 521 return MTP_RESPONSE_SESSION_NOT_OPEN; 522 MtpStorageID id = mRequest.getParameter(1); 523 MtpStorage* storage = getStorage(id); 524 if (!storage) 525 return MTP_RESPONSE_INVALID_STORAGE_ID; 526 527 mData.putUInt16(storage->getType()); 528 mData.putUInt16(storage->getFileSystemType()); 529 mData.putUInt16(storage->getAccessCapability()); 530 mData.putUInt64(storage->getMaxCapacity()); 531 mData.putUInt64(storage->getFreeSpace()); 532 mData.putUInt32(1024*1024*1024); // Free Space in Objects 533 string.set(storage->getDescription()); 534 mData.putString(string); 535 mData.putEmptyString(); // Volume Identifier 536 537 return MTP_RESPONSE_OK; 538} 539 540MtpResponseCode MtpServer::doGetObjectPropsSupported() { 541 if (!mSessionOpen) 542 return MTP_RESPONSE_SESSION_NOT_OPEN; 543 MtpObjectFormat format = mRequest.getParameter(1); 544 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format); 545 mData.putAUInt16(properties); 546 delete properties; 547 return MTP_RESPONSE_OK; 548} 549 550MtpResponseCode MtpServer::doGetObjectHandles() { 551 if (!mSessionOpen) 552 return MTP_RESPONSE_SESSION_NOT_OPEN; 553 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 554 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 555 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 556 // 0x00000000 for all objects 557 558 if (!hasStorage(storageID)) 559 return MTP_RESPONSE_INVALID_STORAGE_ID; 560 561 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 562 mData.putAUInt32(handles); 563 delete handles; 564 return MTP_RESPONSE_OK; 565} 566 567MtpResponseCode MtpServer::doGetNumObjects() { 568 if (!mSessionOpen) 569 return MTP_RESPONSE_SESSION_NOT_OPEN; 570 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 571 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 572 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 573 // 0x00000000 for all objects 574 if (!hasStorage(storageID)) 575 return MTP_RESPONSE_INVALID_STORAGE_ID; 576 577 int count = mDatabase->getNumObjects(storageID, format, parent); 578 if (count >= 0) { 579 mResponse.setParameter(1, count); 580 return MTP_RESPONSE_OK; 581 } else { 582 mResponse.setParameter(1, 0); 583 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 584 } 585} 586 587MtpResponseCode MtpServer::doGetObjectReferences() { 588 if (!mSessionOpen) 589 return MTP_RESPONSE_SESSION_NOT_OPEN; 590 if (!hasStorage()) 591 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 592 MtpObjectHandle handle = mRequest.getParameter(1); 593 594 // FIXME - check for invalid object handle 595 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle); 596 if (handles) { 597 mData.putAUInt32(handles); 598 delete handles; 599 } else { 600 mData.putEmptyArray(); 601 } 602 return MTP_RESPONSE_OK; 603} 604 605MtpResponseCode MtpServer::doSetObjectReferences() { 606 if (!mSessionOpen) 607 return MTP_RESPONSE_SESSION_NOT_OPEN; 608 if (!hasStorage()) 609 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 610 MtpStorageID handle = mRequest.getParameter(1); 611 612 MtpObjectHandleList* references = mData.getAUInt32(); 613 MtpResponseCode result = mDatabase->setObjectReferences(handle, references); 614 delete references; 615 return result; 616} 617 618MtpResponseCode MtpServer::doGetObjectPropValue() { 619 if (!hasStorage()) 620 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 621 MtpObjectHandle handle = mRequest.getParameter(1); 622 MtpObjectProperty property = mRequest.getParameter(2); 623 ALOGV("GetObjectPropValue %d %s\n", handle, 624 MtpDebug::getObjectPropCodeName(property)); 625 626 return mDatabase->getObjectPropertyValue(handle, property, mData); 627} 628 629MtpResponseCode MtpServer::doSetObjectPropValue() { 630 if (!hasStorage()) 631 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 632 MtpObjectHandle handle = mRequest.getParameter(1); 633 MtpObjectProperty property = mRequest.getParameter(2); 634 ALOGV("SetObjectPropValue %d %s\n", handle, 635 MtpDebug::getObjectPropCodeName(property)); 636 637 return mDatabase->setObjectPropertyValue(handle, property, mData); 638} 639 640MtpResponseCode MtpServer::doGetDevicePropValue() { 641 MtpDeviceProperty property = mRequest.getParameter(1); 642 ALOGV("GetDevicePropValue %s\n", 643 MtpDebug::getDevicePropCodeName(property)); 644 645 return mDatabase->getDevicePropertyValue(property, mData); 646} 647 648MtpResponseCode MtpServer::doSetDevicePropValue() { 649 MtpDeviceProperty property = mRequest.getParameter(1); 650 ALOGV("SetDevicePropValue %s\n", 651 MtpDebug::getDevicePropCodeName(property)); 652 653 return mDatabase->setDevicePropertyValue(property, mData); 654} 655 656MtpResponseCode MtpServer::doResetDevicePropValue() { 657 MtpDeviceProperty property = mRequest.getParameter(1); 658 ALOGV("ResetDevicePropValue %s\n", 659 MtpDebug::getDevicePropCodeName(property)); 660 661 return mDatabase->resetDeviceProperty(property); 662} 663 664MtpResponseCode MtpServer::doGetObjectPropList() { 665 if (!hasStorage()) 666 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 667 668 MtpObjectHandle handle = mRequest.getParameter(1); 669 // use uint32_t so we can support 0xFFFFFFFF 670 uint32_t format = mRequest.getParameter(2); 671 uint32_t property = mRequest.getParameter(3); 672 int groupCode = mRequest.getParameter(4); 673 int depth = mRequest.getParameter(5); 674 ALOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", 675 handle, MtpDebug::getFormatCodeName(format), 676 MtpDebug::getObjectPropCodeName(property), groupCode, depth); 677 678 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); 679} 680 681MtpResponseCode MtpServer::doGetObjectInfo() { 682 if (!hasStorage()) 683 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 684 MtpObjectHandle handle = mRequest.getParameter(1); 685 MtpObjectInfo info(handle); 686 MtpResponseCode result = mDatabase->getObjectInfo(handle, info); 687 if (result == MTP_RESPONSE_OK) { 688 char date[20]; 689 690 mData.putUInt32(info.mStorageID); 691 mData.putUInt16(info.mFormat); 692 mData.putUInt16(info.mProtectionStatus); 693 694 // if object is being edited the database size may be out of date 695 uint32_t size = info.mCompressedSize; 696 ObjectEdit* edit = getEditObject(handle); 697 if (edit) 698 size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize); 699 mData.putUInt32(size); 700 701 mData.putUInt16(info.mThumbFormat); 702 mData.putUInt32(info.mThumbCompressedSize); 703 mData.putUInt32(info.mThumbPixWidth); 704 mData.putUInt32(info.mThumbPixHeight); 705 mData.putUInt32(info.mImagePixWidth); 706 mData.putUInt32(info.mImagePixHeight); 707 mData.putUInt32(info.mImagePixDepth); 708 mData.putUInt32(info.mParent); 709 mData.putUInt16(info.mAssociationType); 710 mData.putUInt32(info.mAssociationDesc); 711 mData.putUInt32(info.mSequenceNumber); 712 mData.putString(info.mName); 713 formatDateTime(info.mDateCreated, date, sizeof(date)); 714 mData.putString(date); // date created 715 formatDateTime(info.mDateModified, date, sizeof(date)); 716 mData.putString(date); // date modified 717 mData.putEmptyString(); // keywords 718 } 719 return result; 720} 721 722MtpResponseCode MtpServer::doGetObject() { 723 if (!hasStorage()) 724 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 725 MtpObjectHandle handle = mRequest.getParameter(1); 726 MtpString pathBuf; 727 int64_t fileLength; 728 MtpObjectFormat format; 729 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 730 if (result != MTP_RESPONSE_OK) 731 return result; 732 733 const char* filePath = (const char *)pathBuf; 734 mtp_file_range mfr; 735 mfr.fd = open(filePath, O_RDONLY); 736 if (mfr.fd < 0) { 737 return MTP_RESPONSE_GENERAL_ERROR; 738 } 739 mfr.offset = 0; 740 mfr.length = fileLength; 741 mfr.command = mRequest.getOperationCode(); 742 mfr.transaction_id = mRequest.getTransactionID(); 743 744 // then transfer the file 745 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr); 746 ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret); 747 close(mfr.fd); 748 if (ret < 0) { 749 if (errno == ECANCELED) 750 return MTP_RESPONSE_TRANSACTION_CANCELLED; 751 else 752 return MTP_RESPONSE_GENERAL_ERROR; 753 } 754 return MTP_RESPONSE_OK; 755} 756 757MtpResponseCode MtpServer::doGetThumb() { 758 MtpObjectHandle handle = mRequest.getParameter(1); 759 size_t thumbSize; 760 void* thumb = mDatabase->getThumbnail(handle, thumbSize); 761 if (thumb) { 762 // send data 763 mData.setOperationCode(mRequest.getOperationCode()); 764 mData.setTransactionID(mRequest.getTransactionID()); 765 mData.writeData(mFD, thumb, thumbSize); 766 free(thumb); 767 return MTP_RESPONSE_OK; 768 } else { 769 return MTP_RESPONSE_GENERAL_ERROR; 770 } 771} 772 773MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) { 774 if (!hasStorage()) 775 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 776 MtpObjectHandle handle = mRequest.getParameter(1); 777 uint64_t offset; 778 uint32_t length; 779 offset = mRequest.getParameter(2); 780 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) { 781 // android extension with 64 bit offset 782 uint64_t offset2 = mRequest.getParameter(3); 783 offset = offset | (offset2 << 32); 784 length = mRequest.getParameter(4); 785 } else { 786 // standard GetPartialObject 787 length = mRequest.getParameter(3); 788 } 789 MtpString pathBuf; 790 int64_t fileLength; 791 MtpObjectFormat format; 792 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 793 if (result != MTP_RESPONSE_OK) 794 return result; 795 if (offset + length > fileLength) 796 length = fileLength - offset; 797 798 const char* filePath = (const char *)pathBuf; 799 mtp_file_range mfr; 800 mfr.fd = open(filePath, O_RDONLY); 801 if (mfr.fd < 0) { 802 return MTP_RESPONSE_GENERAL_ERROR; 803 } 804 mfr.offset = offset; 805 mfr.length = length; 806 mfr.command = mRequest.getOperationCode(); 807 mfr.transaction_id = mRequest.getTransactionID(); 808 mResponse.setParameter(1, length); 809 810 // transfer the file 811 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr); 812 ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret); 813 close(mfr.fd); 814 if (ret < 0) { 815 if (errno == ECANCELED) 816 return MTP_RESPONSE_TRANSACTION_CANCELLED; 817 else 818 return MTP_RESPONSE_GENERAL_ERROR; 819 } 820 return MTP_RESPONSE_OK; 821} 822 823MtpResponseCode MtpServer::doSendObjectInfo() { 824 MtpString path; 825 MtpStorageID storageID = mRequest.getParameter(1); 826 MtpStorage* storage = getStorage(storageID); 827 MtpObjectHandle parent = mRequest.getParameter(2); 828 if (!storage) 829 return MTP_RESPONSE_INVALID_STORAGE_ID; 830 831 // special case the root 832 if (parent == MTP_PARENT_ROOT) { 833 path = storage->getPath(); 834 parent = 0; 835 } else { 836 int64_t length; 837 MtpObjectFormat format; 838 int result = mDatabase->getObjectFilePath(parent, path, length, format); 839 if (result != MTP_RESPONSE_OK) 840 return result; 841 if (format != MTP_FORMAT_ASSOCIATION) 842 return MTP_RESPONSE_INVALID_PARENT_OBJECT; 843 } 844 845 // read only the fields we need 846 mData.getUInt32(); // storage ID 847 MtpObjectFormat format = mData.getUInt16(); 848 mData.getUInt16(); // protection status 849 mSendObjectFileSize = mData.getUInt32(); 850 mData.getUInt16(); // thumb format 851 mData.getUInt32(); // thumb compressed size 852 mData.getUInt32(); // thumb pix width 853 mData.getUInt32(); // thumb pix height 854 mData.getUInt32(); // image pix width 855 mData.getUInt32(); // image pix height 856 mData.getUInt32(); // image bit depth 857 mData.getUInt32(); // parent 858 uint16_t associationType = mData.getUInt16(); 859 uint32_t associationDesc = mData.getUInt32(); // association desc 860 mData.getUInt32(); // sequence number 861 MtpStringBuffer name, created, modified; 862 mData.getString(name); // file name 863 mData.getString(created); // date created 864 mData.getString(modified); // date modified 865 // keywords follow 866 867 ALOGV("name: %s format: %04X\n", (const char *)name, format); 868 time_t modifiedTime; 869 if (!parseDateTime(modified, modifiedTime)) 870 modifiedTime = 0; 871 872 if (path[path.size() - 1] != '/') 873 path += "/"; 874 path += (const char *)name; 875 876 // check space first 877 if (mSendObjectFileSize > storage->getFreeSpace()) 878 return MTP_RESPONSE_STORAGE_FULL; 879 uint64_t maxFileSize = storage->getMaxFileSize(); 880 // check storage max file size 881 if (maxFileSize != 0) { 882 // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size 883 // is >= 0xFFFFFFFF 884 if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF) 885 return MTP_RESPONSE_OBJECT_TOO_LARGE; 886 } 887 888 ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID); 889 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 890 format, parent, storageID, mSendObjectFileSize, modifiedTime); 891 if (handle == kInvalidObjectHandle) { 892 return MTP_RESPONSE_GENERAL_ERROR; 893 } 894 895 if (format == MTP_FORMAT_ASSOCIATION) { 896 mode_t mask = umask(0); 897 int ret = mkdir((const char *)path, mDirectoryPermission); 898 umask(mask); 899 if (ret && ret != -EEXIST) 900 return MTP_RESPONSE_GENERAL_ERROR; 901 chown((const char *)path, getuid(), mFileGroup); 902 903 // SendObject does not get sent for directories, so call endSendObject here instead 904 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK); 905 } else { 906 mSendObjectFilePath = path; 907 // save the handle for the SendObject call, which should follow 908 mSendObjectHandle = handle; 909 mSendObjectFormat = format; 910 } 911 912 mResponse.setParameter(1, storageID); 913 mResponse.setParameter(2, parent); 914 mResponse.setParameter(3, handle); 915 916 return MTP_RESPONSE_OK; 917} 918 919MtpResponseCode MtpServer::doSendObject() { 920 if (!hasStorage()) 921 return MTP_RESPONSE_GENERAL_ERROR; 922 MtpResponseCode result = MTP_RESPONSE_OK; 923 mode_t mask; 924 int ret, initialData; 925 926 if (mSendObjectHandle == kInvalidObjectHandle) { 927 ALOGE("Expected SendObjectInfo before SendObject"); 928 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 929 goto done; 930 } 931 932 // read the header, and possibly some data 933 ret = mData.read(mFD); 934 if (ret < MTP_CONTAINER_HEADER_SIZE) { 935 result = MTP_RESPONSE_GENERAL_ERROR; 936 goto done; 937 } 938 initialData = ret - MTP_CONTAINER_HEADER_SIZE; 939 940 mtp_file_range mfr; 941 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); 942 if (mfr.fd < 0) { 943 result = MTP_RESPONSE_GENERAL_ERROR; 944 goto done; 945 } 946 fchown(mfr.fd, getuid(), mFileGroup); 947 // set permissions 948 mask = umask(0); 949 fchmod(mfr.fd, mFilePermission); 950 umask(mask); 951 952 if (initialData > 0) 953 ret = write(mfr.fd, mData.getData(), initialData); 954 955 if (mSendObjectFileSize - initialData > 0) { 956 mfr.offset = initialData; 957 if (mSendObjectFileSize == 0xFFFFFFFF) { 958 // tell driver to read until it receives a short packet 959 mfr.length = 0xFFFFFFFF; 960 } else { 961 mfr.length = mSendObjectFileSize - initialData; 962 } 963 964 ALOGV("receiving %s\n", (const char *)mSendObjectFilePath); 965 // transfer the file 966 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 967 ALOGV("MTP_RECEIVE_FILE returned %d\n", ret); 968 } 969 close(mfr.fd); 970 971 if (ret < 0) { 972 unlink(mSendObjectFilePath); 973 if (errno == ECANCELED) 974 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 975 else 976 result = MTP_RESPONSE_GENERAL_ERROR; 977 } 978 979done: 980 // reset so we don't attempt to send the data back 981 mData.reset(); 982 983 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 984 result == MTP_RESPONSE_OK); 985 mSendObjectHandle = kInvalidObjectHandle; 986 mSendObjectFormat = 0; 987 return result; 988} 989 990static void deleteRecursive(const char* path) { 991 char pathbuf[PATH_MAX]; 992 int pathLength = strlen(path); 993 if (pathLength >= sizeof(pathbuf) - 1) { 994 ALOGE("path too long: %s\n", path); 995 } 996 strcpy(pathbuf, path); 997 if (pathbuf[pathLength - 1] != '/') { 998 pathbuf[pathLength++] = '/'; 999 } 1000 char* fileSpot = pathbuf + pathLength; 1001 int pathRemaining = sizeof(pathbuf) - pathLength - 1; 1002 1003 DIR* dir = opendir(path); 1004 if (!dir) { 1005 ALOGE("opendir %s failed: %s", path, strerror(errno)); 1006 return; 1007 } 1008 1009 struct dirent* entry; 1010 while ((entry = readdir(dir))) { 1011 const char* name = entry->d_name; 1012 1013 // ignore "." and ".." 1014 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 1015 continue; 1016 } 1017 1018 int nameLength = strlen(name); 1019 if (nameLength > pathRemaining) { 1020 ALOGE("path %s/%s too long\n", path, name); 1021 continue; 1022 } 1023 strcpy(fileSpot, name); 1024 1025 int type = entry->d_type; 1026 if (entry->d_type == DT_DIR) { 1027 deleteRecursive(pathbuf); 1028 rmdir(pathbuf); 1029 } else { 1030 unlink(pathbuf); 1031 } 1032 } 1033 closedir(dir); 1034} 1035 1036static void deletePath(const char* path) { 1037 struct stat statbuf; 1038 if (stat(path, &statbuf) == 0) { 1039 if (S_ISDIR(statbuf.st_mode)) { 1040 deleteRecursive(path); 1041 rmdir(path); 1042 } else { 1043 unlink(path); 1044 } 1045 } else { 1046 ALOGE("deletePath stat failed for %s: %s", path, strerror(errno)); 1047 } 1048} 1049 1050MtpResponseCode MtpServer::doDeleteObject() { 1051 if (!hasStorage()) 1052 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 1053 MtpObjectHandle handle = mRequest.getParameter(1); 1054 MtpObjectFormat format = mRequest.getParameter(2); 1055 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 1056 // FIXME - implement deleting objects by format 1057 1058 MtpString filePath; 1059 int64_t fileLength; 1060 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format); 1061 if (result == MTP_RESPONSE_OK) { 1062 ALOGV("deleting %s", (const char *)filePath); 1063 result = mDatabase->deleteFile(handle); 1064 // Don't delete the actual files unless the database deletion is allowed 1065 if (result == MTP_RESPONSE_OK) { 1066 deletePath((const char *)filePath); 1067 } 1068 } 1069 1070 return result; 1071} 1072 1073MtpResponseCode MtpServer::doGetObjectPropDesc() { 1074 MtpObjectProperty propCode = mRequest.getParameter(1); 1075 MtpObjectFormat format = mRequest.getParameter(2); 1076 ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), 1077 MtpDebug::getFormatCodeName(format)); 1078 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); 1079 if (!property) 1080 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 1081 property->write(mData); 1082 delete property; 1083 return MTP_RESPONSE_OK; 1084} 1085 1086MtpResponseCode MtpServer::doGetDevicePropDesc() { 1087 MtpDeviceProperty propCode = mRequest.getParameter(1); 1088 ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); 1089 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); 1090 if (!property) 1091 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 1092 property->write(mData); 1093 delete property; 1094 return MTP_RESPONSE_OK; 1095} 1096 1097MtpResponseCode MtpServer::doSendPartialObject() { 1098 if (!hasStorage()) 1099 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 1100 MtpObjectHandle handle = mRequest.getParameter(1); 1101 uint64_t offset = mRequest.getParameter(2); 1102 uint64_t offset2 = mRequest.getParameter(3); 1103 offset = offset | (offset2 << 32); 1104 uint32_t length = mRequest.getParameter(4); 1105 1106 ObjectEdit* edit = getEditObject(handle); 1107 if (!edit) { 1108 ALOGE("object not open for edit in doSendPartialObject"); 1109 return MTP_RESPONSE_GENERAL_ERROR; 1110 } 1111 1112 // can't start writing past the end of the file 1113 if (offset > edit->mSize) { 1114 ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize); 1115 return MTP_RESPONSE_GENERAL_ERROR; 1116 } 1117 1118 const char* filePath = (const char *)edit->mPath; 1119 ALOGV("receiving partial %s %lld %lld\n", filePath, offset, length); 1120 1121 // read the header, and possibly some data 1122 int ret = mData.read(mFD); 1123 if (ret < MTP_CONTAINER_HEADER_SIZE) 1124 return MTP_RESPONSE_GENERAL_ERROR; 1125 int initialData = ret - MTP_CONTAINER_HEADER_SIZE; 1126 1127 if (initialData > 0) { 1128 ret = pwrite(edit->mFD, mData.getData(), initialData, offset); 1129 offset += initialData; 1130 length -= initialData; 1131 } 1132 1133 if (length > 0) { 1134 mtp_file_range mfr; 1135 mfr.fd = edit->mFD; 1136 mfr.offset = offset; 1137 mfr.length = length; 1138 1139 // transfer the file 1140 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 1141 ALOGV("MTP_RECEIVE_FILE returned %d", ret); 1142 } 1143 if (ret < 0) { 1144 mResponse.setParameter(1, 0); 1145 if (errno == ECANCELED) 1146 return MTP_RESPONSE_TRANSACTION_CANCELLED; 1147 else 1148 return MTP_RESPONSE_GENERAL_ERROR; 1149 } 1150 1151 // reset so we don't attempt to send this back 1152 mData.reset(); 1153 mResponse.setParameter(1, length); 1154 uint64_t end = offset + length; 1155 if (end > edit->mSize) { 1156 edit->mSize = end; 1157 } 1158 return MTP_RESPONSE_OK; 1159} 1160 1161MtpResponseCode MtpServer::doTruncateObject() { 1162 MtpObjectHandle handle = mRequest.getParameter(1); 1163 ObjectEdit* edit = getEditObject(handle); 1164 if (!edit) { 1165 ALOGE("object not open for edit in doTruncateObject"); 1166 return MTP_RESPONSE_GENERAL_ERROR; 1167 } 1168 1169 uint64_t offset = mRequest.getParameter(2); 1170 uint64_t offset2 = mRequest.getParameter(3); 1171 offset |= (offset2 << 32); 1172 if (ftruncate(edit->mFD, offset) != 0) { 1173 return MTP_RESPONSE_GENERAL_ERROR; 1174 } else { 1175 edit->mSize = offset; 1176 return MTP_RESPONSE_OK; 1177 } 1178} 1179 1180MtpResponseCode MtpServer::doBeginEditObject() { 1181 MtpObjectHandle handle = mRequest.getParameter(1); 1182 if (getEditObject(handle)) { 1183 ALOGE("object already open for edit in doBeginEditObject"); 1184 return MTP_RESPONSE_GENERAL_ERROR; 1185 } 1186 1187 MtpString path; 1188 int64_t fileLength; 1189 MtpObjectFormat format; 1190 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format); 1191 if (result != MTP_RESPONSE_OK) 1192 return result; 1193 1194 int fd = open((const char *)path, O_RDWR | O_EXCL); 1195 if (fd < 0) { 1196 ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno); 1197 return MTP_RESPONSE_GENERAL_ERROR; 1198 } 1199 1200 addEditObject(handle, path, fileLength, format, fd); 1201 return MTP_RESPONSE_OK; 1202} 1203 1204MtpResponseCode MtpServer::doEndEditObject() { 1205 MtpObjectHandle handle = mRequest.getParameter(1); 1206 ObjectEdit* edit = getEditObject(handle); 1207 if (!edit) { 1208 ALOGE("object not open for edit in doEndEditObject"); 1209 return MTP_RESPONSE_GENERAL_ERROR; 1210 } 1211 1212 commitEdit(edit); 1213 removeEditObject(handle); 1214 return MTP_RESPONSE_OK; 1215} 1216 1217} // namespace android 1218