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