blob: 5f5cadfd98ac05833747f97dd925b84fa26f4017 [file] [log] [blame]
Mike Lockwood56118b52010-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 Lockwood622ccdc2010-06-14 17:58:08 -070025#include <cutils/properties.h>
26
Mike Lockwood56118b52010-05-11 17:16:59 -040027#include "MtpDebug.h"
Mike Lockwoodc5c78532010-07-09 10:45:22 -040028#include "MtpDatabase.h"
Mike Lockwood767c5e42010-06-30 17:00:35 -040029#include "MtpProperty.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040030#include "MtpServer.h"
31#include "MtpStorage.h"
32#include "MtpStringBuffer.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040033
Mike Lockwood9c7fdf52010-07-15 13:36:52 -040034#include <linux/usb/f_mtp.h>
Mike Lockwood56118b52010-05-11 17:16:59 -040035
Mike Lockwood8d3257a2010-05-14 10:10:36 -040036namespace android {
37
Mike Lockwood56118b52010-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 Lockwood7a047c82010-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 Lockwood56118b52010-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 Lockwood7a047c82010-08-02 10:52:20 -040070// MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwood9a2046f2010-08-03 15:30:09 -040071 MTP_OPERATION_GET_OBJECT_REFERENCES,
72 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood56118b52010-05-11 17:16:59 -040073// MTP_OPERATION_SKIP,
74};
75
Mike Lockwoodbe125a52010-07-12 18:54:16 -040076static const MtpEventCode kSupportedEventCodes[] = {
77 MTP_EVENT_OBJECT_ADDED,
78 MTP_EVENT_OBJECT_REMOVED,
79};
80
Mike Lockwood56118b52010-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 Lockwoodd0782672010-05-14 15:35:17 -040090 // MTP_FORMAT_UNDEFINED,
Mike Lockwood56118b52010-05-11 17:16:59 -040091 MTP_FORMAT_ASSOCIATION,
Mike Lockwoodd0782672010-05-14 15:35:17 -040092 // MTP_FORMAT_TEXT,
93 // MTP_FORMAT_HTML,
Mike Lockwood56118b52010-05-11 17:16:59 -040094 MTP_FORMAT_MP3,
Mike Lockwoodd0782672010-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,
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400114 MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
115 MTP_FORMAT_WPL_PLAYLIST,
116 MTP_FORMAT_M3U_PLAYLIST,
Mike Lockwoodd0782672010-05-14 15:35:17 -0400117 // MTP_FORMAT_MPL_PLAYLIST,
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400118 MTP_FORMAT_PLS_PLAYLIST,
Mike Lockwood56118b52010-05-11 17:16:59 -0400119};
120
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400121MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwooddad69272010-07-02 15:15:07 -0400122 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood56118b52010-05-11 17:16:59 -0400123 : mFD(fd),
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400124 mDatabase(database),
Mike Lockwooddad69272010-07-02 15:15:07 -0400125 mFileGroup(fileGroup),
126 mFilePermission(filePerm),
127 mDirectoryPermission(directoryPerm),
Mike Lockwood56118b52010-05-11 17:16:59 -0400128 mSessionID(0),
129 mSessionOpen(false),
130 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwoodd815f792010-07-12 08:49:01 -0400131 mSendObjectFormat(0),
Mike Lockwood56118b52010-05-11 17:16:59 -0400132 mSendObjectFileSize(0)
133{
Mike Lockwood767c5e42010-06-30 17:00:35 -0400134 initObjectProperties();
Mike Lockwood56118b52010-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 Lockwood56118b52010-05-11 17:16:59 -0400156void MtpServer::run() {
157 int fd = mFD;
158
Mike Lockwood767c5e42010-06-30 17:00:35 -0400159 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400160
161 while (1) {
162 int ret = mRequest.read(fd);
163 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400164 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400165 if (errno == ECANCELED) {
166 // return to top of loop and wait for next command
167 continue;
168 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400169 break;
170 }
171 MtpOperationCode operation = mRequest.getOperationCode();
172 MtpTransactionID transaction = mRequest.getTransactionID();
173
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400174 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400175 mRequest.dump();
176
177 // FIXME need to generalize this
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400178 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
179 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES);
Mike Lockwood56118b52010-05-11 17:16:59 -0400180 if (dataIn) {
181 int ret = mData.read(fd);
182 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400183 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400184 if (errno == ECANCELED) {
185 // return to top of loop and wait for next command
186 continue;
187 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400188 break;
189 }
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400190 LOGV("received data:");
Mike Lockwood56118b52010-05-11 17:16:59 -0400191 mData.dump();
192 } else {
193 mData.reset();
194 }
195
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400196 if (handleRequest()) {
197 if (!dataIn && mData.hasData()) {
198 mData.setOperationCode(operation);
199 mData.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400200 LOGV("sending data:");
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400201 mData.dump();
202 ret = mData.write(fd);
203 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400204 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400205 if (errno == ECANCELED) {
206 // return to top of loop and wait for next command
207 continue;
208 }
209 break;
210 }
211 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400212
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400213 mResponse.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400214 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400215 ret = mResponse.write(fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400216 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400217 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400218 if (errno == ECANCELED) {
219 // return to top of loop and wait for next command
220 continue;
221 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400222 break;
223 }
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400224 } else {
Mike Lockwood767c5e42010-06-30 17:00:35 -0400225 LOGV("skipping response\n");
Mike Lockwood56118b52010-05-11 17:16:59 -0400226 }
227 }
228}
229
Mike Lockwood767c5e42010-06-30 17:00:35 -0400230MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
231 for (int i = 0; i < mObjectProperties.size(); i++) {
232 MtpProperty* property = mObjectProperties[i];
233 if (property->getPropertyCode() == propCode)
234 return property;
235 }
236 return NULL;
237}
238
239MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
240 for (int i = 0; i < mDeviceProperties.size(); i++) {
241 MtpProperty* property = mDeviceProperties[i];
242 if (property->getPropertyCode() == propCode)
243 return property;
244 }
245 return NULL;
246}
247
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400248void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwooddc453d42010-07-19 14:29:58 -0400249 if (mSessionOpen) {
250 LOGD("sendObjectAdded %d\n", handle);
251 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
252 mEvent.setTransactionID(mRequest.getTransactionID());
253 mEvent.setParameter(1, handle);
254 int ret = mEvent.write(mFD);
255 LOGD("mEvent.write returned %d\n", ret);
256 }
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400257}
258
259void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwooddc453d42010-07-19 14:29:58 -0400260 if (mSessionOpen) {
261 LOGD("sendObjectRemoved %d\n", handle);
262 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
263 mEvent.setTransactionID(mRequest.getTransactionID());
264 mEvent.setParameter(1, handle);
265 int ret = mEvent.write(mFD);
266 LOGD("mEvent.write returned %d\n", ret);
267 }
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400268}
269
Mike Lockwood767c5e42010-06-30 17:00:35 -0400270void MtpServer::initObjectProperties() {
271 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
272 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
273 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
274 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
275 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
276}
277
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400278bool MtpServer::handleRequest() {
Mike Lockwood56118b52010-05-11 17:16:59 -0400279 MtpOperationCode operation = mRequest.getOperationCode();
280 MtpResponseCode response;
281
282 mResponse.reset();
283
284 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
285 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400286 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood56118b52010-05-11 17:16:59 -0400287 mSendObjectHandle = kInvalidObjectHandle;
288 }
289
290 switch (operation) {
291 case MTP_OPERATION_GET_DEVICE_INFO:
292 response = doGetDeviceInfo();
293 break;
294 case MTP_OPERATION_OPEN_SESSION:
295 response = doOpenSession();
296 break;
297 case MTP_OPERATION_CLOSE_SESSION:
298 response = doCloseSession();
299 break;
300 case MTP_OPERATION_GET_STORAGE_IDS:
301 response = doGetStorageIDs();
302 break;
303 case MTP_OPERATION_GET_STORAGE_INFO:
304 response = doGetStorageInfo();
305 break;
306 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
307 response = doGetObjectPropsSupported();
308 break;
309 case MTP_OPERATION_GET_OBJECT_HANDLES:
310 response = doGetObjectHandles();
311 break;
Mike Lockwood7a047c82010-08-02 10:52:20 -0400312 case MTP_OPERATION_GET_NUM_OBJECTS:
313 response = doGetNumObjects();
314 break;
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400315 case MTP_OPERATION_GET_OBJECT_REFERENCES:
316 response = doGetObjectReferences();
317 break;
318 case MTP_OPERATION_SET_OBJECT_REFERENCES:
319 response = doSetObjectReferences();
320 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400321 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
322 response = doGetObjectPropValue();
323 break;
324 case MTP_OPERATION_GET_OBJECT_INFO:
325 response = doGetObjectInfo();
326 break;
327 case MTP_OPERATION_GET_OBJECT:
328 response = doGetObject();
329 break;
330 case MTP_OPERATION_SEND_OBJECT_INFO:
331 response = doSendObjectInfo();
332 break;
333 case MTP_OPERATION_SEND_OBJECT:
334 response = doSendObject();
335 break;
336 case MTP_OPERATION_DELETE_OBJECT:
337 response = doDeleteObject();
338 break;
339 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood767c5e42010-06-30 17:00:35 -0400340 response = doGetObjectPropDesc();
341 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400342 default:
343 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
344 break;
345 }
346
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400347 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
348 return false;
Mike Lockwood56118b52010-05-11 17:16:59 -0400349 mResponse.setResponseCode(response);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400350 return true;
Mike Lockwood56118b52010-05-11 17:16:59 -0400351}
352
353MtpResponseCode MtpServer::doGetDeviceInfo() {
354 MtpStringBuffer string;
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700355 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood56118b52010-05-11 17:16:59 -0400356
357 // fill in device info
358 mData.putUInt16(MTP_STANDARD_VERSION);
359 mData.putUInt32(6); // MTP Vendor Extension ID
360 mData.putUInt16(MTP_STANDARD_VERSION);
361 string.set("microsoft.com: 1.0;");
362 mData.putString(string); // MTP Extensions
363 mData.putUInt16(0); //Functional Mode
364 mData.putAUInt16(kSupportedOperationCodes,
365 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400366 mData.putAUInt16(kSupportedEventCodes,
367 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood56118b52010-05-11 17:16:59 -0400368 mData.putEmptyArray(); // Device Properties Supported
369 mData.putEmptyArray(); // Capture Formats
370 mData.putAUInt16(kSupportedPlaybackFormats,
371 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
372 // FIXME
373 string.set("Google, Inc.");
374 mData.putString(string); // Manufacturer
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700375
376 property_get("ro.product.model", prop_value, "MTP Device");
377 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400378 mData.putString(string); // Model
379 string.set("1.0");
380 mData.putString(string); // Device Version
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700381
382 property_get("ro.serialno", prop_value, "????????");
383 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400384 mData.putString(string); // Serial Number
385
386 return MTP_RESPONSE_OK;
387}
388
389MtpResponseCode MtpServer::doOpenSession() {
390 if (mSessionOpen) {
391 mResponse.setParameter(1, mSessionID);
392 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
393 }
394 mSessionID = mRequest.getParameter(1);
395 mSessionOpen = true;
396 return MTP_RESPONSE_OK;
397}
398
399MtpResponseCode MtpServer::doCloseSession() {
400 if (!mSessionOpen)
401 return MTP_RESPONSE_SESSION_NOT_OPEN;
402 mSessionID = 0;
403 mSessionOpen = false;
404 return MTP_RESPONSE_OK;
405}
406
407MtpResponseCode MtpServer::doGetStorageIDs() {
408 if (!mSessionOpen)
409 return MTP_RESPONSE_SESSION_NOT_OPEN;
410
411 int count = mStorages.size();
412 mData.putUInt32(count);
413 for (int i = 0; i < count; i++)
414 mData.putUInt32(mStorages[i]->getStorageID());
415
416 return MTP_RESPONSE_OK;
417}
418
419MtpResponseCode MtpServer::doGetStorageInfo() {
420 MtpStringBuffer string;
421
422 if (!mSessionOpen)
423 return MTP_RESPONSE_SESSION_NOT_OPEN;
424 MtpStorageID id = mRequest.getParameter(1);
425 MtpStorage* storage = getStorage(id);
426 if (!storage)
427 return MTP_RESPONSE_INVALID_STORAGE_ID;
428
429 mData.putUInt16(storage->getType());
430 mData.putUInt16(storage->getFileSystemType());
431 mData.putUInt16(storage->getAccessCapability());
432 mData.putUInt64(storage->getMaxCapacity());
433 mData.putUInt64(storage->getFreeSpace());
434 mData.putUInt32(1024*1024*1024); // Free Space in Objects
435 string.set(storage->getDescription());
436 mData.putString(string);
437 mData.putEmptyString(); // Volume Identifier
438
439 return MTP_RESPONSE_OK;
440}
441
442MtpResponseCode MtpServer::doGetObjectPropsSupported() {
443 if (!mSessionOpen)
444 return MTP_RESPONSE_SESSION_NOT_OPEN;
445 MtpObjectFormat format = mRequest.getParameter(1);
446 mData.putAUInt16(kSupportedObjectProperties,
447 sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
448 return MTP_RESPONSE_OK;
449}
450
451MtpResponseCode MtpServer::doGetObjectHandles() {
452 if (!mSessionOpen)
453 return MTP_RESPONSE_SESSION_NOT_OPEN;
454 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwood37433652010-05-19 15:12:14 -0400455 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood56118b52010-05-11 17:16:59 -0400456 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
457 // 0x00000000 for all objects?
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400458 if (parent == 0xFFFFFFFF)
459 parent = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400460
461 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
462 mData.putAUInt32(handles);
463 delete handles;
464 return MTP_RESPONSE_OK;
465}
466
Mike Lockwood7a047c82010-08-02 10:52:20 -0400467MtpResponseCode MtpServer::doGetNumObjects() {
468 if (!mSessionOpen)
469 return MTP_RESPONSE_SESSION_NOT_OPEN;
470 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
471 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
472 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
473 // 0x00000000 for all objects?
474 if (parent == 0xFFFFFFFF)
475 parent = 0;
476
477 int count = mDatabase->getNumObjects(storageID, format, parent);
478 if (count >= 0) {
479 mResponse.setParameter(1, count);
480 return MTP_RESPONSE_OK;
481 } else {
482 mResponse.setParameter(1, 0);
483 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
484 }
485}
486
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400487MtpResponseCode MtpServer::doGetObjectReferences() {
488 if (!mSessionOpen)
489 return MTP_RESPONSE_SESSION_NOT_OPEN;
490 MtpStorageID handle = mRequest.getParameter(1);
491 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
492 if (!handles) {
493 mData.putEmptyArray();
494 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
495 }
496 mData.putAUInt32(handles);
497 delete handles;
498 return MTP_RESPONSE_OK;
499}
500
501MtpResponseCode MtpServer::doSetObjectReferences() {
502 if (!mSessionOpen)
503 return MTP_RESPONSE_SESSION_NOT_OPEN;
504 MtpStorageID handle = mRequest.getParameter(1);
505 MtpObjectHandleList* references = mData.getAUInt32();
506 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
507 delete references;
508 return result;
509}
510
Mike Lockwood56118b52010-05-11 17:16:59 -0400511MtpResponseCode MtpServer::doGetObjectPropValue() {
512 MtpObjectHandle handle = mRequest.getParameter(1);
513 MtpObjectProperty property = mRequest.getParameter(2);
514
515 return mDatabase->getObjectProperty(handle, property, mData);
516}
517
518MtpResponseCode MtpServer::doGetObjectInfo() {
519 MtpObjectHandle handle = mRequest.getParameter(1);
520 return mDatabase->getObjectInfo(handle, mData);
521}
522
523MtpResponseCode MtpServer::doGetObject() {
524 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400525 MtpString pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400526 int64_t fileLength;
Mike Lockwood59c777a2010-08-02 10:37:41 -0400527 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
528 if (result != MTP_RESPONSE_OK)
529 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400530
Mike Lockwood59c777a2010-08-02 10:37:41 -0400531 const char* filePath = (const char *)pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400532 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400533 mfr.fd = open(filePath, O_RDONLY);
534 if (mfr.fd < 0) {
535 return MTP_RESPONSE_GENERAL_ERROR;
536 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400537 mfr.offset = 0;
538 mfr.length = fileLength;
539
540 // send data header
541 mData.setOperationCode(mRequest.getOperationCode());
542 mData.setTransactionID(mRequest.getTransactionID());
543 mData.writeDataHeader(mFD, fileLength);
544
545 // then transfer the file
546 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400547 close(mfr.fd);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400548 if (ret < 0) {
549 if (errno == ECANCELED)
550 return MTP_RESPONSE_TRANSACTION_CANCELLED;
551 else
552 return MTP_RESPONSE_GENERAL_ERROR;
553 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400554 return MTP_RESPONSE_OK;
555}
556
557MtpResponseCode MtpServer::doSendObjectInfo() {
558 MtpString path;
559 MtpStorageID storageID = mRequest.getParameter(1);
560 MtpStorage* storage = getStorage(storageID);
561 MtpObjectHandle parent = mRequest.getParameter(2);
562 if (!storage)
563 return MTP_RESPONSE_INVALID_STORAGE_ID;
564
565 // special case the root
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400566 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400567 path = storage->getPath();
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400568 parent = 0;
569 } else {
Mike Lockwood56118b52010-05-11 17:16:59 -0400570 int64_t dummy;
Mike Lockwood59c777a2010-08-02 10:37:41 -0400571 int result = mDatabase->getObjectFilePath(parent, path, dummy);
572 if (result != MTP_RESPONSE_OK)
573 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400574 }
575
576 // read only the fields we need
577 mData.getUInt32(); // storage ID
578 MtpObjectFormat format = mData.getUInt16();
579 mData.getUInt16(); // protection status
580 mSendObjectFileSize = mData.getUInt32();
581 mData.getUInt16(); // thumb format
582 mData.getUInt32(); // thumb compressed size
583 mData.getUInt32(); // thumb pix width
584 mData.getUInt32(); // thumb pix height
585 mData.getUInt32(); // image pix width
586 mData.getUInt32(); // image pix height
587 mData.getUInt32(); // image bit depth
588 mData.getUInt32(); // parent
589 uint16_t associationType = mData.getUInt16();
590 uint32_t associationDesc = mData.getUInt32(); // association desc
591 mData.getUInt32(); // sequence number
592 MtpStringBuffer name, created, modified;
593 mData.getString(name); // file name
594 mData.getString(created); // date created
595 mData.getString(modified); // date modified
596 // keywords follow
597
Mike Lockwoodd0782672010-05-14 15:35:17 -0400598 time_t modifiedTime;
Mike Lockwood56118b52010-05-11 17:16:59 -0400599 if (!parseDateTime(modified, modifiedTime))
600 modifiedTime = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400601
602 if (path[path.size() - 1] != '/')
603 path += "/";
604 path += (const char *)name;
605
Mike Lockwoodd815f792010-07-12 08:49:01 -0400606 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
607 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodd0782672010-05-14 15:35:17 -0400608 if (handle == kInvalidObjectHandle) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400609 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd0782672010-05-14 15:35:17 -0400610 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400611
612 if (format == MTP_FORMAT_ASSOCIATION) {
613 mode_t mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400614 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood56118b52010-05-11 17:16:59 -0400615 umask(mask);
616 if (ret && ret != -EEXIST)
617 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooddad69272010-07-02 15:15:07 -0400618 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood56118b52010-05-11 17:16:59 -0400619 } else {
620 mSendObjectFilePath = path;
621 // save the handle for the SendObject call, which should follow
622 mSendObjectHandle = handle;
Mike Lockwoodd815f792010-07-12 08:49:01 -0400623 mSendObjectFormat = format;
Mike Lockwood56118b52010-05-11 17:16:59 -0400624 }
625
626 mResponse.setParameter(1, storageID);
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400627 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
Mike Lockwood56118b52010-05-11 17:16:59 -0400628 mResponse.setParameter(3, handle);
629
630 return MTP_RESPONSE_OK;
631}
632
633MtpResponseCode MtpServer::doSendObject() {
Mike Lockwoodd815f792010-07-12 08:49:01 -0400634 MtpResponseCode result = MTP_RESPONSE_OK;
635 mode_t mask;
636 int ret;
637
Mike Lockwood56118b52010-05-11 17:16:59 -0400638 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400639 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwoodd815f792010-07-12 08:49:01 -0400640 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
641 goto done;
Mike Lockwood56118b52010-05-11 17:16:59 -0400642 }
643
644 // read the header
Mike Lockwoodd815f792010-07-12 08:49:01 -0400645 ret = mData.readDataHeader(mFD);
Mike Lockwood56118b52010-05-11 17:16:59 -0400646 // FIXME - check for errors here.
647
648 // reset so we don't attempt to send this back
649 mData.reset();
650
651 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400652 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
653 if (mfr.fd < 0) {
Mike Lockwoodd815f792010-07-12 08:49:01 -0400654 result = MTP_RESPONSE_GENERAL_ERROR;
655 goto done;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400656 }
Mike Lockwooddad69272010-07-02 15:15:07 -0400657 fchown(mfr.fd, getuid(), mFileGroup);
658 // set permissions
Mike Lockwoodd815f792010-07-12 08:49:01 -0400659 mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400660 fchmod(mfr.fd, mFilePermission);
661 umask(mask);
662
Mike Lockwood56118b52010-05-11 17:16:59 -0400663 mfr.offset = 0;
664 mfr.length = mSendObjectFileSize;
665
666 // transfer the file
667 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400668 close(mfr.fd);
Mike Lockwooddad69272010-07-02 15:15:07 -0400669
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400670 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood56118b52010-05-11 17:16:59 -0400671
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400672 if (ret < 0) {
673 unlink(mSendObjectFilePath);
674 if (errno == ECANCELED)
Mike Lockwoodd815f792010-07-12 08:49:01 -0400675 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400676 else
Mike Lockwoodd815f792010-07-12 08:49:01 -0400677 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400678 }
Mike Lockwoodd815f792010-07-12 08:49:01 -0400679
680done:
681 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
682 result == MTP_RESPONSE_OK);
683 mSendObjectHandle = kInvalidObjectHandle;
684 mSendObjectFormat = 0;
685 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400686}
687
688MtpResponseCode MtpServer::doDeleteObject() {
689 MtpObjectHandle handle = mRequest.getParameter(1);
690 MtpObjectFormat format = mRequest.getParameter(1);
691 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
692 // FIXME - implement deleting objects by format
693 // FIXME - handle non-empty directories
694
695 MtpString filePath;
696 int64_t fileLength;
Mike Lockwood59c777a2010-08-02 10:37:41 -0400697 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
698 if (result == MTP_RESPONSE_OK) {
699 LOGV("deleting %s", (const char *)filePath);
700 // one of these should work
701 rmdir((const char *)filePath);
702 unlink((const char *)filePath);
703 return mDatabase->deleteFile(handle);
704 } else {
705 return result;
706 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400707}
708
709MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood767c5e42010-06-30 17:00:35 -0400710 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood56118b52010-05-11 17:16:59 -0400711 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood767c5e42010-06-30 17:00:35 -0400712 MtpProperty* property = getObjectProperty(propCode);
713 if (!property)
714 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood56118b52010-05-11 17:16:59 -0400715
Mike Lockwood767c5e42010-06-30 17:00:35 -0400716 property->write(mData);
717 return MTP_RESPONSE_OK;
Mike Lockwood56118b52010-05-11 17:16:59 -0400718}
Mike Lockwood8d3257a2010-05-14 10:10:36 -0400719
720} // namespace android