MtpDevice.cpp revision 0cf89f2e622aa53f31fa5762ca4bc805bb509ed3
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 printf("MTP_OPERATION_SEND_OBJECT_INFO sent\n"); 268 MtpResponseCode ret = readResponse(); 269 printf("sendObjectInfo response: %04X\n", ret); 270 if (ret == MTP_RESPONSE_OK) { 271 info->mStorageID = mResponse.getParameter(1); 272 info->mParent = mResponse.getParameter(2); 273 info->mHandle = mResponse.getParameter(3); 274 return info->mHandle; 275 } 276 } 277 return (MtpObjectHandle)-1; 278} 279 280bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) { 281 Mutex::Autolock autoLock(mMutex); 282 283 int remaining = info->mCompressedSize; 284 mRequest.reset(); 285 mRequest.setParameter(1, info->mHandle); 286 if (sendRequest(MTP_OPERATION_SEND_OBJECT)) { 287 printf("MTP_OPERATION_SEND_OBJECT sent\n"); 288 // send data header 289 writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining); 290 291 char buffer[65536]; 292 while (remaining > 0) { 293 int count = read(srcFD, buffer, sizeof(buffer)); 294 if (count > 0) { 295 int written = mData.write(mEndpointOut, buffer, count); 296 printf("wrote %d\n", written); 297 // FIXME check error 298 remaining -= count; 299 } else { 300 break; 301 } 302 } 303 } 304 MtpResponseCode ret = readResponse(); 305 return (remaining == 0 && ret == MTP_RESPONSE_OK); 306} 307 308bool MtpDevice::deleteObject(MtpObjectHandle handle) { 309 Mutex::Autolock autoLock(mMutex); 310 311 mRequest.reset(); 312 mRequest.setParameter(1, handle); 313 if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) { 314 MtpResponseCode ret = readResponse(); 315 if (ret == MTP_RESPONSE_OK) 316 return true; 317 } 318 return false; 319} 320 321MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) { 322 MtpObjectInfo* info = getObjectInfo(handle); 323 if (info) 324 return info->mParent; 325 else 326 return -1; 327} 328 329MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) { 330 MtpObjectInfo* info = getObjectInfo(handle); 331 if (info) 332 return info->mStorageID; 333 else 334 return -1; 335} 336 337MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) { 338 Mutex::Autolock autoLock(mMutex); 339 340 mRequest.reset(); 341 mRequest.setParameter(1, code); 342 if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC)) 343 return NULL; 344 if (!readData()) 345 return NULL; 346 MtpResponseCode ret = readResponse(); 347 if (ret == MTP_RESPONSE_OK) { 348 MtpProperty* property = new MtpProperty; 349 property->read(mData, true); 350 return property; 351 } 352 return NULL; 353} 354 355class ReadObjectThread : public Thread { 356private: 357 MtpDevice* mDevice; 358 MtpObjectHandle mHandle; 359 int mObjectSize; 360 void* mInitialData; 361 int mInitialDataLength; 362 int mFD; 363 364public: 365 ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize) 366 : mDevice(device), 367 mHandle(handle), 368 mObjectSize(objectSize), 369 mInitialData(NULL), 370 mInitialDataLength(0) 371 { 372 } 373 374 virtual ~ReadObjectThread() { 375 if (mFD >= 0) 376 close(mFD); 377 free(mInitialData); 378 } 379 380 // returns file descriptor 381 int init() { 382 mDevice->mRequest.reset(); 383 mDevice->mRequest.setParameter(1, mHandle); 384 if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT) 385 && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) { 386 387 // mData will contain header and possibly the beginning of the object data 388 mInitialData = mDevice->mData.getData(mInitialDataLength); 389 390 // create a pipe for the client to read from 391 int pipefd[2]; 392 if (pipe(pipefd) < 0) { 393 LOGE("pipe failed (%s)", strerror(errno)); 394 return -1; 395 } 396 397 mFD = pipefd[1]; 398 return pipefd[0]; 399 } else { 400 return -1; 401 } 402 } 403 404 virtual bool threadLoop() { 405 int remaining = mObjectSize; 406 if (mInitialData) { 407 write(mFD, mInitialData, mInitialDataLength); 408 remaining -= mInitialDataLength; 409 free(mInitialData); 410 mInitialData = NULL; 411 } 412 413 char buffer[65536]; 414 while (remaining > 0) { 415 int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining); 416 int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize); 417 int written; 418 if (count >= 0) { 419 int written = write(mFD, buffer, count); 420 // FIXME check error 421 remaining -= count; 422 } else { 423 break; 424 } 425 } 426 427 MtpResponseCode ret = mDevice->readResponse(); 428 mDevice->mMutex.unlock(); 429 return false; 430 } 431}; 432 433 // returns the file descriptor for a pipe to read the object's data 434int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) { 435 mMutex.lock(); 436 437 ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize); 438 int fd = thread->init(); 439 if (fd < 0) { 440 delete thread; 441 mMutex.unlock(); 442 } else { 443 thread->run("ReadObjectThread"); 444 } 445 return fd; 446} 447 448bool MtpDevice::sendRequest(MtpOperationCode operation) { 449 LOGD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation)); 450 mRequest.setOperationCode(operation); 451 if (mTransactionID > 0) 452 mRequest.setTransactionID(mTransactionID++); 453 int ret = mRequest.write(mEndpointOut); 454 mRequest.dump(); 455 return (ret > 0); 456} 457 458bool MtpDevice::sendData() { 459 LOGD("sendData\n"); 460 mData.setOperationCode(mRequest.getOperationCode()); 461 mData.setTransactionID(mRequest.getTransactionID()); 462 int ret = mData.write(mEndpointOut); 463 mData.dump(); 464 return (ret > 0); 465} 466 467bool MtpDevice::readData() { 468 mData.reset(); 469 int ret = mData.read(mEndpointIn); 470 LOGD("readData returned %d\n", ret); 471 if (ret >= MTP_CONTAINER_HEADER_SIZE) { 472 mData.dump(); 473 return true; 474 } 475 else { 476 LOGD("readResponse failed\n"); 477 return false; 478 } 479} 480 481bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) { 482 mData.setOperationCode(operation); 483 mData.setTransactionID(mRequest.getTransactionID()); 484 return (!mData.writeDataHeader(mEndpointOut, dataLength)); 485} 486 487MtpResponseCode MtpDevice::readResponse() { 488 LOGD("readResponse\n"); 489 int ret = mResponse.read(mEndpointIn); 490 if (ret >= MTP_CONTAINER_HEADER_SIZE) { 491 mResponse.dump(); 492 return mResponse.getResponseCode(); 493 } 494 else { 495 LOGD("readResponse failed\n"); 496 return -1; 497 } 498} 499 500} // namespace android 501