blob: 967ebc9c02bdcd8b87ed915b61be8570bde42f59 [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 Lockwood767c5e42010-06-30 17:00:35 -040028#include "MtpProperty.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040029#include "MtpServer.h"
Mike Lockwood8dd2a392010-07-02 14:03:31 -040030#include "MtpSqliteDatabase.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040031#include "MtpStorage.h"
32#include "MtpStringBuffer.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040033
34#include "f_mtp.h"
35
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,
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 Lockwoodd0782672010-05-14 15:35:17 -040085 // MTP_FORMAT_UNDEFINED,
Mike Lockwood56118b52010-05-11 17:16:59 -040086 MTP_FORMAT_ASSOCIATION,
Mike Lockwoodd0782672010-05-14 15:35:17 -040087 // MTP_FORMAT_TEXT,
88 // MTP_FORMAT_HTML,
Mike Lockwood56118b52010-05-11 17:16:59 -040089 MTP_FORMAT_MP3,
Mike Lockwoodd0782672010-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 Lockwood56118b52010-05-11 17:16:59 -0400114};
115
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400116MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwooddad69272010-07-02 15:15:07 -0400117 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood56118b52010-05-11 17:16:59 -0400118 : mFD(fd),
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400119 mDatabase(database),
Mike Lockwooddad69272010-07-02 15:15:07 -0400120 mFileGroup(fileGroup),
121 mFilePermission(filePerm),
122 mDirectoryPermission(directoryPerm),
Mike Lockwood56118b52010-05-11 17:16:59 -0400123 mSessionID(0),
124 mSessionOpen(false),
125 mSendObjectHandle(kInvalidObjectHandle),
126 mSendObjectFileSize(0)
127{
Mike Lockwood767c5e42010-06-30 17:00:35 -0400128 initObjectProperties();
Mike Lockwood56118b52010-05-11 17:16:59 -0400129}
130
131MtpServer::~MtpServer() {
132}
133
134void MtpServer::addStorage(const char* filePath) {
135 int index = mStorages.size() + 1;
136 index |= index << 16; // set high and low part to our index
137 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
138 addStorage(storage);
139}
140
141MtpStorage* MtpServer::getStorage(MtpStorageID id) {
142 for (int i = 0; i < mStorages.size(); i++) {
143 MtpStorage* storage = mStorages[i];
144 if (storage->getStorageID() == id)
145 return storage;
146 }
147 return NULL;
148}
149
150void MtpServer::scanStorage() {
151 for (int i = 0; i < mStorages.size(); i++) {
152 MtpStorage* storage = mStorages[i];
153 storage->scanFiles();
154 }
155}
156
157void MtpServer::run() {
158 int fd = mFD;
159
Mike Lockwood767c5e42010-06-30 17:00:35 -0400160 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400161
162 while (1) {
163 int ret = mRequest.read(fd);
164 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400165 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400166 if (errno == ECANCELED) {
167 // return to top of loop and wait for next command
168 continue;
169 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400170 break;
171 }
172 MtpOperationCode operation = mRequest.getOperationCode();
173 MtpTransactionID transaction = mRequest.getTransactionID();
174
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400175 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400176 mRequest.dump();
177
178 // FIXME need to generalize this
179 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
180 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
248void MtpServer::initObjectProperties() {
249 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
250 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
251 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64));
252 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR));
253 mObjectProperties.push(new MtpProperty(MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32));
254}
255
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400256bool MtpServer::handleRequest() {
Mike Lockwood56118b52010-05-11 17:16:59 -0400257 MtpOperationCode operation = mRequest.getOperationCode();
258 MtpResponseCode response;
259
260 mResponse.reset();
261
262 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
263 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400264 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood56118b52010-05-11 17:16:59 -0400265 mSendObjectHandle = kInvalidObjectHandle;
266 }
267
268 switch (operation) {
269 case MTP_OPERATION_GET_DEVICE_INFO:
270 response = doGetDeviceInfo();
271 break;
272 case MTP_OPERATION_OPEN_SESSION:
273 response = doOpenSession();
274 break;
275 case MTP_OPERATION_CLOSE_SESSION:
276 response = doCloseSession();
277 break;
278 case MTP_OPERATION_GET_STORAGE_IDS:
279 response = doGetStorageIDs();
280 break;
281 case MTP_OPERATION_GET_STORAGE_INFO:
282 response = doGetStorageInfo();
283 break;
284 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
285 response = doGetObjectPropsSupported();
286 break;
287 case MTP_OPERATION_GET_OBJECT_HANDLES:
288 response = doGetObjectHandles();
289 break;
290 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
291 response = doGetObjectPropValue();
292 break;
293 case MTP_OPERATION_GET_OBJECT_INFO:
294 response = doGetObjectInfo();
295 break;
296 case MTP_OPERATION_GET_OBJECT:
297 response = doGetObject();
298 break;
299 case MTP_OPERATION_SEND_OBJECT_INFO:
300 response = doSendObjectInfo();
301 break;
302 case MTP_OPERATION_SEND_OBJECT:
303 response = doSendObject();
304 break;
305 case MTP_OPERATION_DELETE_OBJECT:
306 response = doDeleteObject();
307 break;
308 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood767c5e42010-06-30 17:00:35 -0400309 response = doGetObjectPropDesc();
310 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400311 default:
312 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
313 break;
314 }
315
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400316 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
317 return false;
Mike Lockwood56118b52010-05-11 17:16:59 -0400318 mResponse.setResponseCode(response);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400319 return true;
Mike Lockwood56118b52010-05-11 17:16:59 -0400320}
321
322MtpResponseCode MtpServer::doGetDeviceInfo() {
323 MtpStringBuffer string;
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700324 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood56118b52010-05-11 17:16:59 -0400325
326 // fill in device info
327 mData.putUInt16(MTP_STANDARD_VERSION);
328 mData.putUInt32(6); // MTP Vendor Extension ID
329 mData.putUInt16(MTP_STANDARD_VERSION);
330 string.set("microsoft.com: 1.0;");
331 mData.putString(string); // MTP Extensions
332 mData.putUInt16(0); //Functional Mode
333 mData.putAUInt16(kSupportedOperationCodes,
334 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
335 mData.putEmptyArray(); // Events Supported
336 mData.putEmptyArray(); // Device Properties Supported
337 mData.putEmptyArray(); // Capture Formats
338 mData.putAUInt16(kSupportedPlaybackFormats,
339 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
340 // FIXME
341 string.set("Google, Inc.");
342 mData.putString(string); // Manufacturer
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700343
344 property_get("ro.product.model", prop_value, "MTP Device");
345 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400346 mData.putString(string); // Model
347 string.set("1.0");
348 mData.putString(string); // Device Version
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700349
350 property_get("ro.serialno", prop_value, "????????");
351 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400352 mData.putString(string); // Serial Number
353
354 return MTP_RESPONSE_OK;
355}
356
357MtpResponseCode MtpServer::doOpenSession() {
358 if (mSessionOpen) {
359 mResponse.setParameter(1, mSessionID);
360 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
361 }
362 mSessionID = mRequest.getParameter(1);
363 mSessionOpen = true;
364 return MTP_RESPONSE_OK;
365}
366
367MtpResponseCode MtpServer::doCloseSession() {
368 if (!mSessionOpen)
369 return MTP_RESPONSE_SESSION_NOT_OPEN;
370 mSessionID = 0;
371 mSessionOpen = false;
372 return MTP_RESPONSE_OK;
373}
374
375MtpResponseCode MtpServer::doGetStorageIDs() {
376 if (!mSessionOpen)
377 return MTP_RESPONSE_SESSION_NOT_OPEN;
378
379 int count = mStorages.size();
380 mData.putUInt32(count);
381 for (int i = 0; i < count; i++)
382 mData.putUInt32(mStorages[i]->getStorageID());
383
384 return MTP_RESPONSE_OK;
385}
386
387MtpResponseCode MtpServer::doGetStorageInfo() {
388 MtpStringBuffer string;
389
390 if (!mSessionOpen)
391 return MTP_RESPONSE_SESSION_NOT_OPEN;
392 MtpStorageID id = mRequest.getParameter(1);
393 MtpStorage* storage = getStorage(id);
394 if (!storage)
395 return MTP_RESPONSE_INVALID_STORAGE_ID;
396
397 mData.putUInt16(storage->getType());
398 mData.putUInt16(storage->getFileSystemType());
399 mData.putUInt16(storage->getAccessCapability());
400 mData.putUInt64(storage->getMaxCapacity());
401 mData.putUInt64(storage->getFreeSpace());
402 mData.putUInt32(1024*1024*1024); // Free Space in Objects
403 string.set(storage->getDescription());
404 mData.putString(string);
405 mData.putEmptyString(); // Volume Identifier
406
407 return MTP_RESPONSE_OK;
408}
409
410MtpResponseCode MtpServer::doGetObjectPropsSupported() {
411 if (!mSessionOpen)
412 return MTP_RESPONSE_SESSION_NOT_OPEN;
413 MtpObjectFormat format = mRequest.getParameter(1);
414 mData.putAUInt16(kSupportedObjectProperties,
415 sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
416 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 Lockwood37433652010-05-19 15:12:14 -0400423 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood56118b52010-05-11 17:16:59 -0400424 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
425 // 0x00000000 for all objects?
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400426 if (parent == 0xFFFFFFFF)
427 parent = 0;
Mike Lockwood56118b52010-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
435MtpResponseCode MtpServer::doGetObjectPropValue() {
436 MtpObjectHandle handle = mRequest.getParameter(1);
437 MtpObjectProperty property = mRequest.getParameter(2);
438
439 return mDatabase->getObjectProperty(handle, property, mData);
440}
441
442MtpResponseCode MtpServer::doGetObjectInfo() {
443 MtpObjectHandle handle = mRequest.getParameter(1);
444 return mDatabase->getObjectInfo(handle, mData);
445}
446
447MtpResponseCode MtpServer::doGetObject() {
448 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400449 MtpString pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400450 int64_t fileLength;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400451 if (!mDatabase->getObjectFilePath(handle, pathBuf, fileLength))
Mike Lockwood56118b52010-05-11 17:16:59 -0400452 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400453 const char* filePath = (const char *)pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400454
455 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400456 mfr.fd = open(filePath, O_RDONLY);
457 if (mfr.fd < 0) {
458 return MTP_RESPONSE_GENERAL_ERROR;
459 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400460 mfr.offset = 0;
461 mfr.length = fileLength;
462
463 // send data header
464 mData.setOperationCode(mRequest.getOperationCode());
465 mData.setTransactionID(mRequest.getTransactionID());
466 mData.writeDataHeader(mFD, fileLength);
467
468 // then transfer the file
469 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400470 close(mfr.fd);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400471 if (ret < 0) {
472 if (errno == ECANCELED)
473 return MTP_RESPONSE_TRANSACTION_CANCELLED;
474 else
475 return MTP_RESPONSE_GENERAL_ERROR;
476 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400477 return MTP_RESPONSE_OK;
478}
479
480MtpResponseCode MtpServer::doSendObjectInfo() {
481 MtpString path;
482 MtpStorageID storageID = mRequest.getParameter(1);
483 MtpStorage* storage = getStorage(storageID);
484 MtpObjectHandle parent = mRequest.getParameter(2);
485 if (!storage)
486 return MTP_RESPONSE_INVALID_STORAGE_ID;
487
488 // special case the root
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400489 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400490 path = storage->getPath();
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400491 parent = 0;
492 } else {
Mike Lockwood56118b52010-05-11 17:16:59 -0400493 int64_t dummy;
494 if (!mDatabase->getObjectFilePath(parent, path, dummy))
495 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
496 }
497
498 // read only the fields we need
499 mData.getUInt32(); // storage ID
500 MtpObjectFormat format = mData.getUInt16();
501 mData.getUInt16(); // protection status
502 mSendObjectFileSize = mData.getUInt32();
503 mData.getUInt16(); // thumb format
504 mData.getUInt32(); // thumb compressed size
505 mData.getUInt32(); // thumb pix width
506 mData.getUInt32(); // thumb pix height
507 mData.getUInt32(); // image pix width
508 mData.getUInt32(); // image pix height
509 mData.getUInt32(); // image bit depth
510 mData.getUInt32(); // parent
511 uint16_t associationType = mData.getUInt16();
512 uint32_t associationDesc = mData.getUInt32(); // association desc
513 mData.getUInt32(); // sequence number
514 MtpStringBuffer name, created, modified;
515 mData.getString(name); // file name
516 mData.getString(created); // date created
517 mData.getString(modified); // date modified
518 // keywords follow
519
Mike Lockwoodd0782672010-05-14 15:35:17 -0400520 time_t modifiedTime;
Mike Lockwood56118b52010-05-11 17:16:59 -0400521 if (!parseDateTime(modified, modifiedTime))
522 modifiedTime = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400523
524 if (path[path.size() - 1] != '/')
525 path += "/";
526 path += (const char *)name;
527
Mike Lockwoodd0782672010-05-14 15:35:17 -0400528 mDatabase->beginTransaction();
529 MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID,
530 mSendObjectFileSize, modifiedTime);
531 if (handle == kInvalidObjectHandle) {
532 mDatabase->rollbackTransaction();
Mike Lockwood56118b52010-05-11 17:16:59 -0400533 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd0782672010-05-14 15:35:17 -0400534 }
Mike Lockwoodd0782672010-05-14 15:35:17 -0400535 mDatabase->commitTransaction();
Mike Lockwood56118b52010-05-11 17:16:59 -0400536
537 if (format == MTP_FORMAT_ASSOCIATION) {
538 mode_t mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400539 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood56118b52010-05-11 17:16:59 -0400540 umask(mask);
541 if (ret && ret != -EEXIST)
542 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooddad69272010-07-02 15:15:07 -0400543 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood56118b52010-05-11 17:16:59 -0400544 } else {
545 mSendObjectFilePath = path;
546 // save the handle for the SendObject call, which should follow
547 mSendObjectHandle = handle;
548 }
549
550 mResponse.setParameter(1, storageID);
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400551 mResponse.setParameter(2, (parent == 0 ? 0xFFFFFFFF: parent));
Mike Lockwood56118b52010-05-11 17:16:59 -0400552 mResponse.setParameter(3, handle);
553
554 return MTP_RESPONSE_OK;
555}
556
557MtpResponseCode MtpServer::doSendObject() {
558 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400559 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood56118b52010-05-11 17:16:59 -0400560 return MTP_RESPONSE_NO_VALID_OBJECT_INFO;
561 }
562
563 // read the header
564 int ret = mData.readDataHeader(mFD);
565 // 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 Lockwood42dbfa52010-06-22 15:03:53 -0400571 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
572 if (mfr.fd < 0) {
573 return MTP_RESPONSE_GENERAL_ERROR;
574 }
Mike Lockwooddad69272010-07-02 15:15:07 -0400575 fchown(mfr.fd, getuid(), mFileGroup);
576 // set permissions
577 mode_t mask = umask(0);
578 fchmod(mfr.fd, mFilePermission);
579 umask(mask);
580
Mike Lockwood56118b52010-05-11 17:16:59 -0400581 mfr.offset = 0;
582 mfr.length = mSendObjectFileSize;
583
584 // transfer the file
585 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400586 close(mfr.fd);
Mike Lockwooddad69272010-07-02 15:15:07 -0400587
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400588 // FIXME - we need to delete mSendObjectHandle from the database if this fails.
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400589 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood56118b52010-05-11 17:16:59 -0400590 mSendObjectHandle = kInvalidObjectHandle;
591
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400592 if (ret < 0) {
593 unlink(mSendObjectFilePath);
594 if (errno == ECANCELED)
595 return MTP_RESPONSE_TRANSACTION_CANCELLED;
596 else
597 return MTP_RESPONSE_GENERAL_ERROR;
598 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400599 return MTP_RESPONSE_OK;
600}
601
602MtpResponseCode MtpServer::doDeleteObject() {
603 MtpObjectHandle handle = mRequest.getParameter(1);
604 MtpObjectFormat format = mRequest.getParameter(1);
605 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
606 // FIXME - implement deleting objects by format
607 // FIXME - handle non-empty directories
608
609 MtpString filePath;
610 int64_t fileLength;
611 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
612 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
613
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400614 LOGV("deleting %s", (const char *)filePath);
Mike Lockwood56118b52010-05-11 17:16:59 -0400615 // one of these should work
616 rmdir((const char *)filePath);
617 unlink((const char *)filePath);
618
619 mDatabase->deleteFile(handle);
620
621 return MTP_RESPONSE_OK;
622}
623
624MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood767c5e42010-06-30 17:00:35 -0400625 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood56118b52010-05-11 17:16:59 -0400626 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood767c5e42010-06-30 17:00:35 -0400627 MtpProperty* property = getObjectProperty(propCode);
628 if (!property)
629 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood56118b52010-05-11 17:16:59 -0400630
Mike Lockwood767c5e42010-06-30 17:00:35 -0400631 property->write(mData);
632 return MTP_RESPONSE_OK;
Mike Lockwood56118b52010-05-11 17:16:59 -0400633}
Mike Lockwood8d3257a2010-05-14 10:10:36 -0400634
635} // namespace android