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