blob: b9eeec5953cab5ee16ee4e8046addf93ee881eb5 [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 Lockwood21ef7d02010-06-30 17:00:35 -040028#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040029#include "MtpServer.h"
Mike Lockwood02503612010-07-02 14:03:31 -040030#include "MtpSqliteDatabase.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040031#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 Lockwood8e2a2802010-07-02 15:15:07 -0400116MtpServer::MtpServer(int fd, const char* databasePath,
117 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -0400118 : mFD(fd),
119 mDatabasePath(databasePath),
120 mDatabase(NULL),
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400121 mFileGroup(fileGroup),
122 mFilePermission(filePerm),
123 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400124 mSessionID(0),
125 mSessionOpen(false),
126 mSendObjectHandle(kInvalidObjectHandle),
127 mSendObjectFileSize(0)
128{
Mike Lockwood02503612010-07-02 14:03:31 -0400129 mDatabase = new MtpSqliteDatabase();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400130 mDatabase->open(databasePath, true);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400131
132 initObjectProperties();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400133}
134
135MtpServer::~MtpServer() {
136}
137
138void MtpServer::addStorage(const char* filePath) {
139 int index = mStorages.size() + 1;
140 index |= index << 16; // set high and low part to our index
141 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
142 addStorage(storage);
143}
144
145MtpStorage* MtpServer::getStorage(MtpStorageID id) {
146 for (int i = 0; i < mStorages.size(); i++) {
147 MtpStorage* storage = mStorages[i];
148 if (storage->getStorageID() == id)
149 return storage;
150 }
151 return NULL;
152}
153
154void MtpServer::scanStorage() {
155 for (int i = 0; i < mStorages.size(); i++) {
156 MtpStorage* storage = mStorages[i];
157 storage->scanFiles();
158 }
159}
160
161void MtpServer::run() {
162 int fd = mFD;
163
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400164 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400165
166 while (1) {
167 int ret = mRequest.read(fd);
168 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400169 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400170 if (errno == ECANCELED) {
171 // return to top of loop and wait for next command
172 continue;
173 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400174 break;
175 }
176 MtpOperationCode operation = mRequest.getOperationCode();
177 MtpTransactionID transaction = mRequest.getTransactionID();
178
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400179 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400180 mRequest.dump();
181
182 // FIXME need to generalize this
183 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
184 if (dataIn) {
185 int ret = mData.read(fd);
186 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400187 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400188 if (errno == ECANCELED) {
189 // return to top of loop and wait for next command
190 continue;
191 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400192 break;
193 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400194 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400195 mData.dump();
196 } else {
197 mData.reset();
198 }
199
Mike Lockwood916076c2010-06-04 09:49:21 -0400200 if (handleRequest()) {
201 if (!dataIn && mData.hasData()) {
202 mData.setOperationCode(operation);
203 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400204 LOGV("sending data:");
Mike Lockwood916076c2010-06-04 09:49:21 -0400205 mData.dump();
206 ret = mData.write(fd);
207 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400208 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400209 if (errno == ECANCELED) {
210 // return to top of loop and wait for next command
211 continue;
212 }
213 break;
214 }
215 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400216
Mike Lockwood916076c2010-06-04 09:49:21 -0400217 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400218 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400219 ret = mResponse.write(fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400220 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400221 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400222 if (errno == ECANCELED) {
223 // return to top of loop and wait for next command
224 continue;
225 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400226 break;
227 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400228 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400229 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400230 }
231 }
232}
233
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400234MtpProperty* MtpServer::getObjectProperty(MtpPropertyCode propCode) {
235 for (int i = 0; i < mObjectProperties.size(); i++) {
236 MtpProperty* property = mObjectProperties[i];
237 if (property->getPropertyCode() == propCode)
238 return property;
239 }
240 return NULL;
241}
242
243MtpProperty* MtpServer::getDeviceProperty(MtpPropertyCode propCode) {
244 for (int i = 0; i < mDeviceProperties.size(); i++) {
245 MtpProperty* property = mDeviceProperties[i];
246 if (property->getPropertyCode() == propCode)
247 return property;
248 }
249 return NULL;
250}
251
252void MtpServer::initObjectProperties() {
253 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
254 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
255 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
256 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
257 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
258}
259
Mike Lockwood916076c2010-06-04 09:49:21 -0400260bool MtpServer::handleRequest() {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400261 MtpOperationCode operation = mRequest.getOperationCode();
262 MtpResponseCode response;
263
264 mResponse.reset();
265
266 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
267 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400268 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400269 mSendObjectHandle = kInvalidObjectHandle;
270 }
271
272 switch (operation) {
273 case MTP_OPERATION_GET_DEVICE_INFO:
274 response = doGetDeviceInfo();
275 break;
276 case MTP_OPERATION_OPEN_SESSION:
277 response = doOpenSession();
278 break;
279 case MTP_OPERATION_CLOSE_SESSION:
280 response = doCloseSession();
281 break;
282 case MTP_OPERATION_GET_STORAGE_IDS:
283 response = doGetStorageIDs();
284 break;
285 case MTP_OPERATION_GET_STORAGE_INFO:
286 response = doGetStorageInfo();
287 break;
288 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
289 response = doGetObjectPropsSupported();
290 break;
291 case MTP_OPERATION_GET_OBJECT_HANDLES:
292 response = doGetObjectHandles();
293 break;
294 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
295 response = doGetObjectPropValue();
296 break;
297 case MTP_OPERATION_GET_OBJECT_INFO:
298 response = doGetObjectInfo();
299 break;
300 case MTP_OPERATION_GET_OBJECT:
301 response = doGetObject();
302 break;
303 case MTP_OPERATION_SEND_OBJECT_INFO:
304 response = doSendObjectInfo();
305 break;
306 case MTP_OPERATION_SEND_OBJECT:
307 response = doSendObject();
308 break;
309 case MTP_OPERATION_DELETE_OBJECT:
310 response = doDeleteObject();
311 break;
312 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400313 response = doGetObjectPropDesc();
314 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400315 default:
316 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
317 break;
318 }
319
Mike Lockwood916076c2010-06-04 09:49:21 -0400320 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
321 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400322 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400323 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400324}
325
326MtpResponseCode MtpServer::doGetDeviceInfo() {
327 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700328 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400329
330 // fill in device info
331 mData.putUInt16(MTP_STANDARD_VERSION);
332 mData.putUInt32(6); // MTP Vendor Extension ID
333 mData.putUInt16(MTP_STANDARD_VERSION);
334 string.set("microsoft.com: 1.0;");
335 mData.putString(string); // MTP Extensions
336 mData.putUInt16(0); //Functional Mode
337 mData.putAUInt16(kSupportedOperationCodes,
338 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
339 mData.putEmptyArray(); // Events Supported
340 mData.putEmptyArray(); // Device Properties Supported
341 mData.putEmptyArray(); // Capture Formats
342 mData.putAUInt16(kSupportedPlaybackFormats,
343 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
344 // FIXME
345 string.set("Google, Inc.");
346 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700347
348 property_get("ro.product.model", prop_value, "MTP Device");
349 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400350 mData.putString(string); // Model
351 string.set("1.0");
352 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700353
354 property_get("ro.serialno", prop_value, "????????");
355 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400356 mData.putString(string); // Serial Number
357
358 return MTP_RESPONSE_OK;
359}
360
361MtpResponseCode MtpServer::doOpenSession() {
362 if (mSessionOpen) {
363 mResponse.setParameter(1, mSessionID);
364 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
365 }
366 mSessionID = mRequest.getParameter(1);
367 mSessionOpen = true;
368 return MTP_RESPONSE_OK;
369}
370
371MtpResponseCode MtpServer::doCloseSession() {
372 if (!mSessionOpen)
373 return MTP_RESPONSE_SESSION_NOT_OPEN;
374 mSessionID = 0;
375 mSessionOpen = false;
376 return MTP_RESPONSE_OK;
377}
378
379MtpResponseCode MtpServer::doGetStorageIDs() {
380 if (!mSessionOpen)
381 return MTP_RESPONSE_SESSION_NOT_OPEN;
382
383 int count = mStorages.size();
384 mData.putUInt32(count);
385 for (int i = 0; i < count; i++)
386 mData.putUInt32(mStorages[i]->getStorageID());
387
388 return MTP_RESPONSE_OK;
389}
390
391MtpResponseCode MtpServer::doGetStorageInfo() {
392 MtpStringBuffer string;
393
394 if (!mSessionOpen)
395 return MTP_RESPONSE_SESSION_NOT_OPEN;
396 MtpStorageID id = mRequest.getParameter(1);
397 MtpStorage* storage = getStorage(id);
398 if (!storage)
399 return MTP_RESPONSE_INVALID_STORAGE_ID;
400
401 mData.putUInt16(storage->getType());
402 mData.putUInt16(storage->getFileSystemType());
403 mData.putUInt16(storage->getAccessCapability());
404 mData.putUInt64(storage->getMaxCapacity());
405 mData.putUInt64(storage->getFreeSpace());
406 mData.putUInt32(1024*1024*1024); // Free Space in Objects
407 string.set(storage->getDescription());
408 mData.putString(string);
409 mData.putEmptyString(); // Volume Identifier
410
411 return MTP_RESPONSE_OK;
412}
413
414MtpResponseCode MtpServer::doGetObjectPropsSupported() {
415 if (!mSessionOpen)
416 return MTP_RESPONSE_SESSION_NOT_OPEN;
417 MtpObjectFormat format = mRequest.getParameter(1);
418 mData.putAUInt16(kSupportedObjectProperties,
419 sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
420 return MTP_RESPONSE_OK;
421}
422
423MtpResponseCode MtpServer::doGetObjectHandles() {
424 if (!mSessionOpen)
425 return MTP_RESPONSE_SESSION_NOT_OPEN;
426 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400427 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400428 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
429 // 0x00000000 for all objects?
430
431 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
432 mData.putAUInt32(handles);
433 delete handles;
434 return MTP_RESPONSE_OK;
435}
436
437MtpResponseCode MtpServer::doGetObjectPropValue() {
438 MtpObjectHandle handle = mRequest.getParameter(1);
439 MtpObjectProperty property = mRequest.getParameter(2);
440
441 return mDatabase->getObjectProperty(handle, property, mData);
442}
443
444MtpResponseCode MtpServer::doGetObjectInfo() {
445 MtpObjectHandle handle = mRequest.getParameter(1);
446 return mDatabase->getObjectInfo(handle, mData);
447}
448
449MtpResponseCode MtpServer::doGetObject() {
450 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400451 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400452 int64_t fileLength;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400453 if (!mDatabase->getObjectFilePath(handle, pathBuf, fileLength))
Mike Lockwood16864ba2010-05-11 17:16:59 -0400454 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400455 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400456
457 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400458 mfr.fd = open(filePath, O_RDONLY);
459 if (mfr.fd < 0) {
460 return MTP_RESPONSE_GENERAL_ERROR;
461 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400462 mfr.offset = 0;
463 mfr.length = fileLength;
464
465 // send data header
466 mData.setOperationCode(mRequest.getOperationCode());
467 mData.setTransactionID(mRequest.getTransactionID());
468 mData.writeDataHeader(mFD, fileLength);
469
470 // then transfer the file
471 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400472 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400473 if (ret < 0) {
474 if (errno == ECANCELED)
475 return MTP_RESPONSE_TRANSACTION_CANCELLED;
476 else
477 return MTP_RESPONSE_GENERAL_ERROR;
478 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400479 return MTP_RESPONSE_OK;
480}
481
482MtpResponseCode MtpServer::doSendObjectInfo() {
483 MtpString path;
484 MtpStorageID storageID = mRequest.getParameter(1);
485 MtpStorage* storage = getStorage(storageID);
486 MtpObjectHandle parent = mRequest.getParameter(2);
487 if (!storage)
488 return MTP_RESPONSE_INVALID_STORAGE_ID;
489
490 // special case the root
491 if (parent == MTP_PARENT_ROOT)
492 path = storage->getPath();
493 else {
494 int64_t dummy;
495 if (!mDatabase->getObjectFilePath(parent, path, dummy))
496 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
497 }
498
499 // read only the fields we need
500 mData.getUInt32(); // storage ID
501 MtpObjectFormat format = mData.getUInt16();
502 mData.getUInt16(); // protection status
503 mSendObjectFileSize = mData.getUInt32();
504 mData.getUInt16(); // thumb format
505 mData.getUInt32(); // thumb compressed size
506 mData.getUInt32(); // thumb pix width
507 mData.getUInt32(); // thumb pix height
508 mData.getUInt32(); // image pix width
509 mData.getUInt32(); // image pix height
510 mData.getUInt32(); // image bit depth
511 mData.getUInt32(); // parent
512 uint16_t associationType = mData.getUInt16();
513 uint32_t associationDesc = mData.getUInt32(); // association desc
514 mData.getUInt32(); // sequence number
515 MtpStringBuffer name, created, modified;
516 mData.getString(name); // file name
517 mData.getString(created); // date created
518 mData.getString(modified); // date modified
519 // keywords follow
520
Mike Lockwoodfceef462010-05-14 15:35:17 -0400521 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400522 if (!parseDateTime(modified, modifiedTime))
523 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400524
525 if (path[path.size() - 1] != '/')
526 path += "/";
527 path += (const char *)name;
528
Mike Lockwoodfceef462010-05-14 15:35:17 -0400529 mDatabase->beginTransaction();
530 MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID,
531 mSendObjectFileSize, modifiedTime);
532 if (handle == kInvalidObjectHandle) {
533 mDatabase->rollbackTransaction();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400534 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400535 }
536 uint32_t table = MtpDatabase::getTableForFile(format);
537 if (table == kObjectHandleTableAudio)
538 handle = mDatabase->addAudioFile(handle);
539 mDatabase->commitTransaction();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400540
541 if (format == MTP_FORMAT_ASSOCIATION) {
542 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400543 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400544 umask(mask);
545 if (ret && ret != -EEXIST)
546 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400547 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400548 } else {
549 mSendObjectFilePath = path;
550 // save the handle for the SendObject call, which should follow
551 mSendObjectHandle = handle;
552 }
553
554 mResponse.setParameter(1, storageID);
555 mResponse.setParameter(2, parent);
556 mResponse.setParameter(3, handle);
557
558 return MTP_RESPONSE_OK;
559}
560
561MtpResponseCode MtpServer::doSendObject() {
562 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400563 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400564 return MTP_RESPONSE_NO_VALID_OBJECT_INFO;
565 }
566
567 // read the header
568 int ret = mData.readDataHeader(mFD);
569 // FIXME - check for errors here.
570
571 // reset so we don't attempt to send this back
572 mData.reset();
573
574 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400575 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
576 if (mfr.fd < 0) {
577 return MTP_RESPONSE_GENERAL_ERROR;
578 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400579 fchown(mfr.fd, getuid(), mFileGroup);
580 // set permissions
581 mode_t mask = umask(0);
582 fchmod(mfr.fd, mFilePermission);
583 umask(mask);
584
Mike Lockwood16864ba2010-05-11 17:16:59 -0400585 mfr.offset = 0;
586 mfr.length = mSendObjectFileSize;
587
588 // transfer the file
589 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400590 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400591
Mike Lockwood916076c2010-06-04 09:49:21 -0400592 // FIXME - we need to delete mSendObjectHandle from the database if this fails.
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400593 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400594 mSendObjectHandle = kInvalidObjectHandle;
595
Mike Lockwood916076c2010-06-04 09:49:21 -0400596 if (ret < 0) {
597 unlink(mSendObjectFilePath);
598 if (errno == ECANCELED)
599 return MTP_RESPONSE_TRANSACTION_CANCELLED;
600 else
601 return MTP_RESPONSE_GENERAL_ERROR;
602 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400603 return MTP_RESPONSE_OK;
604}
605
606MtpResponseCode MtpServer::doDeleteObject() {
607 MtpObjectHandle handle = mRequest.getParameter(1);
608 MtpObjectFormat format = mRequest.getParameter(1);
609 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
610 // FIXME - implement deleting objects by format
611 // FIXME - handle non-empty directories
612
613 MtpString filePath;
614 int64_t fileLength;
615 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
616 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
617
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400618 LOGV("deleting %s", (const char *)filePath);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400619 // one of these should work
620 rmdir((const char *)filePath);
621 unlink((const char *)filePath);
622
623 mDatabase->deleteFile(handle);
624
625 return MTP_RESPONSE_OK;
626}
627
628MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400629 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400630 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400631 MtpProperty* property = getObjectProperty(propCode);
632 if (!property)
633 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400634
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400635 property->write(mData);
636 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400637}
Mike Lockwood7850ef92010-05-14 10:10:36 -0400638
639} // namespace android