blob: 50a839ea17e8997577c7812b21f733cfa1990804 [file] [log] [blame]
Mike Lockwood16864ba2010-05-11 17:16:59 -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
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
24
Mike Lockwoodc42aa122010-06-14 17:58:08 -070025#include <cutils/properties.h>
26
Mike Lockwood16864ba2010-05-11 17:16:59 -040027#include "MtpDebug.h"
Mike Lockwood7f53a192010-07-09 10:45:22 -040028#include "MtpDatabase.h"
Mike Lockwood21ef7d02010-06-30 17:00:35 -040029#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040030#include "MtpServer.h"
31#include "MtpStorage.h"
32#include "MtpStringBuffer.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040033
Mike Lockwood8065e202010-07-15 13:36:52 -040034#include <linux/usb/f_mtp.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040035
Mike Lockwood7850ef92010-05-14 10:10:36 -040036namespace android {
37
Mike Lockwood16864ba2010-05-11 17:16:59 -040038static const MtpOperationCode kSupportedOperationCodes[] = {
39 MTP_OPERATION_GET_DEVICE_INFO,
40 MTP_OPERATION_OPEN_SESSION,
41 MTP_OPERATION_CLOSE_SESSION,
42 MTP_OPERATION_GET_STORAGE_IDS,
43 MTP_OPERATION_GET_STORAGE_INFO,
44 MTP_OPERATION_GET_NUM_OBJECTS,
45 MTP_OPERATION_GET_OBJECT_HANDLES,
46 MTP_OPERATION_GET_OBJECT_INFO,
47 MTP_OPERATION_GET_OBJECT,
48// MTP_OPERATION_GET_THUMB,
49 MTP_OPERATION_DELETE_OBJECT,
50 MTP_OPERATION_SEND_OBJECT_INFO,
51 MTP_OPERATION_SEND_OBJECT,
52// MTP_OPERATION_INITIATE_CAPTURE,
53// MTP_OPERATION_FORMAT_STORE,
54// MTP_OPERATION_RESET_DEVICE,
55// MTP_OPERATION_SELF_TEST,
56// MTP_OPERATION_SET_OBJECT_PROTECTION,
57// MTP_OPERATION_POWER_DOWN,
Mike Lockwood343af4e2010-08-02 10:52:20 -040058// MTP_OPERATION_GET_DEVICE_PROP_DESC,
59// MTP_OPERATION_GET_DEVICE_PROP_VALUE,
60// MTP_OPERATION_SET_DEVICE_PROP_VALUE,
61// MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040062// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
63// MTP_OPERATION_MOVE_OBJECT,
64// MTP_OPERATION_COPY_OBJECT,
65// MTP_OPERATION_GET_PARTIAL_OBJECT,
66// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
67 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
68// MTP_OPERATION_GET_OBJECT_PROP_DESC,
69 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
Mike Lockwood343af4e2010-08-02 10:52:20 -040070// MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040071// MTP_OPERATION_GET_OBJECT_REFERENCES,
72// MTP_OPERATION_SET_OBJECT_REFERENCES,
73// MTP_OPERATION_SKIP,
74};
75
Mike Lockwood873871f2010-07-12 18:54:16 -040076static const MtpEventCode kSupportedEventCodes[] = {
77 MTP_EVENT_OBJECT_ADDED,
78 MTP_EVENT_OBJECT_REMOVED,
79};
80
Mike Lockwood16864ba2010-05-11 17:16:59 -040081static const MtpObjectProperty kSupportedObjectProperties[] = {
82 MTP_PROPERTY_STORAGE_ID,
83 MTP_PROPERTY_OBJECT_FORMAT,
84 MTP_PROPERTY_OBJECT_SIZE,
85 MTP_PROPERTY_OBJECT_FILE_NAME,
86 MTP_PROPERTY_PARENT_OBJECT,
87};
88
89static const MtpObjectFormat kSupportedPlaybackFormats[] = {
Mike Lockwoodfceef462010-05-14 15:35:17 -040090 // MTP_FORMAT_UNDEFINED,
Mike Lockwood16864ba2010-05-11 17:16:59 -040091 MTP_FORMAT_ASSOCIATION,
Mike Lockwoodfceef462010-05-14 15:35:17 -040092 // MTP_FORMAT_TEXT,
93 // MTP_FORMAT_HTML,
Mike Lockwood16864ba2010-05-11 17:16:59 -040094 MTP_FORMAT_MP3,
Mike Lockwoodfceef462010-05-14 15:35:17 -040095 //MTP_FORMAT_AVI,
96 MTP_FORMAT_MPEG,
97 // MTP_FORMAT_ASF,
98 MTP_FORMAT_EXIF_JPEG,
99 MTP_FORMAT_TIFF_EP,
100 // MTP_FORMAT_BMP,
101 MTP_FORMAT_GIF,
102 MTP_FORMAT_JFIF,
103 MTP_FORMAT_PNG,
104 MTP_FORMAT_TIFF,
105 MTP_FORMAT_WMA,
106 MTP_FORMAT_OGG,
107 MTP_FORMAT_AAC,
108 // MTP_FORMAT_FLAC,
109 // MTP_FORMAT_WMV,
110 MTP_FORMAT_MP4_CONTAINER,
111 MTP_FORMAT_MP2,
112 MTP_FORMAT_3GP_CONTAINER,
113 // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
114 // MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
115 // MTP_FORMAT_WPL_PLAYLIST,
116 // MTP_FORMAT_M3U_PLAYLIST,
117 // MTP_FORMAT_MPL_PLAYLIST,
118 // MTP_FORMAT_PLS_PLAYLIST,
Mike Lockwood16864ba2010-05-11 17:16:59 -0400119};
120
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400121MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400122 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -0400123 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400124 mDatabase(database),
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400125 mFileGroup(fileGroup),
126 mFilePermission(filePerm),
127 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400128 mSessionID(0),
129 mSessionOpen(false),
130 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -0400131 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400132 mSendObjectFileSize(0)
133{
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400134 initObjectProperties();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400135}
136
137MtpServer::~MtpServer() {
138}
139
140void MtpServer::addStorage(const char* filePath) {
141 int index = mStorages.size() + 1;
142 index |= index << 16; // set high and low part to our index
143 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
144 addStorage(storage);
145}
146
147MtpStorage* MtpServer::getStorage(MtpStorageID id) {
148 for (int i = 0; i < mStorages.size(); i++) {
149 MtpStorage* storage = mStorages[i];
150 if (storage->getStorageID() == id)
151 return storage;
152 }
153 return NULL;
154}
155
Mike Lockwood16864ba2010-05-11 17:16:59 -0400156void MtpServer::run() {
157 int fd = mFD;
158
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400159 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400160
161 while (1) {
162 int ret = mRequest.read(fd);
163 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400164 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400165 if (errno == ECANCELED) {
166 // return to top of loop and wait for next command
167 continue;
168 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400169 break;
170 }
171 MtpOperationCode operation = mRequest.getOperationCode();
172 MtpTransactionID transaction = mRequest.getTransactionID();
173
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400174 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400175 mRequest.dump();
176
177 // FIXME need to generalize this
178 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
179 if (dataIn) {
180 int ret = mData.read(fd);
181 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400182 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400183 if (errno == ECANCELED) {
184 // return to top of loop and wait for next command
185 continue;
186 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400187 break;
188 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400189 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400190 mData.dump();
191 } else {
192 mData.reset();
193 }
194
Mike Lockwood916076c2010-06-04 09:49:21 -0400195 if (handleRequest()) {
196 if (!dataIn && mData.hasData()) {
197 mData.setOperationCode(operation);
198 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400199 LOGV("sending data:");
Mike Lockwood916076c2010-06-04 09:49:21 -0400200 mData.dump();
201 ret = mData.write(fd);
202 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400203 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400204 if (errno == ECANCELED) {
205 // return to top of loop and wait for next command
206 continue;
207 }
208 break;
209 }
210 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400211
Mike Lockwood916076c2010-06-04 09:49:21 -0400212 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400213 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400214 ret = mResponse.write(fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400215 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400216 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400217 if (errno == ECANCELED) {
218 // return to top of loop and wait for next command
219 continue;
220 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400221 break;
222 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400223 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400224 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400225 }
226 }
227}
228
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400229MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
230 for (int i = 0; i < mObjectProperties.size(); i++) {
231 MtpProperty* property = mObjectProperties[i];
232 if (property->getPropertyCode() == propCode)
233 return property;
234 }
235 return NULL;
236}
237
238MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
239 for (int i = 0; i < mDeviceProperties.size(); i++) {
240 MtpProperty* property = mDeviceProperties[i];
241 if (property->getPropertyCode() == propCode)
242 return property;
243 }
244 return NULL;
245}
246
Mike Lockwood873871f2010-07-12 18:54:16 -0400247void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400248 if (mSessionOpen) {
249 LOGD("sendObjectAdded %d\n", handle);
250 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
251 mEvent.setTransactionID(mRequest.getTransactionID());
252 mEvent.setParameter(1, handle);
253 int ret = mEvent.write(mFD);
254 LOGD("mEvent.write returned %d\n", ret);
255 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400256}
257
258void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400259 if (mSessionOpen) {
260 LOGD("sendObjectRemoved %d\n", handle);
261 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
262 mEvent.setTransactionID(mRequest.getTransactionID());
263 mEvent.setParameter(1, handle);
264 int ret = mEvent.write(mFD);
265 LOGD("mEvent.write returned %d\n", ret);
266 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400267}
268
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400269void MtpServer::initObjectProperties() {
270 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
271 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
272 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
273 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
274 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
275}
276
Mike Lockwood916076c2010-06-04 09:49:21 -0400277bool MtpServer::handleRequest() {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400278 MtpOperationCode operation = mRequest.getOperationCode();
279 MtpResponseCode response;
280
281 mResponse.reset();
282
283 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
284 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400285 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400286 mSendObjectHandle = kInvalidObjectHandle;
287 }
288
289 switch (operation) {
290 case MTP_OPERATION_GET_DEVICE_INFO:
291 response = doGetDeviceInfo();
292 break;
293 case MTP_OPERATION_OPEN_SESSION:
294 response = doOpenSession();
295 break;
296 case MTP_OPERATION_CLOSE_SESSION:
297 response = doCloseSession();
298 break;
299 case MTP_OPERATION_GET_STORAGE_IDS:
300 response = doGetStorageIDs();
301 break;
302 case MTP_OPERATION_GET_STORAGE_INFO:
303 response = doGetStorageInfo();
304 break;
305 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
306 response = doGetObjectPropsSupported();
307 break;
308 case MTP_OPERATION_GET_OBJECT_HANDLES:
309 response = doGetObjectHandles();
310 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400311 case MTP_OPERATION_GET_NUM_OBJECTS:
312 response = doGetNumObjects();
313 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400314 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
315 response = doGetObjectPropValue();
316 break;
317 case MTP_OPERATION_GET_OBJECT_INFO:
318 response = doGetObjectInfo();
319 break;
320 case MTP_OPERATION_GET_OBJECT:
321 response = doGetObject();
322 break;
323 case MTP_OPERATION_SEND_OBJECT_INFO:
324 response = doSendObjectInfo();
325 break;
326 case MTP_OPERATION_SEND_OBJECT:
327 response = doSendObject();
328 break;
329 case MTP_OPERATION_DELETE_OBJECT:
330 response = doDeleteObject();
331 break;
332 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400333 response = doGetObjectPropDesc();
334 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400335 default:
336 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
337 break;
338 }
339
Mike Lockwood916076c2010-06-04 09:49:21 -0400340 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
341 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400342 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400343 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400344}
345
346MtpResponseCode MtpServer::doGetDeviceInfo() {
347 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700348 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400349
350 // fill in device info
351 mData.putUInt16(MTP_STANDARD_VERSION);
352 mData.putUInt32(6); // MTP Vendor Extension ID
353 mData.putUInt16(MTP_STANDARD_VERSION);
354 string.set("microsoft.com: 1.0;");
355 mData.putString(string); // MTP Extensions
356 mData.putUInt16(0); //Functional Mode
357 mData.putAUInt16(kSupportedOperationCodes,
358 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400359 mData.putAUInt16(kSupportedEventCodes,
360 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood16864ba2010-05-11 17:16:59 -0400361 mData.putEmptyArray(); // Device Properties Supported
362 mData.putEmptyArray(); // Capture Formats
363 mData.putAUInt16(kSupportedPlaybackFormats,
364 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
365 // FIXME
366 string.set("Google, Inc.");
367 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700368
369 property_get("ro.product.model", prop_value, "MTP Device");
370 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400371 mData.putString(string); // Model
372 string.set("1.0");
373 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700374
375 property_get("ro.serialno", prop_value, "????????");
376 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400377 mData.putString(string); // Serial Number
378
379 return MTP_RESPONSE_OK;
380}
381
382MtpResponseCode MtpServer::doOpenSession() {
383 if (mSessionOpen) {
384 mResponse.setParameter(1, mSessionID);
385 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
386 }
387 mSessionID = mRequest.getParameter(1);
388 mSessionOpen = true;
389 return MTP_RESPONSE_OK;
390}
391
392MtpResponseCode MtpServer::doCloseSession() {
393 if (!mSessionOpen)
394 return MTP_RESPONSE_SESSION_NOT_OPEN;
395 mSessionID = 0;
396 mSessionOpen = false;
397 return MTP_RESPONSE_OK;
398}
399
400MtpResponseCode MtpServer::doGetStorageIDs() {
401 if (!mSessionOpen)
402 return MTP_RESPONSE_SESSION_NOT_OPEN;
403
404 int count = mStorages.size();
405 mData.putUInt32(count);
406 for (int i = 0; i < count; i++)
407 mData.putUInt32(mStorages[i]->getStorageID());
408
409 return MTP_RESPONSE_OK;
410}
411
412MtpResponseCode MtpServer::doGetStorageInfo() {
413 MtpStringBuffer string;
414
415 if (!mSessionOpen)
416 return MTP_RESPONSE_SESSION_NOT_OPEN;
417 MtpStorageID id = mRequest.getParameter(1);
418 MtpStorage* storage = getStorage(id);
419 if (!storage)
420 return MTP_RESPONSE_INVALID_STORAGE_ID;
421
422 mData.putUInt16(storage->getType());
423 mData.putUInt16(storage->getFileSystemType());
424 mData.putUInt16(storage->getAccessCapability());
425 mData.putUInt64(storage->getMaxCapacity());
426 mData.putUInt64(storage->getFreeSpace());
427 mData.putUInt32(1024*1024*1024); // Free Space in Objects
428 string.set(storage->getDescription());
429 mData.putString(string);
430 mData.putEmptyString(); // Volume Identifier
431
432 return MTP_RESPONSE_OK;
433}
434
435MtpResponseCode MtpServer::doGetObjectPropsSupported() {
436 if (!mSessionOpen)
437 return MTP_RESPONSE_SESSION_NOT_OPEN;
438 MtpObjectFormat format = mRequest.getParameter(1);
439 mData.putAUInt16(kSupportedObjectProperties,
440 sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
441 return MTP_RESPONSE_OK;
442}
443
444MtpResponseCode MtpServer::doGetObjectHandles() {
445 if (!mSessionOpen)
446 return MTP_RESPONSE_SESSION_NOT_OPEN;
447 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400448 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400449 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
450 // 0x00000000 for all objects?
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400451 if (parent == 0xFFFFFFFF)
452 parent = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400453
454 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
455 mData.putAUInt32(handles);
456 delete handles;
457 return MTP_RESPONSE_OK;
458}
459
Mike Lockwood343af4e2010-08-02 10:52:20 -0400460MtpResponseCode MtpServer::doGetNumObjects() {
461 if (!mSessionOpen)
462 return MTP_RESPONSE_SESSION_NOT_OPEN;
463 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
464 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
465 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
466 // 0x00000000 for all objects?
467 if (parent == 0xFFFFFFFF)
468 parent = 0;
469
470 int count = mDatabase->getNumObjects(storageID, format, parent);
471 if (count >= 0) {
472 mResponse.setParameter(1, count);
473 return MTP_RESPONSE_OK;
474 } else {
475 mResponse.setParameter(1, 0);
476 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
477 }
478}
479
Mike Lockwood16864ba2010-05-11 17:16:59 -0400480MtpResponseCode MtpServer::doGetObjectPropValue() {
481 MtpObjectHandle handle = mRequest.getParameter(1);
482 MtpObjectProperty property = mRequest.getParameter(2);
483
484 return mDatabase->getObjectProperty(handle, property, mData);
485}
486
487MtpResponseCode MtpServer::doGetObjectInfo() {
488 MtpObjectHandle handle = mRequest.getParameter(1);
489 return mDatabase->getObjectInfo(handle, mData);
490}
491
492MtpResponseCode MtpServer::doGetObject() {
493 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400494 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400495 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400496 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
497 if (result != MTP_RESPONSE_OK)
498 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400499
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400500 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400501 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400502 mfr.fd = open(filePath, O_RDONLY);
503 if (mfr.fd < 0) {
504 return MTP_RESPONSE_GENERAL_ERROR;
505 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400506 mfr.offset = 0;
507 mfr.length = fileLength;
508
509 // send data header
510 mData.setOperationCode(mRequest.getOperationCode());
511 mData.setTransactionID(mRequest.getTransactionID());
512 mData.writeDataHeader(mFD, fileLength);
513
514 // then transfer the file
515 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400516 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400517 if (ret < 0) {
518 if (errno == ECANCELED)
519 return MTP_RESPONSE_TRANSACTION_CANCELLED;
520 else
521 return MTP_RESPONSE_GENERAL_ERROR;
522 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400523 return MTP_RESPONSE_OK;
524}
525
526MtpResponseCode MtpServer::doSendObjectInfo() {
527 MtpString path;
528 MtpStorageID storageID = mRequest.getParameter(1);
529 MtpStorage* storage = getStorage(storageID);
530 MtpObjectHandle parent = mRequest.getParameter(2);
531 if (!storage)
532 return MTP_RESPONSE_INVALID_STORAGE_ID;
533
534 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400535 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400536 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400537 parent = 0;
538 } else {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400539 int64_t dummy;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400540 int result = mDatabase->getObjectFilePath(parent, path, dummy);
541 if (result != MTP_RESPONSE_OK)
542 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400543 }
544
545 // read only the fields we need
546 mData.getUInt32(); // storage ID
547 MtpObjectFormat format = mData.getUInt16();
548 mData.getUInt16(); // protection status
549 mSendObjectFileSize = mData.getUInt32();
550 mData.getUInt16(); // thumb format
551 mData.getUInt32(); // thumb compressed size
552 mData.getUInt32(); // thumb pix width
553 mData.getUInt32(); // thumb pix height
554 mData.getUInt32(); // image pix width
555 mData.getUInt32(); // image pix height
556 mData.getUInt32(); // image bit depth
557 mData.getUInt32(); // parent
558 uint16_t associationType = mData.getUInt16();
559 uint32_t associationDesc = mData.getUInt32(); // association desc
560 mData.getUInt32(); // sequence number
561 MtpStringBuffer name, created, modified;
562 mData.getString(name); // file name
563 mData.getString(created); // date created
564 mData.getString(modified); // date modified
565 // keywords follow
566
Mike Lockwoodfceef462010-05-14 15:35:17 -0400567 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400568 if (!parseDateTime(modified, modifiedTime))
569 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400570
571 if (path[path.size() - 1] != '/')
572 path += "/";
573 path += (const char *)name;
574
Mike Lockwood4714b072010-07-12 08:49:01 -0400575 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
576 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400577 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400578 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400579 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400580
581 if (format == MTP_FORMAT_ASSOCIATION) {
582 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400583 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400584 umask(mask);
585 if (ret && ret != -EEXIST)
586 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400587 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400588 } else {
589 mSendObjectFilePath = path;
590 // save the handle for the SendObject call, which should follow
591 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400592 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400593 }
594
595 mResponse.setParameter(1, storageID);
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400596 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400597 mResponse.setParameter(3, handle);
598
599 return MTP_RESPONSE_OK;
600}
601
602MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood4714b072010-07-12 08:49:01 -0400603 MtpResponseCode result = MTP_RESPONSE_OK;
604 mode_t mask;
605 int ret;
606
Mike Lockwood16864ba2010-05-11 17:16:59 -0400607 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400608 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400609 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
610 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400611 }
612
613 // read the header
Mike Lockwood4714b072010-07-12 08:49:01 -0400614 ret = mData.readDataHeader(mFD);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400615 // FIXME - check for errors here.
616
617 // reset so we don't attempt to send this back
618 mData.reset();
619
620 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400621 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
622 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400623 result = MTP_RESPONSE_GENERAL_ERROR;
624 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400625 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400626 fchown(mfr.fd, getuid(), mFileGroup);
627 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400628 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400629 fchmod(mfr.fd, mFilePermission);
630 umask(mask);
631
Mike Lockwood16864ba2010-05-11 17:16:59 -0400632 mfr.offset = 0;
633 mfr.length = mSendObjectFileSize;
634
635 // transfer the file
636 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400637 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400638
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400639 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400640
Mike Lockwood916076c2010-06-04 09:49:21 -0400641 if (ret < 0) {
642 unlink(mSendObjectFilePath);
643 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400644 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400645 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400646 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400647 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400648
649done:
650 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
651 result == MTP_RESPONSE_OK);
652 mSendObjectHandle = kInvalidObjectHandle;
653 mSendObjectFormat = 0;
654 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400655}
656
657MtpResponseCode MtpServer::doDeleteObject() {
658 MtpObjectHandle handle = mRequest.getParameter(1);
659 MtpObjectFormat format = mRequest.getParameter(1);
660 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
661 // FIXME - implement deleting objects by format
662 // FIXME - handle non-empty directories
663
664 MtpString filePath;
665 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400666 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
667 if (result == MTP_RESPONSE_OK) {
668 LOGV("deleting %s", (const char *)filePath);
669 // one of these should work
670 rmdir((const char *)filePath);
671 unlink((const char *)filePath);
672 return mDatabase->deleteFile(handle);
673 } else {
674 return result;
675 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400676}
677
678MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400679 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400680 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400681 MtpProperty* property = getObjectProperty(propCode);
682 if (!property)
683 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400684
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400685 property->write(mData);
686 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400687}
Mike Lockwood7850ef92010-05-14 10:10:36 -0400688
689} // namespace android