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