MtpDevice.cpp revision e3e76c456baee122de6715ae280130abaddc906c
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 351class ReadObjectThread : public Thread { 352private: 353 MtpDevice* mDevice; 354 MtpObjectHandle mHandle; 355 int mObjectSize; 356 void* mInitialData; 357 int mInitialDataLength; 358 int mFD; 359 360public: 361 ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize) 362 : mDevice(device), 363 mHandle(handle), 364 mObjectSize(objectSize), 365 mInitialData(NULL), 366 mInitialDataLength(0) 367 { 368 } 369 370 virtual ~ReadObjectThread() { 371 if (mFD >= 0) 372 close(mFD); 373 free(mInitialData); 374 } 375 376 // returns file descriptor 377 int init() { 378 mDevice->mRequest.reset(); 379 mDevice->mRequest.setParameter(1, mHandle); 380 if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT) 381 && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) { 382 383 // mData will contain header and possibly the beginning of the object data 384 mInitialData = mDevice->mData.getData(mInitialDataLength); 385 386 // create a pipe for the client to read from 387 int pipefd[2]; 388 if (pipe(pipefd) < 0) { 389 LOGE("pipe failed (%s)", strerror(errno)); 390 return -1; 391 } 392 393 mFD = pipefd[1]; 394 return pipefd[0]; 395 } else { 396 return -1; 397 } 398 } 399 400 virtual bool threadLoop() { 401 int remaining = mObjectSize; 402 if (mInitialData) { 403 write(mFD, mInitialData, mInitialDataLength); 404 remaining -= mInitialDataLength; 405 free(mInitialData); 406 mInitialData = NULL; 407 } 408 409 char buffer[16384]; 410 while (remaining > 0) { 411 int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining); 412 int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize); 413 int written; 414 if (count >= 0) { 415 int written = write(mFD, buffer, count); 416 // FIXME check error 417 remaining -= count; 418 } else { 419 break; 420 } 421 } 422 423 MtpResponseCode ret = mDevice->readResponse(); 424 mDevice->mMutex.unlock(); 425 return false; 426 } 427}; 428 429 // returns the file descriptor for a pipe to read the object's data 430int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) { 431 mMutex.lock(); 432 433 ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize); 434 int fd = thread->init(); 435 if (fd < 0) { 436 delete thread; 437 mMutex.unlock(); 438 } else { 439 thread->run("ReadObjectThread"); 440 } 441 return fd; 442} 443 444bool MtpDevice::sendRequest(MtpOperationCode operation) { 445 LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation)); 446 mRequest.setOperationCode(operation); 447 if (mTransactionID > 0) 448 mRequest.setTransactionID(mTransactionID++); 449 int ret = mRequest.write(mEndpointOut); 450 mRequest.dump(); 451 return (ret > 0); 452} 453 454bool MtpDevice::sendData() { 455 LOGV("sendData\n"); 456 mData.setOperationCode(mRequest.getOperationCode()); 457 mData.setTransactionID(mRequest.getTransactionID()); 458 int ret = mData.write(mEndpointOut); 459 mData.dump(); 460 return (ret > 0); 461} 462 463bool MtpDevice::readData() { 464 mData.reset(); 465 int ret = mData.read(mEndpointIn); 466 LOGV("readData returned %d\n", ret); 467 if (ret >= MTP_CONTAINER_HEADER_SIZE) { 468 mData.dump(); 469 return true; 470 } 471 else { 472 LOGV("readResponse failed\n"); 473 return false; 474 } 475} 476 477bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) { 478 mData.setOperationCode(operation); 479 mData.setTransactionID(mRequest.getTransactionID()); 480 return (!mData.writeDataHeader(mEndpointOut, dataLength)); 481} 482 483MtpResponseCode MtpDevice::readResponse() { 484 LOGV("readResponse\n"); 485 int ret = mResponse.read(mEndpointIn); 486 if (ret >= MTP_CONTAINER_HEADER_SIZE) { 487 mResponse.dump(); 488 return mResponse.getResponseCode(); 489 } 490 else { 491 LOGD("readResponse failed\n"); 492 return -1; 493 } 494} 495 496} // namespace android 497