blob: e8f09faebbb6ee1e739c3f6c83ca491095e24c5f [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
34#include "f_mtp.h"
35
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,
58 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,
62// 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,
70 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
71// MTP_OPERATION_GET_OBJECT_REFERENCES,
72// MTP_OPERATION_SET_OBJECT_REFERENCES,
73// MTP_OPERATION_SKIP,
74};
75
76static const MtpObjectProperty kSupportedObjectProperties[] = {
77 MTP_PROPERTY_STORAGE_ID,
78 MTP_PROPERTY_OBJECT_FORMAT,
79 MTP_PROPERTY_OBJECT_SIZE,
80 MTP_PROPERTY_OBJECT_FILE_NAME,
81 MTP_PROPERTY_PARENT_OBJECT,
82};
83
84static const MtpObjectFormat kSupportedPlaybackFormats[] = {
Mike Lockwoodfceef462010-05-14 15:35:17 -040085 // MTP_FORMAT_UNDEFINED,
Mike Lockwood16864ba2010-05-11 17:16:59 -040086 MTP_FORMAT_ASSOCIATION,
Mike Lockwoodfceef462010-05-14 15:35:17 -040087 // MTP_FORMAT_TEXT,
88 // MTP_FORMAT_HTML,
Mike Lockwood16864ba2010-05-11 17:16:59 -040089 MTP_FORMAT_MP3,
Mike Lockwoodfceef462010-05-14 15:35:17 -040090 //MTP_FORMAT_AVI,
91 MTP_FORMAT_MPEG,
92 // MTP_FORMAT_ASF,
93 MTP_FORMAT_EXIF_JPEG,
94 MTP_FORMAT_TIFF_EP,
95 // MTP_FORMAT_BMP,
96 MTP_FORMAT_GIF,
97 MTP_FORMAT_JFIF,
98 MTP_FORMAT_PNG,
99 MTP_FORMAT_TIFF,
100 MTP_FORMAT_WMA,
101 MTP_FORMAT_OGG,
102 MTP_FORMAT_AAC,
103 // MTP_FORMAT_FLAC,
104 // MTP_FORMAT_WMV,
105 MTP_FORMAT_MP4_CONTAINER,
106 MTP_FORMAT_MP2,
107 MTP_FORMAT_3GP_CONTAINER,
108 // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM,
109 // MTP_FORMAT_ABSTRACT_AV_PLAYLIST,
110 // MTP_FORMAT_WPL_PLAYLIST,
111 // MTP_FORMAT_M3U_PLAYLIST,
112 // MTP_FORMAT_MPL_PLAYLIST,
113 // MTP_FORMAT_PLS_PLAYLIST,
Mike Lockwood16864ba2010-05-11 17:16:59 -0400114};
115
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400116MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400117 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -0400118 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400119 mDatabase(database),
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400120 mFileGroup(fileGroup),
121 mFilePermission(filePerm),
122 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400123 mSessionID(0),
124 mSessionOpen(false),
125 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -0400126 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400127 mSendObjectFileSize(0)
128{
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400129 initObjectProperties();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400130}
131
132MtpServer::~MtpServer() {
133}
134
135void MtpServer::addStorage(const char* filePath) {
136 int index = mStorages.size() + 1;
137 index |= index << 16; // set high and low part to our index
138 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
139 addStorage(storage);
140}
141
142MtpStorage* MtpServer::getStorage(MtpStorageID id) {
143 for (int i = 0; i < mStorages.size(); i++) {
144 MtpStorage* storage = mStorages[i];
145 if (storage->getStorageID() == id)
146 return storage;
147 }
148 return NULL;
149}
150
Mike Lockwood16864ba2010-05-11 17:16:59 -0400151void MtpServer::run() {
152 int fd = mFD;
153
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400154 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400155
156 while (1) {
157 int ret = mRequest.read(fd);
158 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400159 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400160 if (errno == ECANCELED) {
161 // return to top of loop and wait for next command
162 continue;
163 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400164 break;
165 }
166 MtpOperationCode operation = mRequest.getOperationCode();
167 MtpTransactionID transaction = mRequest.getTransactionID();
168
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400169 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400170 mRequest.dump();
171
172 // FIXME need to generalize this
173 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
174 if (dataIn) {
175 int ret = mData.read(fd);
176 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400177 LOGE("data read 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 Lockwoodb14e5882010-06-29 18:11:52 -0400184 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400185 mData.dump();
186 } else {
187 mData.reset();
188 }
189
Mike Lockwood916076c2010-06-04 09:49:21 -0400190 if (handleRequest()) {
191 if (!dataIn && mData.hasData()) {
192 mData.setOperationCode(operation);
193 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400194 LOGV("sending data:");
Mike Lockwood916076c2010-06-04 09:49:21 -0400195 mData.dump();
196 ret = mData.write(fd);
197 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400198 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400199 if (errno == ECANCELED) {
200 // return to top of loop and wait for next command
201 continue;
202 }
203 break;
204 }
205 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400206
Mike Lockwood916076c2010-06-04 09:49:21 -0400207 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400208 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400209 ret = mResponse.write(fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400210 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400211 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400212 if (errno == ECANCELED) {
213 // return to top of loop and wait for next command
214 continue;
215 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400216 break;
217 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400218 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400219 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400220 }
221 }
222}
223
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400224MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
225 for (int i = 0; i < mObjectProperties.size(); i++) {
226 MtpProperty* property = mObjectProperties[i];
227 if (property->getPropertyCode() == propCode)
228 return property;
229 }
230 return NULL;
231}
232
233MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
234 for (int i = 0; i < mDeviceProperties.size(); i++) {
235 MtpProperty* property = mDeviceProperties[i];
236 if (property->getPropertyCode() == propCode)
237 return property;
238 }
239 return NULL;
240}
241
242void MtpServer::initObjectProperties() {
243 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
244 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
245 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
246 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
247 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
248}
249
Mike Lockwood916076c2010-06-04 09:49:21 -0400250bool MtpServer::handleRequest() {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400251 MtpOperationCode operation = mRequest.getOperationCode();
252 MtpResponseCode response;
253
254 mResponse.reset();
255
256 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
257 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400258 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400259 mSendObjectHandle = kInvalidObjectHandle;
260 }
261
262 switch (operation) {
263 case MTP_OPERATION_GET_DEVICE_INFO:
264 response = doGetDeviceInfo();
265 break;
266 case MTP_OPERATION_OPEN_SESSION:
267 response = doOpenSession();
268 break;
269 case MTP_OPERATION_CLOSE_SESSION:
270 response = doCloseSession();
271 break;
272 case MTP_OPERATION_GET_STORAGE_IDS:
273 response = doGetStorageIDs();
274 break;
275 case MTP_OPERATION_GET_STORAGE_INFO:
276 response = doGetStorageInfo();
277 break;
278 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
279 response = doGetObjectPropsSupported();
280 break;
281 case MTP_OPERATION_GET_OBJECT_HANDLES:
282 response = doGetObjectHandles();
283 break;
284 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
285 response = doGetObjectPropValue();
286 break;
287 case MTP_OPERATION_GET_OBJECT_INFO:
288 response = doGetObjectInfo();
289 break;
290 case MTP_OPERATION_GET_OBJECT:
291 response = doGetObject();
292 break;
293 case MTP_OPERATION_SEND_OBJECT_INFO:
294 response = doSendObjectInfo();
295 break;
296 case MTP_OPERATION_SEND_OBJECT:
297 response = doSendObject();
298 break;
299 case MTP_OPERATION_DELETE_OBJECT:
300 response = doDeleteObject();
301 break;
302 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400303 response = doGetObjectPropDesc();
304 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400305 default:
306 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
307 break;
308 }
309
Mike Lockwood916076c2010-06-04 09:49:21 -0400310 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
311 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400312 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400313 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400314}
315
316MtpResponseCode MtpServer::doGetDeviceInfo() {
317 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700318 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400319
320 // fill in device info
321 mData.putUInt16(MTP_STANDARD_VERSION);
322 mData.putUInt32(6); // MTP Vendor Extension ID
323 mData.putUInt16(MTP_STANDARD_VERSION);
324 string.set("microsoft.com: 1.0;");
325 mData.putString(string); // MTP Extensions
326 mData.putUInt16(0); //Functional Mode
327 mData.putAUInt16(kSupportedOperationCodes,
328 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
329 mData.putEmptyArray(); // Events Supported
330 mData.putEmptyArray(); // Device Properties Supported
331 mData.putEmptyArray(); // Capture Formats
332 mData.putAUInt16(kSupportedPlaybackFormats,
333 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
334 // FIXME
335 string.set("Google, Inc.");
336 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700337
338 property_get("ro.product.model", prop_value, "MTP Device");
339 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400340 mData.putString(string); // Model
341 string.set("1.0");
342 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700343
344 property_get("ro.serialno", prop_value, "????????");
345 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400346 mData.putString(string); // Serial Number
347
348 return MTP_RESPONSE_OK;
349}
350
351MtpResponseCode MtpServer::doOpenSession() {
352 if (mSessionOpen) {
353 mResponse.setParameter(1, mSessionID);
354 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
355 }
356 mSessionID = mRequest.getParameter(1);
357 mSessionOpen = true;
358 return MTP_RESPONSE_OK;
359}
360
361MtpResponseCode MtpServer::doCloseSession() {
362 if (!mSessionOpen)
363 return MTP_RESPONSE_SESSION_NOT_OPEN;
364 mSessionID = 0;
365 mSessionOpen = false;
366 return MTP_RESPONSE_OK;
367}
368
369MtpResponseCode MtpServer::doGetStorageIDs() {
370 if (!mSessionOpen)
371 return MTP_RESPONSE_SESSION_NOT_OPEN;
372
373 int count = mStorages.size();
374 mData.putUInt32(count);
375 for (int i = 0; i < count; i++)
376 mData.putUInt32(mStorages[i]->getStorageID());
377
378 return MTP_RESPONSE_OK;
379}
380
381MtpResponseCode MtpServer::doGetStorageInfo() {
382 MtpStringBuffer string;
383
384 if (!mSessionOpen)
385 return MTP_RESPONSE_SESSION_NOT_OPEN;
386 MtpStorageID id = mRequest.getParameter(1);
387 MtpStorage* storage = getStorage(id);
388 if (!storage)
389 return MTP_RESPONSE_INVALID_STORAGE_ID;
390
391 mData.putUInt16(storage->getType());
392 mData.putUInt16(storage->getFileSystemType());
393 mData.putUInt16(storage->getAccessCapability());
394 mData.putUInt64(storage->getMaxCapacity());
395 mData.putUInt64(storage->getFreeSpace());
396 mData.putUInt32(1024*1024*1024); // Free Space in Objects
397 string.set(storage->getDescription());
398 mData.putString(string);
399 mData.putEmptyString(); // Volume Identifier
400
401 return MTP_RESPONSE_OK;
402}
403
404MtpResponseCode MtpServer::doGetObjectPropsSupported() {
405 if (!mSessionOpen)
406 return MTP_RESPONSE_SESSION_NOT_OPEN;
407 MtpObjectFormat format = mRequest.getParameter(1);
408 mData.putAUInt16(kSupportedObjectProperties,
409 sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
410 return MTP_RESPONSE_OK;
411}
412
413MtpResponseCode MtpServer::doGetObjectHandles() {
414 if (!mSessionOpen)
415 return MTP_RESPONSE_SESSION_NOT_OPEN;
416 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400417 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400418 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
419 // 0x00000000 for all objects?
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400420 if (parent == 0xFFFFFFFF)
421 parent = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400422
423 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
424 mData.putAUInt32(handles);
425 delete handles;
426 return MTP_RESPONSE_OK;
427}
428
429MtpResponseCode MtpServer::doGetObjectPropValue() {
430 MtpObjectHandle handle = mRequest.getParameter(1);
431 MtpObjectProperty property = mRequest.getParameter(2);
432
433 return mDatabase->getObjectProperty(handle, property, mData);
434}
435
436MtpResponseCode MtpServer::doGetObjectInfo() {
437 MtpObjectHandle handle = mRequest.getParameter(1);
438 return mDatabase->getObjectInfo(handle, mData);
439}
440
441MtpResponseCode MtpServer::doGetObject() {
442 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400443 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400444 int64_t fileLength;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400445 if (!mDatabase->getObjectFilePath(handle, pathBuf, fileLength))
Mike Lockwood16864ba2010-05-11 17:16:59 -0400446 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400447 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400448
449 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400450 mfr.fd = open(filePath, O_RDONLY);
451 if (mfr.fd < 0) {
452 return MTP_RESPONSE_GENERAL_ERROR;
453 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400454 mfr.offset = 0;
455 mfr.length = fileLength;
456
457 // send data header
458 mData.setOperationCode(mRequest.getOperationCode());
459 mData.setTransactionID(mRequest.getTransactionID());
460 mData.writeDataHeader(mFD, fileLength);
461
462 // then transfer the file
463 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400464 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400465 if (ret < 0) {
466 if (errno == ECANCELED)
467 return MTP_RESPONSE_TRANSACTION_CANCELLED;
468 else
469 return MTP_RESPONSE_GENERAL_ERROR;
470 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400471 return MTP_RESPONSE_OK;
472}
473
474MtpResponseCode MtpServer::doSendObjectInfo() {
475 MtpString path;
476 MtpStorageID storageID = mRequest.getParameter(1);
477 MtpStorage* storage = getStorage(storageID);
478 MtpObjectHandle parent = mRequest.getParameter(2);
479 if (!storage)
480 return MTP_RESPONSE_INVALID_STORAGE_ID;
481
482 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400483 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400484 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400485 parent = 0;
486 } else {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400487 int64_t dummy;
488 if (!mDatabase->getObjectFilePath(parent, path, dummy))
489 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
490 }
491
492 // read only the fields we need
493 mData.getUInt32(); // storage ID
494 MtpObjectFormat format = mData.getUInt16();
495 mData.getUInt16(); // protection status
496 mSendObjectFileSize = mData.getUInt32();
497 mData.getUInt16(); // thumb format
498 mData.getUInt32(); // thumb compressed size
499 mData.getUInt32(); // thumb pix width
500 mData.getUInt32(); // thumb pix height
501 mData.getUInt32(); // image pix width
502 mData.getUInt32(); // image pix height
503 mData.getUInt32(); // image bit depth
504 mData.getUInt32(); // parent
505 uint16_t associationType = mData.getUInt16();
506 uint32_t associationDesc = mData.getUInt32(); // association desc
507 mData.getUInt32(); // sequence number
508 MtpStringBuffer name, created, modified;
509 mData.getString(name); // file name
510 mData.getString(created); // date created
511 mData.getString(modified); // date modified
512 // keywords follow
513
Mike Lockwoodfceef462010-05-14 15:35:17 -0400514 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400515 if (!parseDateTime(modified, modifiedTime))
516 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400517
518 if (path[path.size() - 1] != '/')
519 path += "/";
520 path += (const char *)name;
521
Mike Lockwoodfceef462010-05-14 15:35:17 -0400522 mDatabase->beginTransaction();
Mike Lockwood4714b072010-07-12 08:49:01 -0400523 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
524 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400525 if (handle == kInvalidObjectHandle) {
526 mDatabase->rollbackTransaction();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400527 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400528 }
Mike Lockwoodfceef462010-05-14 15:35:17 -0400529 mDatabase->commitTransaction();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400530
531 if (format == MTP_FORMAT_ASSOCIATION) {
532 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400533 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400534 umask(mask);
535 if (ret && ret != -EEXIST)
536 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400537 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400538 } else {
539 mSendObjectFilePath = path;
540 // save the handle for the SendObject call, which should follow
541 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400542 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400543 }
544
545 mResponse.setParameter(1, storageID);
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400546 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400547 mResponse.setParameter(3, handle);
548
549 return MTP_RESPONSE_OK;
550}
551
552MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood4714b072010-07-12 08:49:01 -0400553 MtpResponseCode result = MTP_RESPONSE_OK;
554 mode_t mask;
555 int ret;
556
Mike Lockwood16864ba2010-05-11 17:16:59 -0400557 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400558 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400559 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
560 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400561 }
562
563 // read the header
Mike Lockwood4714b072010-07-12 08:49:01 -0400564 ret = mData.readDataHeader(mFD);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400565 // FIXME - check for errors here.
566
567 // reset so we don't attempt to send this back
568 mData.reset();
569
570 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400571 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
572 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400573 result = MTP_RESPONSE_GENERAL_ERROR;
574 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400575 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400576 fchown(mfr.fd, getuid(), mFileGroup);
577 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400578 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400579 fchmod(mfr.fd, mFilePermission);
580 umask(mask);
581
Mike Lockwood16864ba2010-05-11 17:16:59 -0400582 mfr.offset = 0;
583 mfr.length = mSendObjectFileSize;
584
585 // transfer the file
586 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400587 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400588
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400589 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400590
Mike Lockwood916076c2010-06-04 09:49:21 -0400591 if (ret < 0) {
592 unlink(mSendObjectFilePath);
593 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400594 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400595 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400596 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400597 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400598
599done:
600 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
601 result == MTP_RESPONSE_OK);
602 mSendObjectHandle = kInvalidObjectHandle;
603 mSendObjectFormat = 0;
604 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400605}
606
607MtpResponseCode MtpServer::doDeleteObject() {
608 MtpObjectHandle handle = mRequest.getParameter(1);
609 MtpObjectFormat format = mRequest.getParameter(1);
610 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
611 // FIXME - implement deleting objects by format
612 // FIXME - handle non-empty directories
613
614 MtpString filePath;
615 int64_t fileLength;
616 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
617 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
618
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400619 LOGV("deleting %s", (const char *)filePath);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400620 // one of these should work
621 rmdir((const char *)filePath);
622 unlink((const char *)filePath);
623
624 mDatabase->deleteFile(handle);
625
626 return MTP_RESPONSE_OK;
627}
628
629MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400630 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400631 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400632 MtpProperty* property = getObjectProperty(propCode);
633 if (!property)
634 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400635
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400636 property->write(mData);
637 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400638}
Mike Lockwood7850ef92010-05-14 10:10:36 -0400639
640} // namespace android