blob: fca014286e48b5d962f4f5e389f2b2b7d47fe2cd [file] [log] [blame]
Mike Lockwood755fd612010-05-25 19:08:48 -04001/*
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
Mike Lockwood90f48732010-06-05 22:45:01 -040017#define LOG_TAG "MtpDevice"
Mike Lockwood3e6616d2010-06-29 18:11:52 -040018
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"
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -040026#include "MtpUtils.h"
Mike Lockwood90f48732010-06-05 22:45:01 -040027
Mike Lockwood755fd612010-05-25 19:08:48 -040028#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>
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -040035#include <endian.h>
Mike Lockwood755fd612010-05-25 19:08:48 -040036
37#include <usbhost/usbhost.h>
38
Mike Lockwood755fd612010-05-25 19:08:48 -040039namespace 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();
Mike Lockwood90f48732010-06-05 22:45:01 -040058 for (int i = 0; i < mDeviceProperties.size(); i++)
59 delete mDeviceProperties[i];
Mike Lockwood755fd612010-05-25 19:08:48 -040060}
61
62void MtpDevice::initialize() {
63 openSession();
64 mDeviceInfo = getDeviceInfo();
65 if (mDeviceInfo) {
66 mDeviceInfo->print();
Mike Lockwood90f48732010-06-05 22:45:01 -040067
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 }
Mike Lockwood755fd612010-05-25 19:08:48 -040079 }
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() {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -040098 Mutex::Autolock autoLock(mMutex);
99
Mike Lockwood755fd612010-05-25 19:08:48 -0400100 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() {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400124 Mutex::Autolock autoLock(mMutex);
125
Mike Lockwood755fd612010-05-25 19:08:48 -0400126 mRequest.reset();
127 if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
128 return NULL;
129 if (!readData())
130 return NULL;
131 MtpResponseCode ret = readResponse();
Mike Lockwood755fd612010-05-25 19:08:48 -0400132 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() {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400141 Mutex::Autolock autoLock(mMutex);
142
Mike Lockwood755fd612010-05-25 19:08:48 -0400143 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) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400156 Mutex::Autolock autoLock(mMutex);
157
Mike Lockwood755fd612010-05-25 19:08:48 -0400158 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();
Mike Lockwood755fd612010-05-25 19:08:48 -0400165 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) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400175 Mutex::Autolock autoLock(mMutex);
176
Mike Lockwood755fd612010-05-25 19:08:48 -0400177 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();
Mike Lockwood755fd612010-05-25 19:08:48 -0400186 if (ret == MTP_RESPONSE_OK) {
187 return mData.getAUInt32();
188 }
189 return NULL;
190}
191
192MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400193 Mutex::Autolock autoLock(mMutex);
194
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400195 // FIXME - we might want to add some caching here
196
Mike Lockwood755fd612010-05-25 19:08:48 -0400197 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();
Mike Lockwood755fd612010-05-25 19:08:48 -0400204 if (ret == MTP_RESPONSE_OK) {
205 MtpObjectInfo* info = new MtpObjectInfo(handle);
206 info->read(mData);
207 return info;
208 }
209 return NULL;
210}
211
Mike Lockwooddda56862010-06-10 16:34:20 -0400212void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400213 Mutex::Autolock autoLock(mMutex);
214
Mike Lockwooddda56862010-06-10 16:34:20 -0400215 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;
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400225}
Mike Lockwooddda56862010-06-10 16:34:20 -0400226
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400227MtpObjectHandle 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()) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400267 MtpResponseCode ret = readResponse();
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400268 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)) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400285 // 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);
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400293 // 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
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400304bool MtpDevice::deleteObject(MtpObjectHandle handle) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400305 Mutex::Autolock autoLock(mMutex);
306
Mike Lockwoode0a89f62010-06-11 16:34:52 -0400307 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;
Mike Lockwooddda56862010-06-10 16:34:20 -0400331}
332
Mike Lockwood90f48732010-06-05 22:45:01 -0400333MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400334 Mutex::Autolock autoLock(mMutex);
335
Mike Lockwood90f48732010-06-05 22:45:01 -0400336 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;
Mike Lockwood59e3f0d2010-09-02 14:57:30 -0400345 property->read(mData);
Mike Lockwood90f48732010-06-05 22:45:01 -0400346 return property;
347 }
348 return NULL;
349}
350
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400351class 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
Mike Lockwoodbb2b2a92010-07-27 11:52:45 -0400409 char buffer[16384];
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400410 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}
Mike Lockwood90f48732010-06-05 22:45:01 -0400443
Mike Lockwood755fd612010-05-25 19:08:48 -0400444bool MtpDevice::sendRequest(MtpOperationCode operation) {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400445 LOGV("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
Mike Lockwood755fd612010-05-25 19:08:48 -0400446 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
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400454bool MtpDevice::sendData() {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400455 LOGV("sendData\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400456 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);
Mike Lockwood456d8e62010-07-27 11:50:34 -0400466 LOGV("readData returned %d\n", ret);
Mike Lockwood755fd612010-05-25 19:08:48 -0400467 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
468 mData.dump();
469 return true;
470 }
471 else {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400472 LOGV("readResponse failed\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400473 return false;
474 }
475}
476
Mike Lockwoodbc4cb0b2010-07-26 20:40:45 -0400477bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
478 mData.setOperationCode(operation);
479 mData.setTransactionID(mRequest.getTransactionID());
480 return (!mData.writeDataHeader(mEndpointOut, dataLength));
481}
482
Mike Lockwood755fd612010-05-25 19:08:48 -0400483MtpResponseCode MtpDevice::readResponse() {
Mike Lockwood456d8e62010-07-27 11:50:34 -0400484 LOGV("readResponse\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400485 int ret = mResponse.read(mEndpointIn);
486 if (ret >= MTP_CONTAINER_HEADER_SIZE) {
487 mResponse.dump();
488 return mResponse.getResponseCode();
489 }
490 else {
Mike Lockwood90f48732010-06-05 22:45:01 -0400491 LOGD("readResponse failed\n");
Mike Lockwood755fd612010-05-25 19:08:48 -0400492 return -1;
493 }
494}
495
496} // namespace android