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