MtpDevice.cpp revision f7454622eaac287d20ab43013d7015fe42b894f8
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#define LOG_TAG "MtpDevice" 18 19#include "MtpDebug.h" 20#include "MtpDevice.h" 21#include "MtpDeviceInfo.h" 22#include "MtpObjectInfo.h" 23#include "MtpProperty.h" 24#include "MtpStorageInfo.h" 25#include "MtpStringBuffer.h" 26#include "MtpUtils.h" 27 28#include <stdio.h> 29#include <stdlib.h> 30#include <sys/types.h> 31#include <sys/ioctl.h> 32#include <sys/stat.h> 33#include <fcntl.h> 34#include <errno.h> 35#include <endian.h> 36 37#include <usbhost/usbhost.h> 38 39namespace android { 40 41MtpDevice::MtpDevice(struct usb_device* device, int interface, 42 struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, 43 struct usb_endpoint *ep_intr) 44 : mDevice(device), 45 mInterface(interface), 46 mEndpointIn(ep_in), 47 mEndpointOut(ep_out), 48 mEndpointIntr(ep_intr), 49 mDeviceInfo(NULL), 50 mID(usb_device_get_unique_id(device)), 51 mSessionID(0), 52 mTransactionID(0), 53 mReceivedResponse(false) 54{ 55} 56 57MtpDevice::~MtpDevice() { 58 close(); 59 for (int i = 0; i < mDeviceProperties.size(); i++) 60 delete mDeviceProperties[i]; 61} 62 63void MtpDevice::initialize() { 64 openSession(); 65 mDeviceInfo = getDeviceInfo(); 66 if (mDeviceInfo) { 67 if (mDeviceInfo->mDeviceProperties) { 68 int count = mDeviceInfo->mDeviceProperties->size(); 69 for (int i = 0; i < count; i++) { 70 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i]; 71 MtpProperty* property = getDevicePropDesc(propCode); 72 if (property) 73 mDeviceProperties.push(property); 74 } 75 } 76 } 77} 78 79void MtpDevice::close() { 80 if (mDevice) { 81 usb_device_release_interface(mDevice, mInterface); 82 usb_device_close(mDevice); 83 mDevice = NULL; 84 } 85} 86 87void MtpDevice::print() { 88 if (mDeviceInfo) { 89 mDeviceInfo->print(); 90 91 if (mDeviceInfo->mDeviceProperties) { 92 LOGI("***** DEVICE PROPERTIES *****\n"); 93 int count = mDeviceInfo->mDeviceProperties->size(); 94 for (int i = 0; i < count; i++) { 95 MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i]; 96 MtpProperty* property = getDevicePropDesc(propCode); 97 if (property) { 98 property->print(); 99 } 100 } 101 } 102 } 103 104 if (mDeviceInfo->mPlaybackFormats) { 105 LOGI("***** OBJECT PROPERTIES *****\n"); 106 int count = mDeviceInfo->mPlaybackFormats->size(); 107 for (int i = 0; i < count; i++) { 108 MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i]; 109 LOGI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format)); 110 MtpObjectPropertyList* props = getObjectPropsSupported(format); 111 if (props) { 112 for (int j = 0; j < props->size(); j++) { 113 MtpObjectProperty prop = (*props)[j]; 114 MtpProperty* property = getObjectPropDesc(prop, format); 115 if (property) 116 property->print(); 117 else 118 LOGE("could not fetch property: %s", 119 MtpDebug::getObjectPropCodeName(prop)); 120 } 121 } 122 } 123 } 124} 125 126const char* MtpDevice::getDeviceName() { 127 if (mDevice) 128 return usb_device_get_name(mDevice); 129 else 130 return "???"; 131} 132 133bool MtpDevice::openSession() { 134 Mutex::Autolock autoLock(mMutex); 135 136 mSessionID = 0; 137 mTransactionID = 0; 138 MtpSessionID newSession = 1; 139 mRequest.reset(); 140 mRequest.setParameter(1, newSession); 141 if (!sendRequest(MTP_OPERATION_OPEN_SESSION)) 142 return false; 143 MtpResponseCode ret = readResponse(); 144 if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN) 145 newSession = mResponse.getParameter(1); 146 else if (ret != MTP_RESPONSE_OK) 147 return false; 148 149 mSessionID = newSession; 150 mTransactionID = 1; 151 return true; 152} 153 154bool MtpDevice::closeSession() { 155 // FIXME 156 return true; 157} 158 159MtpDeviceInfo* MtpDevice::getDeviceInfo() { 160 Mutex::Autolock autoLock(mMutex); 161 162 mRequest.reset(); 163 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO)) 164 return NULL; 165 if (!readData()) 166 return NULL; 167 MtpResponseCode ret = readResponse(); 168 if (ret == MTP_RESPONSE_OK) { 169 MtpDeviceInfo* info = new MtpDeviceInfo; 170 info->read(mData); 171 return info; 172 } 173 return NULL; 174} 175 176MtpStorageIDList* MtpDevice::getStorageIDs() { 177 Mutex::Autolock autoLock(mMutex); 178 179 mRequest.reset(); 180 if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS)) 181 return NULL; 182 if (!readData()) 183 return NULL; 184 MtpResponseCode ret = readResponse(); 185 if (ret == MTP_RESPONSE_OK) { 186 return mData.getAUInt32(); 187 } 188 return NULL; 189} 190 191MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) { 192 Mutex::Autolock autoLock(mMutex); 193 194 mRequest.reset(); 195 mRequest.setParameter(1, storageID); 196 if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO)) 197 return NULL; 198 if (!readData()) 199 return NULL; 200 MtpResponseCode ret = readResponse(); 201 if (ret == MTP_RESPONSE_OK) { 202 MtpStorageInfo* info = new MtpStorageInfo(storageID); 203 info->read(mData); 204 return info; 205 } 206 return NULL; 207} 208 209MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID, 210 MtpObjectFormat format, MtpObjectHandle parent) { 211 Mutex::Autolock autoLock(mMutex); 212 213 mRequest.reset(); 214 mRequest.setParameter(1, storageID); 215 mRequest.setParameter(2, format); 216 mRequest.setParameter(3, parent); 217 if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES)) 218 return NULL; 219 if (!readData()) 220 return NULL; 221 MtpResponseCode ret = readResponse(); 222 if (ret == MTP_RESPONSE_OK) { 223 return mData.getAUInt32(); 224 } 225 return NULL; 226} 227 228MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) { 229 Mutex::Autolock autoLock(mMutex); 230 231 // FIXME - we might want to add some caching here 232 233 mRequest.reset(); 234 mRequest.setParameter(1, handle); 235 if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO)) 236 return NULL; 237 if (!readData()) 238 return NULL; 239 MtpResponseCode ret = readResponse(); 240 if (ret == MTP_RESPONSE_OK) { 241 MtpObjectInfo* info = new MtpObjectInfo(handle); 242 info->read(mData); 243 return info; 244 } 245 return NULL; 246} 247 248void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) { 249 Mutex::Autolock autoLock(mMutex); 250 251 mRequest.reset(); 252 mRequest.setParameter(1, handle); 253 if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) { 254 MtpResponseCode ret = readResponse(); 255 if (ret == MTP_RESPONSE_OK) { 256 return mData.getData(outLength); 257 } 258 } 259 outLength = 0; 260 return NULL; 261} 262 263MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) { 264 Mutex::Autolock autoLock(mMutex); 265 266 mRequest.reset(); 267 MtpObjectHandle parent = info->mParent; 268 if (parent == 0) 269 parent = MTP_PARENT_ROOT; 270 271 mRequest.setParameter(1, info->mStorageID); 272 mRequest.setParameter(2, info->mParent); 273 274 mData.putUInt32(info->mStorageID); 275 mData.putUInt16(info->mFormat); 276 mData.putUInt16(info->mProtectionStatus); 277 mData.putUInt32(info->mCompressedSize); 278 mData.putUInt16(info->mThumbFormat); 279 mData.putUInt32(info->mThumbCompressedSize); 280 mData.putUInt32(info->mThumbPixWidth); 281 mData.putUInt32(info->mThumbPixHeight); 282 mData.putUInt32(info->mImagePixWidth); 283 mData.putUInt32(info->mImagePixHeight); 284 mData.putUInt32(info->mImagePixDepth); 285 mData.putUInt32(info->mParent); 286 mData.putUInt16(info->mAssociationType); 287 mData.putUInt32(info->mAssociationDesc); 288 mData.putUInt32(info->mSequenceNumber); 289 mData.putString(info->mName); 290 291 char created[100], modified[100]; 292 formatDateTime(info->mDateCreated, created, sizeof(created)); 293 formatDateTime(info->mDateModified, modified, sizeof(modified)); 294 295 mData.putString(created); 296 mData.putString(modified); 297 if (info->mKeywords) 298 mData.putString(info->mKeywords); 299 else 300 mData.putEmptyString(); 301 302 if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) { 303 MtpResponseCode ret = readResponse(); 304 if (ret == MTP_RESPONSE_OK) { 305 info->mStorageID = mResponse.getParameter(1); 306 info->mParent = mResponse.getParameter(2); 307 info->mHandle = mResponse.getParameter(3); 308 return info->mHandle; 309 } 310 } 311 return (MtpObjectHandle)-1; 312} 313 314bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) { 315 Mutex::Autolock autoLock(mMutex); 316 317 int remaining = info->mCompressedSize; 318 mRequest.reset(); 319 mRequest.setParameter(1, info->mHandle); 320 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) { 321 // send data header 322 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining); 323 324 char buffer[65536]; 325 while (remaining > 0) { 326 int count = read(srcFD, buffer, sizeof(buffer)); 327 if (count > 0) { 328 int written = mData.write(mEndpointOut, buffer, count); 329 // FIXME check error 330 remaining -= count; 331 } else { 332 break; 333 } 334 } 335 } 336 MtpResponseCode ret = readResponse(); 337 return (remaining == 0 && ret == MTP_RESPONSE_OK); 338} 339 340bool MtpDevice::deleteObject(MtpObjectHandle handle) { 341 Mutex::Autolock autoLock(mMutex); 342 343 mRequest.reset(); 344 mRequest.setParameter(1, handle); 345 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) { 346 MtpResponseCode ret = readResponse(); 347 if (ret == MTP_RESPONSE_OK) 348 return true; 349 } 350 return false; 351} 352 353MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) { 354 MtpObjectInfo* info = getObjectInfo(handle); 355 if (info) 356 return info->mParent; 357 else 358 return -1; 359} 360 361MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) { 362 MtpObjectInfo* info = getObjectInfo(handle); 363 if (info) 364 return info->mStorageID; 365 else 366 return -1; 367} 368 369MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) { 370 Mutex::Autolock autoLock(mMutex); 371 372 mRequest.reset(); 373 mRequest.setParameter(1, format); 374 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED)) 375 return NULL; 376 if (!readData()) 377 return NULL; 378 MtpResponseCode ret = readResponse(); 379 if (ret == MTP_RESPONSE_OK) { 380 return mData.getAUInt16(); 381 } 382 return NULL; 383 384} 385 386MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) { 387 Mutex::Autolock autoLock(mMutex); 388 389 mRequest.reset(); 390 mRequest.setParameter(1, code); 391 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC)) 392 return NULL; 393 if (!readData()) 394 return NULL; 395 MtpResponseCode ret = readResponse(); 396 if (ret == MTP_RESPONSE_OK) { 397 MtpProperty* property = new MtpProperty; 398 property->read(mData); 399 return property; 400 } 401 return NULL; 402} 403 404MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) { 405 Mutex::Autolock autoLock(mMutex); 406 407 mRequest.reset(); 408 mRequest.setParameter(1, code); 409 mRequest.setParameter(2, format); 410 if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC)) 411 return NULL; 412 if (!readData()) 413 return NULL; 414 MtpResponseCode ret = readResponse(); 415 if (ret == MTP_RESPONSE_OK) { 416 MtpProperty* property = new MtpProperty; 417 property->read(mData); 418 return property; 419 } 420 return NULL; 421} 422 423// reads the object's data and writes it to the specified file path 424bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) { 425 LOGD("readObject: %s", destPath); 426 int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC); 427 if (fd < 0) { 428 LOGE("open failed for %s", destPath); 429 return false; 430 } 431 432 fchown(fd, getuid(), group); 433 // set permissions 434 int mask = umask(0); 435 fchmod(fd, perm); 436 umask(mask); 437 438 Mutex::Autolock autoLock(mMutex); 439 bool result = false; 440 441 mRequest.reset(); 442 mRequest.setParameter(1, handle); 443 if (sendRequest(MTP_OPERATION_GET_OBJECT) 444 && mData.readDataHeader(mEndpointIn)) { 445 uint32_t length = mData.getContainerLength(); 446 if (length < MTP_CONTAINER_HEADER_SIZE) 447 goto fail; 448 length -= MTP_CONTAINER_HEADER_SIZE; 449 uint32_t remaining = length; 450 451 int initialDataLength = 0; 452 void* initialData = mData.getData(initialDataLength); 453 if (initialData) { 454 if (initialDataLength > 0) { 455 if (write(fd, initialData, initialDataLength) != initialDataLength) 456 goto fail; 457 remaining -= initialDataLength; 458 } 459 free(initialData); 460 } 461 462 // USB reads greater than 16K don't work 463 char buffer1[16384], buffer2[16384]; 464 char* readBuffer = buffer1; 465 char* writeBuffer = NULL; 466 int writeLength = 0; 467 468 while (remaining > 0 || writeBuffer) { 469 if (remaining > 0) { 470 // queue up a read request 471 int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining); 472 if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) { 473 LOGE("readDataAsync failed"); 474 goto fail; 475 } 476 } else { 477 readBuffer = NULL; 478 } 479 480 if (writeBuffer) { 481 // write previous buffer 482 if (write(fd, writeBuffer, writeLength) != writeLength) { 483 LOGE("write failed"); 484 // wait for pending read before failing 485 if (readBuffer) 486 mData.readDataWait(mEndpointIn); 487 goto fail; 488 } 489 writeBuffer = NULL; 490 } 491 492 // wait for read to complete 493 if (readBuffer) { 494 int read = mData.readDataWait(mEndpointIn); 495 if (read < 0) 496 goto fail; 497 498 writeBuffer = readBuffer; 499 writeLength = read; 500 remaining -= read; 501 readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1); 502 } 503 } 504 505 MtpResponseCode response = readResponse(); 506 if (response == MTP_RESPONSE_OK) 507 result = true; 508 } 509 510fail: 511 ::close(fd); 512 return result; 513} 514 515bool MtpDevice::sendRequest(MtpOperationCode operation) { 516 LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation)); 517 mReceivedResponse = false; 518 mRequest.setOperationCode(operation); 519 if (mTransactionID > 0) 520 mRequest.setTransactionID(mTransactionID++); 521 int ret = mRequest.write(mEndpointOut); 522 mRequest.dump(); 523 return (ret > 0); 524} 525 526bool MtpDevice::sendData() { 527 LOGV("sendData\n"); 528 mData.setOperationCode(mRequest.getOperationCode()); 529 mData.setTransactionID(mRequest.getTransactionID()); 530 int ret = mData.write(mEndpointOut); 531 mData.dump(); 532 return (ret > 0); 533} 534 535bool MtpDevice::readData() { 536 mData.reset(); 537 int ret = mData.read(mEndpointIn); 538 LOGV("readData returned %d\n", ret); 539 if (ret >= MTP_CONTAINER_HEADER_SIZE) { 540 if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) { 541 LOGD("got response packet instead of data packet"); 542 // we got a response packet rather than data 543 // copy it to mResponse 544 mResponse.copyFrom(mData); 545 mReceivedResponse = true; 546 return false; 547 } 548 mData.dump(); 549 return true; 550 } 551 else { 552 LOGV("readResponse failed\n"); 553 return false; 554 } 555} 556 557bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) { 558 mData.setOperationCode(operation); 559 mData.setTransactionID(mRequest.getTransactionID()); 560 return (!mData.writeDataHeader(mEndpointOut, dataLength)); 561} 562 563MtpResponseCode MtpDevice::readResponse() { 564 LOGV("readResponse\n"); 565 if (mReceivedResponse) { 566 mReceivedResponse = false; 567 return mResponse.getResponseCode(); 568 } 569 int ret = mResponse.read(mEndpointIn); 570 if (ret >= MTP_CONTAINER_HEADER_SIZE) { 571 mResponse.dump(); 572 return mResponse.getResponseCode(); 573 } else { 574 LOGD("readResponse failed\n"); 575 return -1; 576 } 577} 578 579} // namespace android 580