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