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