MtpServer.cpp revision a849440ca96e93f700d62c6e41d48905b4d405b6
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <sys/types.h> 20#include <sys/ioctl.h> 21#include <sys/stat.h> 22#include <fcntl.h> 23#include <errno.h> 24#include <sys/stat.h> 25#include <dirent.h> 26 27#include <cutils/properties.h> 28 29#define LOG_TAG "MtpServer" 30 31#include "MtpDebug.h" 32#include "MtpDatabase.h" 33#include "MtpProperty.h" 34#include "MtpServer.h" 35#include "MtpStorage.h" 36#include "MtpStringBuffer.h" 37 38#include <linux/usb/f_mtp.h> 39 40namespace android { 41 42static const MtpOperationCode kSupportedOperationCodes[] = { 43 MTP_OPERATION_GET_DEVICE_INFO, 44 MTP_OPERATION_OPEN_SESSION, 45 MTP_OPERATION_CLOSE_SESSION, 46 MTP_OPERATION_GET_STORAGE_IDS, 47 MTP_OPERATION_GET_STORAGE_INFO, 48 MTP_OPERATION_GET_NUM_OBJECTS, 49 MTP_OPERATION_GET_OBJECT_HANDLES, 50 MTP_OPERATION_GET_OBJECT_INFO, 51 MTP_OPERATION_GET_OBJECT, 52// MTP_OPERATION_GET_THUMB, 53 MTP_OPERATION_DELETE_OBJECT, 54 MTP_OPERATION_SEND_OBJECT_INFO, 55 MTP_OPERATION_SEND_OBJECT, 56// MTP_OPERATION_INITIATE_CAPTURE, 57// MTP_OPERATION_FORMAT_STORE, 58// MTP_OPERATION_RESET_DEVICE, 59// MTP_OPERATION_SELF_TEST, 60// MTP_OPERATION_SET_OBJECT_PROTECTION, 61// MTP_OPERATION_POWER_DOWN, 62 MTP_OPERATION_GET_DEVICE_PROP_DESC, 63 MTP_OPERATION_GET_DEVICE_PROP_VALUE, 64 MTP_OPERATION_SET_DEVICE_PROP_VALUE, 65 MTP_OPERATION_RESET_DEVICE_PROP_VALUE, 66// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, 67// MTP_OPERATION_MOVE_OBJECT, 68// MTP_OPERATION_COPY_OBJECT, 69 MTP_OPERATION_GET_PARTIAL_OBJECT, 70// MTP_OPERATION_INITIATE_OPEN_CAPTURE, 71 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, 72 MTP_OPERATION_GET_OBJECT_PROP_DESC, 73 MTP_OPERATION_GET_OBJECT_PROP_VALUE, 74 MTP_OPERATION_SET_OBJECT_PROP_VALUE, 75 MTP_OPERATION_GET_OBJECT_PROP_LIST, 76// MTP_OPERATION_SET_OBJECT_PROP_LIST, 77// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC, 78// MTP_OPERATION_SEND_OBJECT_PROP_LIST, 79 MTP_OPERATION_GET_OBJECT_REFERENCES, 80 MTP_OPERATION_SET_OBJECT_REFERENCES, 81// MTP_OPERATION_SKIP, 82}; 83 84static const MtpEventCode kSupportedEventCodes[] = { 85 MTP_EVENT_OBJECT_ADDED, 86 MTP_EVENT_OBJECT_REMOVED, 87 MTP_EVENT_STORE_ADDED, 88 MTP_EVENT_STORE_REMOVED, 89}; 90 91MtpServer::MtpServer(int fd, MtpDatabase* database, 92 int fileGroup, int filePerm, int directoryPerm) 93 : mFD(fd), 94 mDatabase(database), 95 mFileGroup(fileGroup), 96 mFilePermission(filePerm), 97 mDirectoryPermission(directoryPerm), 98 mSessionID(0), 99 mSessionOpen(false), 100 mSendObjectHandle(kInvalidObjectHandle), 101 mSendObjectFormat(0), 102 mSendObjectFileSize(0) 103{ 104} 105 106MtpServer::~MtpServer() { 107} 108 109void MtpServer::addStorage(MtpStorage* storage) { 110 Mutex::Autolock autoLock(mMutex); 111 112 mStorages.push(storage); 113 sendStoreAdded(storage->getStorageID()); 114} 115 116void MtpServer::removeStorage(MtpStorage* storage) { 117 Mutex::Autolock autoLock(mMutex); 118 119 for (int i = 0; i < mStorages.size(); i++) { 120 if (mStorages[i] == storage) { 121 mStorages.removeAt(i); 122 sendStoreRemoved(storage->getStorageID()); 123 break; 124 } 125 } 126} 127 128MtpStorage* MtpServer::getStorage(MtpStorageID id) { 129 if (id == 0) 130 return mStorages[0]; 131 for (int i = 0; i < mStorages.size(); i++) { 132 MtpStorage* storage = mStorages[i]; 133 if (storage->getStorageID() == id) 134 return storage; 135 } 136 return NULL; 137} 138 139bool MtpServer::hasStorage(MtpStorageID id) { 140 if (id == 0 || id == 0xFFFFFFFF) 141 return mStorages.size() > 0; 142 return (getStorage(id) != NULL); 143} 144 145void MtpServer::run() { 146 int fd = mFD; 147 148 LOGV("MtpServer::run fd: %d\n", fd); 149 150 while (1) { 151 int ret = mRequest.read(fd); 152 if (ret < 0) { 153 LOGV("request read returned %d, errno: %d", ret, errno); 154 if (errno == ECANCELED) { 155 // return to top of loop and wait for next command 156 continue; 157 } 158 break; 159 } 160 MtpOperationCode operation = mRequest.getOperationCode(); 161 MtpTransactionID transaction = mRequest.getTransactionID(); 162 163 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation)); 164 mRequest.dump(); 165 166 // FIXME need to generalize this 167 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO 168 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES 169 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE 170 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE); 171 if (dataIn) { 172 int ret = mData.read(fd); 173 if (ret < 0) { 174 LOGE("data read returned %d, errno: %d", ret, errno); 175 if (errno == ECANCELED) { 176 // return to top of loop and wait for next command 177 continue; 178 } 179 break; 180 } 181 LOGV("received data:"); 182 mData.dump(); 183 } else { 184 mData.reset(); 185 } 186 187 if (handleRequest()) { 188 if (!dataIn && mData.hasData()) { 189 mData.setOperationCode(operation); 190 mData.setTransactionID(transaction); 191 LOGV("sending data:"); 192 mData.dump(); 193 ret = mData.write(fd); 194 if (ret < 0) { 195 LOGE("request write returned %d, errno: %d", ret, errno); 196 if (errno == ECANCELED) { 197 // return to top of loop and wait for next command 198 continue; 199 } 200 break; 201 } 202 } 203 204 mResponse.setTransactionID(transaction); 205 LOGV("sending response %04X", mResponse.getResponseCode()); 206 ret = mResponse.write(fd); 207 mResponse.dump(); 208 if (ret < 0) { 209 LOGE("request write returned %d, errno: %d", ret, errno); 210 if (errno == ECANCELED) { 211 // return to top of loop and wait for next command 212 continue; 213 } 214 break; 215 } 216 } else { 217 LOGV("skipping response\n"); 218 } 219 } 220 221 if (mSessionOpen) 222 mDatabase->sessionEnded(); 223} 224 225void MtpServer::sendObjectAdded(MtpObjectHandle handle) { 226 LOGV("sendObjectAdded %d\n", handle); 227 sendEvent(MTP_EVENT_OBJECT_ADDED, handle); 228} 229 230void MtpServer::sendObjectRemoved(MtpObjectHandle handle) { 231 LOGV("sendObjectRemoved %d\n", handle); 232 sendEvent(MTP_EVENT_OBJECT_REMOVED, handle); 233} 234 235void MtpServer::sendStoreAdded(MtpStorageID id) { 236 LOGV("sendStoreAdded %08X\n", id); 237 sendEvent(MTP_EVENT_STORE_ADDED, id); 238} 239 240void MtpServer::sendStoreRemoved(MtpStorageID id) { 241 LOGV("sendStoreRemoved %08X\n", id); 242 sendEvent(MTP_EVENT_STORE_REMOVED, id); 243} 244 245void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) { 246 if (mSessionOpen) { 247 mEvent.setEventCode(code); 248 mEvent.setTransactionID(mRequest.getTransactionID()); 249 mEvent.setParameter(1, param1); 250 int ret = mEvent.write(mFD); 251 LOGV("mEvent.write returned %d\n", ret); 252 } 253} 254 255bool MtpServer::handleRequest() { 256 Mutex::Autolock autoLock(mMutex); 257 258 MtpOperationCode operation = mRequest.getOperationCode(); 259 MtpResponseCode response; 260 261 mResponse.reset(); 262 263 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 264 // FIXME - need to delete mSendObjectHandle from the database 265 LOGE("expected SendObject after SendObjectInfo"); 266 mSendObjectHandle = kInvalidObjectHandle; 267 } 268 269 switch (operation) { 270 case MTP_OPERATION_GET_DEVICE_INFO: 271 response = doGetDeviceInfo(); 272 break; 273 case MTP_OPERATION_OPEN_SESSION: 274 response = doOpenSession(); 275 break; 276 case MTP_OPERATION_CLOSE_SESSION: 277 response = doCloseSession(); 278 break; 279 case MTP_OPERATION_GET_STORAGE_IDS: 280 response = doGetStorageIDs(); 281 break; 282 case MTP_OPERATION_GET_STORAGE_INFO: 283 response = doGetStorageInfo(); 284 break; 285 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 286 response = doGetObjectPropsSupported(); 287 break; 288 case MTP_OPERATION_GET_OBJECT_HANDLES: 289 response = doGetObjectHandles(); 290 break; 291 case MTP_OPERATION_GET_NUM_OBJECTS: 292 response = doGetNumObjects(); 293 break; 294 case MTP_OPERATION_GET_OBJECT_REFERENCES: 295 response = doGetObjectReferences(); 296 break; 297 case MTP_OPERATION_SET_OBJECT_REFERENCES: 298 response = doSetObjectReferences(); 299 break; 300 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 301 response = doGetObjectPropValue(); 302 break; 303 case MTP_OPERATION_SET_OBJECT_PROP_VALUE: 304 response = doSetObjectPropValue(); 305 break; 306 case MTP_OPERATION_GET_DEVICE_PROP_VALUE: 307 response = doGetDevicePropValue(); 308 break; 309 case MTP_OPERATION_SET_DEVICE_PROP_VALUE: 310 response = doSetDevicePropValue(); 311 break; 312 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE: 313 response = doResetDevicePropValue(); 314 break; 315 case MTP_OPERATION_GET_OBJECT_PROP_LIST: 316 response = doGetObjectPropList(); 317 break; 318 case MTP_OPERATION_GET_OBJECT_INFO: 319 response = doGetObjectInfo(); 320 break; 321 case MTP_OPERATION_GET_OBJECT: 322 response = doGetObject(); 323 break; 324 case MTP_OPERATION_GET_PARTIAL_OBJECT: 325 response = doGetPartialObject(); 326 break; 327 case MTP_OPERATION_SEND_OBJECT_INFO: 328 response = doSendObjectInfo(); 329 break; 330 case MTP_OPERATION_SEND_OBJECT: 331 response = doSendObject(); 332 break; 333 case MTP_OPERATION_DELETE_OBJECT: 334 response = doDeleteObject(); 335 break; 336 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 337 response = doGetObjectPropDesc(); 338 break; 339 case MTP_OPERATION_GET_DEVICE_PROP_DESC: 340 response = doGetDevicePropDesc(); 341 break; 342 default: 343 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); 344 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 345 break; 346 } 347 348 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 349 return false; 350 mResponse.setResponseCode(response); 351 return true; 352} 353 354MtpResponseCode MtpServer::doGetDeviceInfo() { 355 MtpStringBuffer string; 356 char prop_value[PROPERTY_VALUE_MAX]; 357 358 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats(); 359 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats(); 360 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties(); 361 362 // fill in device info 363 mData.putUInt16(MTP_STANDARD_VERSION); 364 mData.putUInt32(6); // MTP Vendor Extension ID 365 mData.putUInt16(MTP_STANDARD_VERSION); 366 string.set("microsoft.com: 1.0;"); 367 mData.putString(string); // MTP Extensions 368 mData.putUInt16(0); //Functional Mode 369 mData.putAUInt16(kSupportedOperationCodes, 370 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 371 mData.putAUInt16(kSupportedEventCodes, 372 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported 373 mData.putAUInt16(deviceProperties); // Device Properties Supported 374 mData.putAUInt16(captureFormats); // Capture Formats 375 mData.putAUInt16(playbackFormats); // Playback Formats 376 377 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer"); 378 string.set(prop_value); 379 mData.putString(string); // Manufacturer 380 381 property_get("ro.product.model", prop_value, "MTP Device"); 382 string.set(prop_value); 383 mData.putString(string); // Model 384 string.set("1.0"); 385 mData.putString(string); // Device Version 386 387 property_get("ro.serialno", prop_value, "????????"); 388 string.set(prop_value); 389 mData.putString(string); // Serial Number 390 391 delete playbackFormats; 392 delete captureFormats; 393 delete deviceProperties; 394 395 return MTP_RESPONSE_OK; 396} 397 398MtpResponseCode MtpServer::doOpenSession() { 399 if (mSessionOpen) { 400 mResponse.setParameter(1, mSessionID); 401 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 402 } 403 mSessionID = mRequest.getParameter(1); 404 mSessionOpen = true; 405 406 mDatabase->sessionStarted(); 407 408 return MTP_RESPONSE_OK; 409} 410 411MtpResponseCode MtpServer::doCloseSession() { 412 if (!mSessionOpen) 413 return MTP_RESPONSE_SESSION_NOT_OPEN; 414 mSessionID = 0; 415 mSessionOpen = false; 416 mDatabase->sessionEnded(); 417 return MTP_RESPONSE_OK; 418} 419 420MtpResponseCode MtpServer::doGetStorageIDs() { 421 if (!mSessionOpen) 422 return MTP_RESPONSE_SESSION_NOT_OPEN; 423 424 int count = mStorages.size(); 425 mData.putUInt32(count); 426 for (int i = 0; i < count; i++) 427 mData.putUInt32(mStorages[i]->getStorageID()); 428 429 return MTP_RESPONSE_OK; 430} 431 432MtpResponseCode MtpServer::doGetStorageInfo() { 433 MtpStringBuffer string; 434 435 if (!mSessionOpen) 436 return MTP_RESPONSE_SESSION_NOT_OPEN; 437 MtpStorageID id = mRequest.getParameter(1); 438 MtpStorage* storage = getStorage(id); 439 if (!storage) 440 return MTP_RESPONSE_INVALID_STORAGE_ID; 441 442 mData.putUInt16(storage->getType()); 443 mData.putUInt16(storage->getFileSystemType()); 444 mData.putUInt16(storage->getAccessCapability()); 445 mData.putUInt64(storage->getMaxCapacity()); 446 mData.putUInt64(storage->getFreeSpace()); 447 mData.putUInt32(1024*1024*1024); // Free Space in Objects 448 string.set(storage->getDescription()); 449 mData.putString(string); 450 mData.putEmptyString(); // Volume Identifier 451 452 return MTP_RESPONSE_OK; 453} 454 455MtpResponseCode MtpServer::doGetObjectPropsSupported() { 456 if (!mSessionOpen) 457 return MTP_RESPONSE_SESSION_NOT_OPEN; 458 MtpObjectFormat format = mRequest.getParameter(1); 459 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format); 460 mData.putAUInt16(properties); 461 delete properties; 462 return MTP_RESPONSE_OK; 463} 464 465MtpResponseCode MtpServer::doGetObjectHandles() { 466 if (!mSessionOpen) 467 return MTP_RESPONSE_SESSION_NOT_OPEN; 468 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 469 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 470 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 471 // 0x00000000 for all objects? 472 473 if (!hasStorage(storageID)) 474 return MTP_RESPONSE_INVALID_STORAGE_ID; 475 if (parent == 0xFFFFFFFF) 476 parent = 0; 477 478 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 479 mData.putAUInt32(handles); 480 delete handles; 481 return MTP_RESPONSE_OK; 482} 483 484MtpResponseCode MtpServer::doGetNumObjects() { 485 if (!mSessionOpen) 486 return MTP_RESPONSE_SESSION_NOT_OPEN; 487 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 488 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 489 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 490 // 0x00000000 for all objects? 491 if (!hasStorage(storageID)) 492 return MTP_RESPONSE_INVALID_STORAGE_ID; 493 if (parent == 0xFFFFFFFF) 494 parent = 0; 495 496 int count = mDatabase->getNumObjects(storageID, format, parent); 497 if (count >= 0) { 498 mResponse.setParameter(1, count); 499 return MTP_RESPONSE_OK; 500 } else { 501 mResponse.setParameter(1, 0); 502 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 503 } 504} 505 506MtpResponseCode MtpServer::doGetObjectReferences() { 507 if (!mSessionOpen) 508 return MTP_RESPONSE_SESSION_NOT_OPEN; 509 if (!hasStorage()) 510 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 511 MtpObjectHandle handle = mRequest.getParameter(1); 512 513 // FIXME - check for invalid object handle 514 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle); 515 if (handles) { 516 mData.putAUInt32(handles); 517 delete handles; 518 } else { 519 mData.putEmptyArray(); 520 } 521 return MTP_RESPONSE_OK; 522} 523 524MtpResponseCode MtpServer::doSetObjectReferences() { 525 if (!mSessionOpen) 526 return MTP_RESPONSE_SESSION_NOT_OPEN; 527 if (!hasStorage()) 528 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 529 MtpStorageID handle = mRequest.getParameter(1); 530 531 MtpObjectHandleList* references = mData.getAUInt32(); 532 MtpResponseCode result = mDatabase->setObjectReferences(handle, references); 533 delete references; 534 return result; 535} 536 537MtpResponseCode MtpServer::doGetObjectPropValue() { 538 if (!hasStorage()) 539 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 540 MtpObjectHandle handle = mRequest.getParameter(1); 541 MtpObjectProperty property = mRequest.getParameter(2); 542 LOGV("GetObjectPropValue %d %s\n", handle, 543 MtpDebug::getObjectPropCodeName(property)); 544 545 return mDatabase->getObjectPropertyValue(handle, property, mData); 546} 547 548MtpResponseCode MtpServer::doSetObjectPropValue() { 549 if (!hasStorage()) 550 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 551 MtpObjectHandle handle = mRequest.getParameter(1); 552 MtpObjectProperty property = mRequest.getParameter(2); 553 LOGV("SetObjectPropValue %d %s\n", handle, 554 MtpDebug::getObjectPropCodeName(property)); 555 556 return mDatabase->setObjectPropertyValue(handle, property, mData); 557} 558 559MtpResponseCode MtpServer::doGetDevicePropValue() { 560 MtpDeviceProperty property = mRequest.getParameter(1); 561 LOGV("GetDevicePropValue %s\n", 562 MtpDebug::getDevicePropCodeName(property)); 563 564 return mDatabase->getDevicePropertyValue(property, mData); 565} 566 567MtpResponseCode MtpServer::doSetDevicePropValue() { 568 MtpDeviceProperty property = mRequest.getParameter(1); 569 LOGV("SetDevicePropValue %s\n", 570 MtpDebug::getDevicePropCodeName(property)); 571 572 return mDatabase->setDevicePropertyValue(property, mData); 573} 574 575MtpResponseCode MtpServer::doResetDevicePropValue() { 576 MtpDeviceProperty property = mRequest.getParameter(1); 577 LOGV("ResetDevicePropValue %s\n", 578 MtpDebug::getDevicePropCodeName(property)); 579 580 return mDatabase->resetDeviceProperty(property); 581} 582 583MtpResponseCode MtpServer::doGetObjectPropList() { 584 if (!hasStorage()) 585 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 586 587 MtpObjectHandle handle = mRequest.getParameter(1); 588 // use uint32_t so we can support 0xFFFFFFFF 589 uint32_t format = mRequest.getParameter(2); 590 uint32_t property = mRequest.getParameter(3); 591 int groupCode = mRequest.getParameter(4); 592 int depth = mRequest.getParameter(5); 593 LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n", 594 handle, MtpDebug::getFormatCodeName(format), 595 MtpDebug::getObjectPropCodeName(property), groupCode, depth); 596 597 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData); 598} 599 600MtpResponseCode MtpServer::doGetObjectInfo() { 601 if (!hasStorage()) 602 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 603 MtpObjectHandle handle = mRequest.getParameter(1); 604 return mDatabase->getObjectInfo(handle, mData); 605} 606 607MtpResponseCode MtpServer::doGetObject() { 608 if (!hasStorage()) 609 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 610 MtpObjectHandle handle = mRequest.getParameter(1); 611 MtpString pathBuf; 612 int64_t fileLength; 613 MtpObjectFormat format; 614 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 615 if (result != MTP_RESPONSE_OK) 616 return result; 617 618 const char* filePath = (const char *)pathBuf; 619 mtp_file_range mfr; 620 mfr.fd = open(filePath, O_RDONLY); 621 if (mfr.fd < 0) { 622 return MTP_RESPONSE_GENERAL_ERROR; 623 } 624 mfr.offset = 0; 625 mfr.length = fileLength; 626 627 // send data header 628 mData.setOperationCode(mRequest.getOperationCode()); 629 mData.setTransactionID(mRequest.getTransactionID()); 630 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE); 631 632 // then transfer the file 633 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 634 close(mfr.fd); 635 if (ret < 0) { 636 if (errno == ECANCELED) 637 return MTP_RESPONSE_TRANSACTION_CANCELLED; 638 else 639 return MTP_RESPONSE_GENERAL_ERROR; 640 } 641 return MTP_RESPONSE_OK; 642} 643 644MtpResponseCode MtpServer::doGetPartialObject() { 645 if (!hasStorage()) 646 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 647 MtpObjectHandle handle = mRequest.getParameter(1); 648 uint32_t offset = mRequest.getParameter(2); 649 uint32_t length = mRequest.getParameter(3); 650 MtpString pathBuf; 651 int64_t fileLength; 652 MtpObjectFormat format; 653 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format); 654 if (result != MTP_RESPONSE_OK) 655 return result; 656 if (offset + length > fileLength) 657 length = fileLength - offset; 658 659 const char* filePath = (const char *)pathBuf; 660 mtp_file_range mfr; 661 mfr.fd = open(filePath, O_RDONLY); 662 if (mfr.fd < 0) { 663 return MTP_RESPONSE_GENERAL_ERROR; 664 } 665 mfr.offset = offset; 666 mfr.length = length; 667 mResponse.setParameter(1, length); 668 669 // send data header 670 mData.setOperationCode(mRequest.getOperationCode()); 671 mData.setTransactionID(mRequest.getTransactionID()); 672 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE); 673 674 // then transfer the file 675 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 676 close(mfr.fd); 677 if (ret < 0) { 678 if (errno == ECANCELED) 679 return MTP_RESPONSE_TRANSACTION_CANCELLED; 680 else 681 return MTP_RESPONSE_GENERAL_ERROR; 682 } 683 return MTP_RESPONSE_OK; 684} 685 686MtpResponseCode MtpServer::doSendObjectInfo() { 687 MtpString path; 688 MtpStorageID storageID = mRequest.getParameter(1); 689 MtpStorage* storage = getStorage(storageID); 690 MtpObjectHandle parent = mRequest.getParameter(2); 691 if (!storage) 692 return MTP_RESPONSE_INVALID_STORAGE_ID; 693 694 // special case the root 695 if (parent == MTP_PARENT_ROOT) { 696 path = storage->getPath(); 697 parent = 0; 698 } else { 699 int64_t length; 700 MtpObjectFormat format; 701 int result = mDatabase->getObjectFilePath(parent, path, length, format); 702 if (result != MTP_RESPONSE_OK) 703 return result; 704 if (format != MTP_FORMAT_ASSOCIATION) 705 return MTP_RESPONSE_INVALID_PARENT_OBJECT; 706 } 707 708 // read only the fields we need 709 mData.getUInt32(); // storage ID 710 MtpObjectFormat format = mData.getUInt16(); 711 mData.getUInt16(); // protection status 712 mSendObjectFileSize = mData.getUInt32(); 713 mData.getUInt16(); // thumb format 714 mData.getUInt32(); // thumb compressed size 715 mData.getUInt32(); // thumb pix width 716 mData.getUInt32(); // thumb pix height 717 mData.getUInt32(); // image pix width 718 mData.getUInt32(); // image pix height 719 mData.getUInt32(); // image bit depth 720 mData.getUInt32(); // parent 721 uint16_t associationType = mData.getUInt16(); 722 uint32_t associationDesc = mData.getUInt32(); // association desc 723 mData.getUInt32(); // sequence number 724 MtpStringBuffer name, created, modified; 725 mData.getString(name); // file name 726 mData.getString(created); // date created 727 mData.getString(modified); // date modified 728 // keywords follow 729 730 LOGV("name: %s format: %04X\n", (const char *)name, format); 731 time_t modifiedTime; 732 if (!parseDateTime(modified, modifiedTime)) 733 modifiedTime = 0; 734 735 if (path[path.size() - 1] != '/') 736 path += "/"; 737 path += (const char *)name; 738 739 // check space first 740 if (mSendObjectFileSize > storage->getFreeSpace()) 741 return MTP_RESPONSE_STORAGE_FULL; 742 743LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID); 744 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, 745 format, parent, storageID, mSendObjectFileSize, modifiedTime); 746 if (handle == kInvalidObjectHandle) { 747 return MTP_RESPONSE_GENERAL_ERROR; 748 } 749 750 if (format == MTP_FORMAT_ASSOCIATION) { 751 mode_t mask = umask(0); 752 int ret = mkdir((const char *)path, mDirectoryPermission); 753 umask(mask); 754 if (ret && ret != -EEXIST) 755 return MTP_RESPONSE_GENERAL_ERROR; 756 chown((const char *)path, getuid(), mFileGroup); 757 758 // SendObject does not get sent for directories, so call endSendObject here instead 759 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK); 760 } else { 761 mSendObjectFilePath = path; 762 // save the handle for the SendObject call, which should follow 763 mSendObjectHandle = handle; 764 mSendObjectFormat = format; 765 } 766 767 mResponse.setParameter(1, storageID); 768 mResponse.setParameter(2, parent); 769 mResponse.setParameter(3, handle); 770 771 return MTP_RESPONSE_OK; 772} 773 774MtpResponseCode MtpServer::doSendObject() { 775 if (!hasStorage()) 776 return MTP_RESPONSE_GENERAL_ERROR; 777 MtpResponseCode result = MTP_RESPONSE_OK; 778 mode_t mask; 779 int ret; 780 781 if (mSendObjectHandle == kInvalidObjectHandle) { 782 LOGE("Expected SendObjectInfo before SendObject"); 783 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO; 784 goto done; 785 } 786 787 // read the header 788 ret = mData.readDataHeader(mFD); 789 // FIXME - check for errors here. 790 791 // reset so we don't attempt to send this back 792 mData.reset(); 793 794 mtp_file_range mfr; 795 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); 796 if (mfr.fd < 0) { 797 result = MTP_RESPONSE_GENERAL_ERROR; 798 goto done; 799 } 800 fchown(mfr.fd, getuid(), mFileGroup); 801 // set permissions 802 mask = umask(0); 803 fchmod(mfr.fd, mFilePermission); 804 umask(mask); 805 806 mfr.offset = 0; 807 mfr.length = mSendObjectFileSize; 808 809 LOGV("receiving %s\n", (const char *)mSendObjectFilePath); 810 // transfer the file 811 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 812 close(mfr.fd); 813 814 LOGV("MTP_RECEIVE_FILE returned %d", ret); 815 816 if (ret < 0) { 817 unlink(mSendObjectFilePath); 818 if (errno == ECANCELED) 819 result = MTP_RESPONSE_TRANSACTION_CANCELLED; 820 else 821 result = MTP_RESPONSE_GENERAL_ERROR; 822 } 823 824done: 825 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, 826 result == MTP_RESPONSE_OK); 827 mSendObjectHandle = kInvalidObjectHandle; 828 mSendObjectFormat = 0; 829 return result; 830} 831 832static void deleteRecursive(const char* path) { 833 char pathbuf[PATH_MAX]; 834 int pathLength = strlen(path); 835 if (pathLength >= sizeof(pathbuf) - 1) { 836 LOGE("path too long: %s\n", path); 837 } 838 strcpy(pathbuf, path); 839 if (pathbuf[pathLength - 1] != '/') { 840 pathbuf[pathLength++] = '/'; 841 } 842 char* fileSpot = pathbuf + pathLength; 843 int pathRemaining = sizeof(pathbuf) - pathLength - 1; 844 845 DIR* dir = opendir(path); 846 if (!dir) { 847 LOGE("opendir %s failed: %s", path, strerror(errno)); 848 return; 849 } 850 851 struct dirent* entry; 852 while ((entry = readdir(dir))) { 853 const char* name = entry->d_name; 854 855 // ignore "." and ".." 856 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { 857 continue; 858 } 859 860 int nameLength = strlen(name); 861 if (nameLength > pathRemaining) { 862 LOGE("path %s/%s too long\n", path, name); 863 continue; 864 } 865 strcpy(fileSpot, name); 866 867 int type = entry->d_type; 868 if (entry->d_type == DT_DIR) { 869 deleteRecursive(pathbuf); 870 rmdir(pathbuf); 871 } else { 872 unlink(pathbuf); 873 } 874 } 875 closedir(dir); 876} 877 878static void deletePath(const char* path) { 879 struct stat statbuf; 880 if (stat(path, &statbuf) == 0) { 881 if (S_ISDIR(statbuf.st_mode)) { 882 deleteRecursive(path); 883 rmdir(path); 884 } else { 885 unlink(path); 886 } 887 } else { 888 LOGE("deletePath stat failed for %s: %s", path, strerror(errno)); 889 } 890} 891 892MtpResponseCode MtpServer::doDeleteObject() { 893 if (!hasStorage()) 894 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 895 MtpObjectHandle handle = mRequest.getParameter(1); 896 MtpObjectFormat format = mRequest.getParameter(2); 897 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 898 // FIXME - implement deleting objects by format 899 900 MtpString filePath; 901 int64_t fileLength; 902 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format); 903 if (result == MTP_RESPONSE_OK) { 904 LOGV("deleting %s", (const char *)filePath); 905 deletePath((const char *)filePath); 906 return mDatabase->deleteFile(handle); 907 } else { 908 return result; 909 } 910} 911 912MtpResponseCode MtpServer::doGetObjectPropDesc() { 913 MtpObjectProperty propCode = mRequest.getParameter(1); 914 MtpObjectFormat format = mRequest.getParameter(2); 915 LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode), 916 MtpDebug::getFormatCodeName(format)); 917 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format); 918 if (!property) 919 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED; 920 property->write(mData); 921 delete property; 922 return MTP_RESPONSE_OK; 923} 924 925MtpResponseCode MtpServer::doGetDevicePropDesc() { 926 MtpDeviceProperty propCode = mRequest.getParameter(1); 927 LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode)); 928 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode); 929 if (!property) 930 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; 931 property->write(mData); 932 delete property; 933 return MTP_RESPONSE_OK; 934} 935 936} // namespace android 937