blob: 6d2eec3d69107797a935746a9a119fdb8d4f97d1 [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 Lockwood438344f2010-08-03 15:30:09 -040071 MTP_OPERATION_GET_OBJECT_REFERENCES,
72 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood16864ba2010-05-11 17:16:59 -040073// 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 Lockwood1865a5d2010-07-03 00:44:05 -040081MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwood8e2a2802010-07-02 15:15:07 -040082 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -040083 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -040084 mDatabase(database),
Mike Lockwood8e2a2802010-07-02 15:15:07 -040085 mFileGroup(fileGroup),
86 mFilePermission(filePerm),
87 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -040088 mSessionID(0),
89 mSessionOpen(false),
90 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -040091 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -040092 mSendObjectFileSize(0)
93{
Mike Lockwood21ef7d02010-06-30 17:00:35 -040094 initObjectProperties();
Mike Lockwood16864ba2010-05-11 17:16:59 -040095}
96
97MtpServer::~MtpServer() {
98}
99
100void MtpServer::addStorage(const char* filePath) {
101 int index = mStorages.size() + 1;
102 index |= index << 16; // set high and low part to our index
103 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
104 addStorage(storage);
105}
106
107MtpStorage* MtpServer::getStorage(MtpStorageID id) {
108 for (int i = 0; i < mStorages.size(); i++) {
109 MtpStorage* storage = mStorages[i];
110 if (storage->getStorageID() == id)
111 return storage;
112 }
113 return NULL;
114}
115
Mike Lockwood16864ba2010-05-11 17:16:59 -0400116void MtpServer::run() {
117 int fd = mFD;
118
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400119 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400120
121 while (1) {
122 int ret = mRequest.read(fd);
123 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400124 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400125 if (errno == ECANCELED) {
126 // return to top of loop and wait for next command
127 continue;
128 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400129 break;
130 }
131 MtpOperationCode operation = mRequest.getOperationCode();
132 MtpTransactionID transaction = mRequest.getTransactionID();
133
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400134 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400135 mRequest.dump();
136
137 // FIXME need to generalize this
Mike Lockwood438344f2010-08-03 15:30:09 -0400138 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
139 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400140 if (dataIn) {
141 int ret = mData.read(fd);
142 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400143 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400144 if (errno == ECANCELED) {
145 // return to top of loop and wait for next command
146 continue;
147 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400148 break;
149 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400150 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400151 mData.dump();
152 } else {
153 mData.reset();
154 }
155
Mike Lockwood916076c2010-06-04 09:49:21 -0400156 if (handleRequest()) {
157 if (!dataIn && mData.hasData()) {
158 mData.setOperationCode(operation);
159 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400160 LOGV("sending data:");
Mike Lockwood916076c2010-06-04 09:49:21 -0400161 mData.dump();
162 ret = mData.write(fd);
163 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400164 LOGE("request write 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 }
169 break;
170 }
171 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400172
Mike Lockwood916076c2010-06-04 09:49:21 -0400173 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400174 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400175 ret = mResponse.write(fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400176 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400177 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400178 if (errno == ECANCELED) {
179 // return to top of loop and wait for next command
180 continue;
181 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400182 break;
183 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400184 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400185 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400186 }
187 }
188}
189
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400190MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
191 for (int i = 0; i < mObjectProperties.size(); i++) {
192 MtpProperty* property = mObjectProperties[i];
193 if (property->getPropertyCode() == propCode)
194 return property;
195 }
196 return NULL;
197}
198
199MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
200 for (int i = 0; i < mDeviceProperties.size(); i++) {
201 MtpProperty* property = mDeviceProperties[i];
202 if (property->getPropertyCode() == propCode)
203 return property;
204 }
205 return NULL;
206}
207
Mike Lockwood873871f2010-07-12 18:54:16 -0400208void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400209 if (mSessionOpen) {
210 LOGD("sendObjectAdded %d\n", handle);
211 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
212 mEvent.setTransactionID(mRequest.getTransactionID());
213 mEvent.setParameter(1, handle);
214 int ret = mEvent.write(mFD);
215 LOGD("mEvent.write returned %d\n", ret);
216 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400217}
218
219void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400220 if (mSessionOpen) {
221 LOGD("sendObjectRemoved %d\n", handle);
222 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
223 mEvent.setTransactionID(mRequest.getTransactionID());
224 mEvent.setParameter(1, handle);
225 int ret = mEvent.write(mFD);
226 LOGD("mEvent.write returned %d\n", ret);
227 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400228}
229
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400230void MtpServer::initObjectProperties() {
Mike Lockwoodf83caa22010-08-09 14:17:52 -0400231 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32));
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400232 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
233 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
234 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
235 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
236}
237
Mike Lockwood916076c2010-06-04 09:49:21 -0400238bool MtpServer::handleRequest() {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400239 MtpOperationCode operation = mRequest.getOperationCode();
240 MtpResponseCode response;
241
242 mResponse.reset();
243
244 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
245 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400246 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400247 mSendObjectHandle = kInvalidObjectHandle;
248 }
249
250 switch (operation) {
251 case MTP_OPERATION_GET_DEVICE_INFO:
252 response = doGetDeviceInfo();
253 break;
254 case MTP_OPERATION_OPEN_SESSION:
255 response = doOpenSession();
256 break;
257 case MTP_OPERATION_CLOSE_SESSION:
258 response = doCloseSession();
259 break;
260 case MTP_OPERATION_GET_STORAGE_IDS:
261 response = doGetStorageIDs();
262 break;
263 case MTP_OPERATION_GET_STORAGE_INFO:
264 response = doGetStorageInfo();
265 break;
266 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
267 response = doGetObjectPropsSupported();
268 break;
269 case MTP_OPERATION_GET_OBJECT_HANDLES:
270 response = doGetObjectHandles();
271 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400272 case MTP_OPERATION_GET_NUM_OBJECTS:
273 response = doGetNumObjects();
274 break;
Mike Lockwood438344f2010-08-03 15:30:09 -0400275 case MTP_OPERATION_GET_OBJECT_REFERENCES:
276 response = doGetObjectReferences();
277 break;
278 case MTP_OPERATION_SET_OBJECT_REFERENCES:
279 response = doSetObjectReferences();
280 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400281 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
282 response = doGetObjectPropValue();
283 break;
284 case MTP_OPERATION_GET_OBJECT_INFO:
285 response = doGetObjectInfo();
286 break;
287 case MTP_OPERATION_GET_OBJECT:
288 response = doGetObject();
289 break;
290 case MTP_OPERATION_SEND_OBJECT_INFO:
291 response = doSendObjectInfo();
292 break;
293 case MTP_OPERATION_SEND_OBJECT:
294 response = doSendObject();
295 break;
296 case MTP_OPERATION_DELETE_OBJECT:
297 response = doDeleteObject();
298 break;
299 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400300 response = doGetObjectPropDesc();
301 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400302 default:
303 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
304 break;
305 }
306
Mike Lockwood916076c2010-06-04 09:49:21 -0400307 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
308 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400309 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400310 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400311}
312
313MtpResponseCode MtpServer::doGetDeviceInfo() {
314 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700315 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400316
Mike Lockwood782aef12010-08-10 07:37:50 -0400317 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
318 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
319 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
320
Mike Lockwood16864ba2010-05-11 17:16:59 -0400321 // fill in device info
322 mData.putUInt16(MTP_STANDARD_VERSION);
323 mData.putUInt32(6); // MTP Vendor Extension ID
324 mData.putUInt16(MTP_STANDARD_VERSION);
325 string.set("microsoft.com: 1.0;");
326 mData.putString(string); // MTP Extensions
327 mData.putUInt16(0); //Functional Mode
328 mData.putAUInt16(kSupportedOperationCodes,
329 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400330 mData.putAUInt16(kSupportedEventCodes,
331 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood782aef12010-08-10 07:37:50 -0400332 mData.putAUInt16(deviceProperties); // Device Properties Supported
333 mData.putAUInt16(captureFormats); // Capture Formats
334 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400335 // FIXME
336 string.set("Google, Inc.");
337 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700338
339 property_get("ro.product.model", prop_value, "MTP Device");
340 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400341 mData.putString(string); // Model
342 string.set("1.0");
343 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700344
345 property_get("ro.serialno", prop_value, "????????");
346 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400347 mData.putString(string); // Serial Number
348
Mike Lockwood782aef12010-08-10 07:37:50 -0400349 delete playbackFormats;
350 delete captureFormats;
351 delete deviceProperties;
352
Mike Lockwood16864ba2010-05-11 17:16:59 -0400353 return MTP_RESPONSE_OK;
354}
355
356MtpResponseCode MtpServer::doOpenSession() {
357 if (mSessionOpen) {
358 mResponse.setParameter(1, mSessionID);
359 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
360 }
361 mSessionID = mRequest.getParameter(1);
362 mSessionOpen = true;
363 return MTP_RESPONSE_OK;
364}
365
366MtpResponseCode MtpServer::doCloseSession() {
367 if (!mSessionOpen)
368 return MTP_RESPONSE_SESSION_NOT_OPEN;
369 mSessionID = 0;
370 mSessionOpen = false;
371 return MTP_RESPONSE_OK;
372}
373
374MtpResponseCode MtpServer::doGetStorageIDs() {
375 if (!mSessionOpen)
376 return MTP_RESPONSE_SESSION_NOT_OPEN;
377
378 int count = mStorages.size();
379 mData.putUInt32(count);
380 for (int i = 0; i < count; i++)
381 mData.putUInt32(mStorages[i]->getStorageID());
382
383 return MTP_RESPONSE_OK;
384}
385
386MtpResponseCode MtpServer::doGetStorageInfo() {
387 MtpStringBuffer string;
388
389 if (!mSessionOpen)
390 return MTP_RESPONSE_SESSION_NOT_OPEN;
391 MtpStorageID id = mRequest.getParameter(1);
392 MtpStorage* storage = getStorage(id);
393 if (!storage)
394 return MTP_RESPONSE_INVALID_STORAGE_ID;
395
396 mData.putUInt16(storage->getType());
397 mData.putUInt16(storage->getFileSystemType());
398 mData.putUInt16(storage->getAccessCapability());
399 mData.putUInt64(storage->getMaxCapacity());
400 mData.putUInt64(storage->getFreeSpace());
401 mData.putUInt32(1024*1024*1024); // Free Space in Objects
402 string.set(storage->getDescription());
403 mData.putString(string);
404 mData.putEmptyString(); // Volume Identifier
405
406 return MTP_RESPONSE_OK;
407}
408
409MtpResponseCode MtpServer::doGetObjectPropsSupported() {
410 if (!mSessionOpen)
411 return MTP_RESPONSE_SESSION_NOT_OPEN;
412 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood782aef12010-08-10 07:37:50 -0400413 MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
414 mData.putAUInt16(properties);
415 delete[] properties;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400416 return MTP_RESPONSE_OK;
417}
418
419MtpResponseCode MtpServer::doGetObjectHandles() {
420 if (!mSessionOpen)
421 return MTP_RESPONSE_SESSION_NOT_OPEN;
422 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400423 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400424 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
425 // 0x00000000 for all objects?
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400426 if (parent == 0xFFFFFFFF)
427 parent = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400428
429 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
430 mData.putAUInt32(handles);
431 delete handles;
432 return MTP_RESPONSE_OK;
433}
434
Mike Lockwood343af4e2010-08-02 10:52:20 -0400435MtpResponseCode MtpServer::doGetNumObjects() {
436 if (!mSessionOpen)
437 return MTP_RESPONSE_SESSION_NOT_OPEN;
438 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
439 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
440 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
441 // 0x00000000 for all objects?
442 if (parent == 0xFFFFFFFF)
443 parent = 0;
444
445 int count = mDatabase->getNumObjects(storageID, format, parent);
446 if (count >= 0) {
447 mResponse.setParameter(1, count);
448 return MTP_RESPONSE_OK;
449 } else {
450 mResponse.setParameter(1, 0);
451 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
452 }
453}
454
Mike Lockwood438344f2010-08-03 15:30:09 -0400455MtpResponseCode MtpServer::doGetObjectReferences() {
456 if (!mSessionOpen)
457 return MTP_RESPONSE_SESSION_NOT_OPEN;
458 MtpStorageID handle = mRequest.getParameter(1);
459 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
460 if (!handles) {
461 mData.putEmptyArray();
462 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
463 }
464 mData.putAUInt32(handles);
465 delete handles;
466 return MTP_RESPONSE_OK;
467}
468
469MtpResponseCode MtpServer::doSetObjectReferences() {
470 if (!mSessionOpen)
471 return MTP_RESPONSE_SESSION_NOT_OPEN;
472 MtpStorageID handle = mRequest.getParameter(1);
473 MtpObjectHandleList* references = mData.getAUInt32();
474 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
475 delete references;
476 return result;
477}
478
Mike Lockwood16864ba2010-05-11 17:16:59 -0400479MtpResponseCode MtpServer::doGetObjectPropValue() {
480 MtpObjectHandle handle = mRequest.getParameter(1);
481 MtpObjectProperty property = mRequest.getParameter(2);
482
483 return mDatabase->getObjectProperty(handle, property, mData);
484}
485
486MtpResponseCode MtpServer::doGetObjectInfo() {
487 MtpObjectHandle handle = mRequest.getParameter(1);
488 return mDatabase->getObjectInfo(handle, mData);
489}
490
491MtpResponseCode MtpServer::doGetObject() {
492 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400493 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400494 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400495 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
496 if (result != MTP_RESPONSE_OK)
497 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400498
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400499 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400500 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400501 mfr.fd = open(filePath, O_RDONLY);
502 if (mfr.fd < 0) {
503 return MTP_RESPONSE_GENERAL_ERROR;
504 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400505 mfr.offset = 0;
506 mfr.length = fileLength;
507
508 // send data header
509 mData.setOperationCode(mRequest.getOperationCode());
510 mData.setTransactionID(mRequest.getTransactionID());
511 mData.writeDataHeader(mFD, fileLength);
512
513 // then transfer the file
514 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400515 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400516 if (ret < 0) {
517 if (errno == ECANCELED)
518 return MTP_RESPONSE_TRANSACTION_CANCELLED;
519 else
520 return MTP_RESPONSE_GENERAL_ERROR;
521 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400522 return MTP_RESPONSE_OK;
523}
524
525MtpResponseCode MtpServer::doSendObjectInfo() {
526 MtpString path;
527 MtpStorageID storageID = mRequest.getParameter(1);
528 MtpStorage* storage = getStorage(storageID);
529 MtpObjectHandle parent = mRequest.getParameter(2);
530 if (!storage)
531 return MTP_RESPONSE_INVALID_STORAGE_ID;
532
533 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400534 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400535 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400536 parent = 0;
537 } else {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400538 int64_t dummy;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400539 int result = mDatabase->getObjectFilePath(parent, path, dummy);
540 if (result != MTP_RESPONSE_OK)
541 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400542 }
543
544 // read only the fields we need
545 mData.getUInt32(); // storage ID
546 MtpObjectFormat format = mData.getUInt16();
547 mData.getUInt16(); // protection status
548 mSendObjectFileSize = mData.getUInt32();
549 mData.getUInt16(); // thumb format
550 mData.getUInt32(); // thumb compressed size
551 mData.getUInt32(); // thumb pix width
552 mData.getUInt32(); // thumb pix height
553 mData.getUInt32(); // image pix width
554 mData.getUInt32(); // image pix height
555 mData.getUInt32(); // image bit depth
556 mData.getUInt32(); // parent
557 uint16_t associationType = mData.getUInt16();
558 uint32_t associationDesc = mData.getUInt32(); // association desc
559 mData.getUInt32(); // sequence number
560 MtpStringBuffer name, created, modified;
561 mData.getString(name); // file name
562 mData.getString(created); // date created
563 mData.getString(modified); // date modified
564 // keywords follow
565
Mike Lockwoodfceef462010-05-14 15:35:17 -0400566 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400567 if (!parseDateTime(modified, modifiedTime))
568 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400569
570 if (path[path.size() - 1] != '/')
571 path += "/";
572 path += (const char *)name;
573
Mike Lockwood4714b072010-07-12 08:49:01 -0400574 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
575 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400576 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400577 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400578 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400579
580 if (format == MTP_FORMAT_ASSOCIATION) {
581 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400582 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400583 umask(mask);
584 if (ret && ret != -EEXIST)
585 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400586 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400587 } else {
588 mSendObjectFilePath = path;
589 // save the handle for the SendObject call, which should follow
590 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400591 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400592 }
593
594 mResponse.setParameter(1, storageID);
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400595 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400596 mResponse.setParameter(3, handle);
597
598 return MTP_RESPONSE_OK;
599}
600
601MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood4714b072010-07-12 08:49:01 -0400602 MtpResponseCode result = MTP_RESPONSE_OK;
603 mode_t mask;
604 int ret;
605
Mike Lockwood16864ba2010-05-11 17:16:59 -0400606 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400607 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400608 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
609 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400610 }
611
612 // read the header
Mike Lockwood4714b072010-07-12 08:49:01 -0400613 ret = mData.readDataHeader(mFD);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400614 // FIXME - check for errors here.
615
616 // reset so we don't attempt to send this back
617 mData.reset();
618
619 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400620 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
621 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400622 result = MTP_RESPONSE_GENERAL_ERROR;
623 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400624 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400625 fchown(mfr.fd, getuid(), mFileGroup);
626 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400627 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400628 fchmod(mfr.fd, mFilePermission);
629 umask(mask);
630
Mike Lockwood16864ba2010-05-11 17:16:59 -0400631 mfr.offset = 0;
632 mfr.length = mSendObjectFileSize;
633
634 // transfer the file
635 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400636 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400637
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400638 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400639
Mike Lockwood916076c2010-06-04 09:49:21 -0400640 if (ret < 0) {
641 unlink(mSendObjectFilePath);
642 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400643 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400644 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400645 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400646 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400647
648done:
649 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
650 result == MTP_RESPONSE_OK);
651 mSendObjectHandle = kInvalidObjectHandle;
652 mSendObjectFormat = 0;
653 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400654}
655
656MtpResponseCode MtpServer::doDeleteObject() {
657 MtpObjectHandle handle = mRequest.getParameter(1);
658 MtpObjectFormat format = mRequest.getParameter(1);
659 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
660 // FIXME - implement deleting objects by format
661 // FIXME - handle non-empty directories
662
663 MtpString filePath;
664 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400665 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
666 if (result == MTP_RESPONSE_OK) {
667 LOGV("deleting %s", (const char *)filePath);
668 // one of these should work
669 rmdir((const char *)filePath);
670 unlink((const char *)filePath);
671 return mDatabase->deleteFile(handle);
672 } else {
673 return result;
674 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400675}
676
677MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400678 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400679 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400680 MtpProperty* property = getObjectProperty(propCode);
681 if (!property)
682 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400683
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400684 property->write(mData);
685 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400686}
Mike Lockwood7850ef92010-05-14 10:10:36 -0400687
688} // namespace android