blob: ff4009c4916b3201cf9473fee66d4f983d6a2556 [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>
Mike Lockwoodd3211492010-09-13 17:15:58 -040024#include <sys/stat.h>
25#include <dirent.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040026
Mike Lockwoodc42aa122010-06-14 17:58:08 -070027#include <cutils/properties.h>
28
Mike Lockwooda881b442010-09-23 22:32:05 -040029#define LOG_TAG "MtpServer"
30
Mike Lockwood16864ba2010-05-11 17:16:59 -040031#include "MtpDebug.h"
Mike Lockwood7f53a192010-07-09 10:45:22 -040032#include "MtpDatabase.h"
Mike Lockwood7d77dcf2011-04-21 17:05:55 -070033#include "MtpObjectInfo.h"
Mike Lockwood21ef7d02010-06-30 17:00:35 -040034#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040035#include "MtpServer.h"
36#include "MtpStorage.h"
37#include "MtpStringBuffer.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040038
Mike Lockwood8065e202010-07-15 13:36:52 -040039#include <linux/usb/f_mtp.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040040
Mike Lockwood7850ef92010-05-14 10:10:36 -040041namespace android {
42
Mike Lockwood16864ba2010-05-11 17:16:59 -040043static const MtpOperationCode kSupportedOperationCodes[] = {
44 MTP_OPERATION_GET_DEVICE_INFO,
45 MTP_OPERATION_OPEN_SESSION,
46 MTP_OPERATION_CLOSE_SESSION,
47 MTP_OPERATION_GET_STORAGE_IDS,
48 MTP_OPERATION_GET_STORAGE_INFO,
49 MTP_OPERATION_GET_NUM_OBJECTS,
50 MTP_OPERATION_GET_OBJECT_HANDLES,
51 MTP_OPERATION_GET_OBJECT_INFO,
52 MTP_OPERATION_GET_OBJECT,
53// MTP_OPERATION_GET_THUMB,
54 MTP_OPERATION_DELETE_OBJECT,
55 MTP_OPERATION_SEND_OBJECT_INFO,
56 MTP_OPERATION_SEND_OBJECT,
57// MTP_OPERATION_INITIATE_CAPTURE,
58// MTP_OPERATION_FORMAT_STORE,
59// MTP_OPERATION_RESET_DEVICE,
60// MTP_OPERATION_SELF_TEST,
61// MTP_OPERATION_SET_OBJECT_PROTECTION,
62// MTP_OPERATION_POWER_DOWN,
Mike Lockwoode3e76c42010-09-02 14:57:30 -040063 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood8277cec2010-08-10 15:20:35 -040064 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
65 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
66 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040067// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
68// MTP_OPERATION_MOVE_OBJECT,
69// MTP_OPERATION_COPY_OBJECT,
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -050070 MTP_OPERATION_GET_PARTIAL_OBJECT,
Mike Lockwood16864ba2010-05-11 17:16:59 -040071// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
72 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
Mike Lockwood8277cec2010-08-10 15:20:35 -040073 MTP_OPERATION_GET_OBJECT_PROP_DESC,
Mike Lockwood677f5702010-09-23 23:04:28 -040074 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
75 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwoodb6da06e2010-10-14 18:03:25 -040076 MTP_OPERATION_GET_OBJECT_PROP_LIST,
77// MTP_OPERATION_SET_OBJECT_PROP_LIST,
78// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
79// MTP_OPERATION_SEND_OBJECT_PROP_LIST,
Mike Lockwood438344f2010-08-03 15:30:09 -040080 MTP_OPERATION_GET_OBJECT_REFERENCES,
81 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood16864ba2010-05-11 17:16:59 -040082// MTP_OPERATION_SKIP,
Mike Lockwood7d77dcf2011-04-21 17:05:55 -070083 // Android extension for direct file IO
84 MTP_OPERATION_GET_PARTIAL_OBJECT_64,
85 MTP_OPERATION_SEND_PARTIAL_OBJECT,
86 MTP_OPERATION_TRUNCATE_OBJECT,
87 MTP_OPERATION_BEGIN_EDIT_OBJECT,
88 MTP_OPERATION_END_EDIT_OBJECT,
Mike Lockwood16864ba2010-05-11 17:16:59 -040089};
90
Mike Lockwood873871f2010-07-12 18:54:16 -040091static const MtpEventCode kSupportedEventCodes[] = {
92 MTP_EVENT_OBJECT_ADDED,
93 MTP_EVENT_OBJECT_REMOVED,
Mike Lockwooda8494402011-02-18 09:07:14 -050094 MTP_EVENT_STORE_ADDED,
95 MTP_EVENT_STORE_REMOVED,
Mike Lockwood873871f2010-07-12 18:54:16 -040096};
97
Mike Lockwood1865a5d2010-07-03 00:44:05 -040098MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwood8e2a2802010-07-02 15:15:07 -040099 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -0400100 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400101 mDatabase(database),
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400102 mFileGroup(fileGroup),
103 mFilePermission(filePerm),
104 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400105 mSessionID(0),
106 mSessionOpen(false),
107 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -0400108 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400109 mSendObjectFileSize(0)
110{
Mike Lockwood16864ba2010-05-11 17:16:59 -0400111}
112
113MtpServer::~MtpServer() {
114}
115
Mike Lockwooda8494402011-02-18 09:07:14 -0500116void MtpServer::addStorage(MtpStorage* storage) {
117 Mutex::Autolock autoLock(mMutex);
118
119 mStorages.push(storage);
120 sendStoreAdded(storage->getStorageID());
121}
122
123void MtpServer::removeStorage(MtpStorage* storage) {
124 Mutex::Autolock autoLock(mMutex);
125
126 for (int i = 0; i < mStorages.size(); i++) {
127 if (mStorages[i] == storage) {
128 mStorages.removeAt(i);
129 sendStoreRemoved(storage->getStorageID());
130 break;
131 }
132 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400133}
134
135MtpStorage* MtpServer::getStorage(MtpStorageID id) {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800136 if (id == 0)
137 return mStorages[0];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400138 for (int i = 0; i < mStorages.size(); i++) {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800139 MtpStorage* storage = mStorages[i];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400140 if (storage->getStorageID() == id)
141 return storage;
142 }
143 return NULL;
144}
145
Mike Lockwooda8494402011-02-18 09:07:14 -0500146bool MtpServer::hasStorage(MtpStorageID id) {
147 if (id == 0 || id == 0xFFFFFFFF)
148 return mStorages.size() > 0;
149 return (getStorage(id) != NULL);
150}
151
Mike Lockwood16864ba2010-05-11 17:16:59 -0400152void MtpServer::run() {
153 int fd = mFD;
154
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400155 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400156
157 while (1) {
158 int ret = mRequest.read(fd);
159 if (ret < 0) {
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800160 LOGV("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400161 if (errno == ECANCELED) {
162 // return to top of loop and wait for next command
163 continue;
164 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400165 break;
166 }
167 MtpOperationCode operation = mRequest.getOperationCode();
168 MtpTransactionID transaction = mRequest.getTransactionID();
169
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400170 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400171 mRequest.dump();
172
173 // FIXME need to generalize this
Mike Lockwood438344f2010-08-03 15:30:09 -0400174 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood8277cec2010-08-10 15:20:35 -0400175 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
176 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
177 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400178 if (dataIn) {
179 int ret = mData.read(fd);
180 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400181 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400182 if (errno == ECANCELED) {
183 // return to top of loop and wait for next command
184 continue;
185 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400186 break;
187 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400188 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400189 mData.dump();
190 } else {
191 mData.reset();
192 }
193
Mike Lockwood916076c2010-06-04 09:49:21 -0400194 if (handleRequest()) {
195 if (!dataIn && mData.hasData()) {
196 mData.setOperationCode(operation);
197 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400198 LOGV("sending data:");
Mike Lockwood23d20712010-10-11 17:31:44 -0400199 mData.dump();
Mike Lockwood916076c2010-06-04 09:49:21 -0400200 ret = mData.write(fd);
201 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400202 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400203 if (errno == ECANCELED) {
204 // return to top of loop and wait for next command
205 continue;
206 }
207 break;
208 }
209 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400210
Mike Lockwood916076c2010-06-04 09:49:21 -0400211 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400212 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400213 ret = mResponse.write(fd);
Mike Lockwood23d20712010-10-11 17:31:44 -0400214 mResponse.dump();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400215 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400216 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400217 if (errno == ECANCELED) {
218 // return to top of loop and wait for next command
219 continue;
220 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400221 break;
222 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400223 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400224 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400225 }
226 }
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400227
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700228 // commit any open edits
229 int count = mObjectEditList.size();
230 for (int i = 0; i < count; i++) {
231 ObjectEdit* edit = mObjectEditList[i];
232 commitEdit(edit);
233 delete edit;
234 }
235 mObjectEditList.clear();
236
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400237 if (mSessionOpen)
238 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400239}
240
Mike Lockwood873871f2010-07-12 18:54:16 -0400241void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500242 LOGV("sendObjectAdded %d\n", handle);
243 sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
Mike Lockwood873871f2010-07-12 18:54:16 -0400244}
245
246void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500247 LOGV("sendObjectRemoved %d\n", handle);
248 sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
249}
250
251void MtpServer::sendStoreAdded(MtpStorageID id) {
252 LOGV("sendStoreAdded %08X\n", id);
253 sendEvent(MTP_EVENT_STORE_ADDED, id);
254}
255
256void MtpServer::sendStoreRemoved(MtpStorageID id) {
257 LOGV("sendStoreRemoved %08X\n", id);
258 sendEvent(MTP_EVENT_STORE_REMOVED, id);
259}
260
261void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400262 if (mSessionOpen) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500263 mEvent.setEventCode(code);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400264 mEvent.setTransactionID(mRequest.getTransactionID());
Mike Lockwooda8494402011-02-18 09:07:14 -0500265 mEvent.setParameter(1, param1);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400266 int ret = mEvent.write(mFD);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800267 LOGV("mEvent.write returned %d\n", ret);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400268 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400269}
270
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700271void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
272 uint64_t size, MtpObjectFormat format, int fd) {
273 ObjectEdit* edit = new ObjectEdit;
274 edit->handle = handle;
275 edit->path = path;
276 edit->size = size;
277 edit->format = format;
278 edit->fd = fd;
279 mObjectEditList.add(edit);
280}
281
282MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
283 int count = mObjectEditList.size();
284 for (int i = 0; i < count; i++) {
285 ObjectEdit* edit = mObjectEditList[i];
286 if (edit->handle == handle) return edit;
287 }
288 return NULL;
289}
290
291void MtpServer::removeEditObject(MtpObjectHandle handle) {
292 int count = mObjectEditList.size();
293 for (int i = 0; i < count; i++) {
294 ObjectEdit* edit = mObjectEditList[i];
295 if (edit->handle == handle) {
296 delete edit;
297 mObjectEditList.removeAt(i);
298 return;
299 }
300 }
301 LOGE("ObjectEdit not found in removeEditObject");
302}
303
304void MtpServer::commitEdit(ObjectEdit* edit) {
305 mDatabase->endSendObject((const char *)edit->path, edit->handle, edit->format, true);
306}
307
308
Mike Lockwood916076c2010-06-04 09:49:21 -0400309bool MtpServer::handleRequest() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500310 Mutex::Autolock autoLock(mMutex);
311
Mike Lockwood16864ba2010-05-11 17:16:59 -0400312 MtpOperationCode operation = mRequest.getOperationCode();
313 MtpResponseCode response;
314
315 mResponse.reset();
316
317 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
318 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400319 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400320 mSendObjectHandle = kInvalidObjectHandle;
321 }
322
323 switch (operation) {
324 case MTP_OPERATION_GET_DEVICE_INFO:
325 response = doGetDeviceInfo();
326 break;
327 case MTP_OPERATION_OPEN_SESSION:
328 response = doOpenSession();
329 break;
330 case MTP_OPERATION_CLOSE_SESSION:
331 response = doCloseSession();
332 break;
333 case MTP_OPERATION_GET_STORAGE_IDS:
334 response = doGetStorageIDs();
335 break;
336 case MTP_OPERATION_GET_STORAGE_INFO:
337 response = doGetStorageInfo();
338 break;
339 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
340 response = doGetObjectPropsSupported();
341 break;
342 case MTP_OPERATION_GET_OBJECT_HANDLES:
343 response = doGetObjectHandles();
344 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400345 case MTP_OPERATION_GET_NUM_OBJECTS:
346 response = doGetNumObjects();
347 break;
Mike Lockwood438344f2010-08-03 15:30:09 -0400348 case MTP_OPERATION_GET_OBJECT_REFERENCES:
349 response = doGetObjectReferences();
350 break;
351 case MTP_OPERATION_SET_OBJECT_REFERENCES:
352 response = doSetObjectReferences();
353 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400354 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
355 response = doGetObjectPropValue();
356 break;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400357 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
358 response = doSetObjectPropValue();
359 break;
360 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
361 response = doGetDevicePropValue();
362 break;
363 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
364 response = doSetDevicePropValue();
365 break;
366 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
367 response = doResetDevicePropValue();
368 break;
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400369 case MTP_OPERATION_GET_OBJECT_PROP_LIST:
370 response = doGetObjectPropList();
371 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400372 case MTP_OPERATION_GET_OBJECT_INFO:
373 response = doGetObjectInfo();
374 break;
375 case MTP_OPERATION_GET_OBJECT:
376 response = doGetObject();
377 break;
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500378 case MTP_OPERATION_GET_PARTIAL_OBJECT:
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700379 case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
380 response = doGetPartialObject(operation);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500381 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400382 case MTP_OPERATION_SEND_OBJECT_INFO:
383 response = doSendObjectInfo();
384 break;
385 case MTP_OPERATION_SEND_OBJECT:
386 response = doSendObject();
387 break;
388 case MTP_OPERATION_DELETE_OBJECT:
389 response = doDeleteObject();
390 break;
391 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400392 response = doGetObjectPropDesc();
393 break;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400394 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
395 response = doGetDevicePropDesc();
396 break;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700397 case MTP_OPERATION_SEND_PARTIAL_OBJECT:
398 response = doSendPartialObject();
399 break;
400 case MTP_OPERATION_TRUNCATE_OBJECT:
401 response = doTruncateObject();
402 break;
403 case MTP_OPERATION_BEGIN_EDIT_OBJECT:
404 response = doBeginEditObject();
405 break;
406 case MTP_OPERATION_END_EDIT_OBJECT:
407 response = doEndEditObject();
408 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400409 default:
Mike Lockwooda881b442010-09-23 22:32:05 -0400410 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400411 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
412 break;
413 }
414
Mike Lockwood916076c2010-06-04 09:49:21 -0400415 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
416 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400417 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400418 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400419}
420
421MtpResponseCode MtpServer::doGetDeviceInfo() {
422 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700423 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400424
Mike Lockwood782aef12010-08-10 07:37:50 -0400425 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
426 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
427 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
428
Mike Lockwood16864ba2010-05-11 17:16:59 -0400429 // fill in device info
430 mData.putUInt16(MTP_STANDARD_VERSION);
431 mData.putUInt32(6); // MTP Vendor Extension ID
432 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700433 string.set("microsoft.com: 1.0; android.com: 1.0;");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400434 mData.putString(string); // MTP Extensions
435 mData.putUInt16(0); //Functional Mode
436 mData.putAUInt16(kSupportedOperationCodes,
437 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400438 mData.putAUInt16(kSupportedEventCodes,
439 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood782aef12010-08-10 07:37:50 -0400440 mData.putAUInt16(deviceProperties); // Device Properties Supported
441 mData.putAUInt16(captureFormats); // Capture Formats
442 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood8d08c5a2011-01-31 16:44:44 -0500443
444 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
445 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400446 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700447
448 property_get("ro.product.model", prop_value, "MTP Device");
449 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400450 mData.putString(string); // Model
451 string.set("1.0");
452 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700453
454 property_get("ro.serialno", prop_value, "????????");
455 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400456 mData.putString(string); // Serial Number
457
Mike Lockwood782aef12010-08-10 07:37:50 -0400458 delete playbackFormats;
459 delete captureFormats;
460 delete deviceProperties;
461
Mike Lockwood16864ba2010-05-11 17:16:59 -0400462 return MTP_RESPONSE_OK;
463}
464
465MtpResponseCode MtpServer::doOpenSession() {
466 if (mSessionOpen) {
467 mResponse.setParameter(1, mSessionID);
468 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
469 }
470 mSessionID = mRequest.getParameter(1);
471 mSessionOpen = true;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400472
473 mDatabase->sessionStarted();
474
Mike Lockwood16864ba2010-05-11 17:16:59 -0400475 return MTP_RESPONSE_OK;
476}
477
478MtpResponseCode MtpServer::doCloseSession() {
479 if (!mSessionOpen)
480 return MTP_RESPONSE_SESSION_NOT_OPEN;
481 mSessionID = 0;
482 mSessionOpen = false;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400483 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400484 return MTP_RESPONSE_OK;
485}
486
487MtpResponseCode MtpServer::doGetStorageIDs() {
488 if (!mSessionOpen)
489 return MTP_RESPONSE_SESSION_NOT_OPEN;
490
491 int count = mStorages.size();
492 mData.putUInt32(count);
493 for (int i = 0; i < count; i++)
494 mData.putUInt32(mStorages[i]->getStorageID());
495
496 return MTP_RESPONSE_OK;
497}
498
499MtpResponseCode MtpServer::doGetStorageInfo() {
500 MtpStringBuffer string;
501
502 if (!mSessionOpen)
503 return MTP_RESPONSE_SESSION_NOT_OPEN;
504 MtpStorageID id = mRequest.getParameter(1);
505 MtpStorage* storage = getStorage(id);
506 if (!storage)
507 return MTP_RESPONSE_INVALID_STORAGE_ID;
508
509 mData.putUInt16(storage->getType());
510 mData.putUInt16(storage->getFileSystemType());
511 mData.putUInt16(storage->getAccessCapability());
512 mData.putUInt64(storage->getMaxCapacity());
513 mData.putUInt64(storage->getFreeSpace());
514 mData.putUInt32(1024*1024*1024); // Free Space in Objects
515 string.set(storage->getDescription());
516 mData.putString(string);
517 mData.putEmptyString(); // Volume Identifier
518
519 return MTP_RESPONSE_OK;
520}
521
522MtpResponseCode MtpServer::doGetObjectPropsSupported() {
523 if (!mSessionOpen)
524 return MTP_RESPONSE_SESSION_NOT_OPEN;
525 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood2e09e282010-12-07 10:51:20 -0800526 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
Mike Lockwood782aef12010-08-10 07:37:50 -0400527 mData.putAUInt16(properties);
Mike Lockwoodbf9b2052010-08-10 15:11:32 -0400528 delete properties;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400529 return MTP_RESPONSE_OK;
530}
531
532MtpResponseCode MtpServer::doGetObjectHandles() {
533 if (!mSessionOpen)
534 return MTP_RESPONSE_SESSION_NOT_OPEN;
535 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400536 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400537 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
538 // 0x00000000 for all objects?
Mike Lockwooda8494402011-02-18 09:07:14 -0500539
540 if (!hasStorage(storageID))
541 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400542 if (parent == 0xFFFFFFFF)
543 parent = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400544
545 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
546 mData.putAUInt32(handles);
547 delete handles;
548 return MTP_RESPONSE_OK;
549}
550
Mike Lockwood343af4e2010-08-02 10:52:20 -0400551MtpResponseCode MtpServer::doGetNumObjects() {
552 if (!mSessionOpen)
553 return MTP_RESPONSE_SESSION_NOT_OPEN;
554 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
555 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
556 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
557 // 0x00000000 for all objects?
Mike Lockwooda8494402011-02-18 09:07:14 -0500558 if (!hasStorage(storageID))
559 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400560 if (parent == 0xFFFFFFFF)
561 parent = 0;
562
563 int count = mDatabase->getNumObjects(storageID, format, parent);
564 if (count >= 0) {
565 mResponse.setParameter(1, count);
566 return MTP_RESPONSE_OK;
567 } else {
568 mResponse.setParameter(1, 0);
569 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
570 }
571}
572
Mike Lockwood438344f2010-08-03 15:30:09 -0400573MtpResponseCode MtpServer::doGetObjectReferences() {
574 if (!mSessionOpen)
575 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwooda8494402011-02-18 09:07:14 -0500576 if (!hasStorage())
577 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
578 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400579
580 // FIXME - check for invalid object handle
Mike Lockwood438344f2010-08-03 15:30:09 -0400581 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400582 if (handles) {
583 mData.putAUInt32(handles);
584 delete handles;
585 } else {
Mike Lockwood438344f2010-08-03 15:30:09 -0400586 mData.putEmptyArray();
Mike Lockwood438344f2010-08-03 15:30:09 -0400587 }
Mike Lockwood438344f2010-08-03 15:30:09 -0400588 return MTP_RESPONSE_OK;
589}
590
591MtpResponseCode MtpServer::doSetObjectReferences() {
592 if (!mSessionOpen)
593 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwooda8494402011-02-18 09:07:14 -0500594 if (!hasStorage())
595 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood438344f2010-08-03 15:30:09 -0400596 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwooda8494402011-02-18 09:07:14 -0500597
Mike Lockwood438344f2010-08-03 15:30:09 -0400598 MtpObjectHandleList* references = mData.getAUInt32();
599 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
600 delete references;
601 return result;
602}
603
Mike Lockwood16864ba2010-05-11 17:16:59 -0400604MtpResponseCode MtpServer::doGetObjectPropValue() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500605 if (!hasStorage())
606 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400607 MtpObjectHandle handle = mRequest.getParameter(1);
608 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800609 LOGV("GetObjectPropValue %d %s\n", handle,
Mike Lockwood8277cec2010-08-10 15:20:35 -0400610 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400611
Mike Lockwood8277cec2010-08-10 15:20:35 -0400612 return mDatabase->getObjectPropertyValue(handle, property, mData);
613}
614
615MtpResponseCode MtpServer::doSetObjectPropValue() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500616 if (!hasStorage())
617 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400618 MtpObjectHandle handle = mRequest.getParameter(1);
619 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800620 LOGV("SetObjectPropValue %d %s\n", handle,
Mike Lockwood8277cec2010-08-10 15:20:35 -0400621 MtpDebug::getObjectPropCodeName(property));
622
623 return mDatabase->setObjectPropertyValue(handle, property, mData);
624}
625
626MtpResponseCode MtpServer::doGetDevicePropValue() {
627 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800628 LOGV("GetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400629 MtpDebug::getDevicePropCodeName(property));
630
631 return mDatabase->getDevicePropertyValue(property, mData);
632}
633
634MtpResponseCode MtpServer::doSetDevicePropValue() {
635 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800636 LOGV("SetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400637 MtpDebug::getDevicePropCodeName(property));
638
639 return mDatabase->setDevicePropertyValue(property, mData);
640}
641
642MtpResponseCode MtpServer::doResetDevicePropValue() {
643 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800644 LOGV("ResetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400645 MtpDebug::getDevicePropCodeName(property));
646
647 return mDatabase->resetDeviceProperty(property);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400648}
649
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400650MtpResponseCode MtpServer::doGetObjectPropList() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500651 if (!hasStorage())
652 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400653
654 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood40ce1f22010-12-01 18:46:23 -0500655 // use uint32_t so we can support 0xFFFFFFFF
656 uint32_t format = mRequest.getParameter(2);
657 uint32_t property = mRequest.getParameter(3);
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400658 int groupCode = mRequest.getParameter(4);
Mike Lockwoodf05ff072010-11-23 18:45:25 -0500659 int depth = mRequest.getParameter(5);
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800660 LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400661 handle, MtpDebug::getFormatCodeName(format),
662 MtpDebug::getObjectPropCodeName(property), groupCode, depth);
663
664 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
665}
666
Mike Lockwood16864ba2010-05-11 17:16:59 -0400667MtpResponseCode MtpServer::doGetObjectInfo() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500668 if (!hasStorage())
669 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400670 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700671 MtpObjectInfo info(handle);
672 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
673 if (result == MTP_RESPONSE_OK) {
674 char date[20];
675
676 mData.putUInt32(info.mStorageID);
677 mData.putUInt16(info.mFormat);
678 mData.putUInt16(info.mProtectionStatus);
679
680 // if object is being edited the database size may be out of date
681 uint32_t size = info.mCompressedSize;
682 ObjectEdit* edit = getEditObject(handle);
683 if (edit)
684 size = (edit->size > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->size);
685 mData.putUInt32(size);
686
687 mData.putUInt16(info.mThumbFormat);
688 mData.putUInt32(info.mThumbCompressedSize);
689 mData.putUInt32(info.mThumbPixWidth);
690 mData.putUInt32(info.mThumbPixHeight);
691 mData.putUInt32(info.mImagePixWidth);
692 mData.putUInt32(info.mImagePixHeight);
693 mData.putUInt32(info.mImagePixDepth);
694 mData.putUInt32(info.mParent);
695 mData.putUInt16(info.mAssociationType);
696 mData.putUInt32(info.mAssociationDesc);
697 mData.putUInt32(info.mSequenceNumber);
698 mData.putString(info.mName);
699 mData.putEmptyString(); // date created
700 formatDateTime(info.mDateModified, date, sizeof(date));
701 mData.putString(date); // date modified
702 mData.putEmptyString(); // keywords
703 }
704 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400705}
706
707MtpResponseCode MtpServer::doGetObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500708 if (!hasStorage())
709 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400710 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400711 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400712 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800713 MtpObjectFormat format;
714 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400715 if (result != MTP_RESPONSE_OK)
716 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400717
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400718 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400719 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400720 mfr.fd = open(filePath, O_RDONLY);
721 if (mfr.fd < 0) {
722 return MTP_RESPONSE_GENERAL_ERROR;
723 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400724 mfr.offset = 0;
725 mfr.length = fileLength;
726
727 // send data header
728 mData.setOperationCode(mRequest.getOperationCode());
729 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood23d20712010-10-11 17:31:44 -0400730 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400731
732 // then transfer the file
733 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400734 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400735 if (ret < 0) {
736 if (errno == ECANCELED)
737 return MTP_RESPONSE_TRANSACTION_CANCELLED;
738 else
739 return MTP_RESPONSE_GENERAL_ERROR;
740 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400741 return MTP_RESPONSE_OK;
742}
743
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700744MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500745 if (!hasStorage())
746 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500747 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700748 uint64_t offset;
749 uint32_t length;
750 offset = mRequest.getParameter(2);
751 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
752 // android extension with 64 bit offset
753 uint64_t offset2 = mRequest.getParameter(3);
754 offset = offset | (offset2 << 32);
755 length = mRequest.getParameter(4);
756 } else {
757 // standard GetPartialObject
758 length = mRequest.getParameter(3);
759 }
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500760 MtpString pathBuf;
761 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800762 MtpObjectFormat format;
763 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500764 if (result != MTP_RESPONSE_OK)
765 return result;
766 if (offset + length > fileLength)
767 length = fileLength - offset;
768
769 const char* filePath = (const char *)pathBuf;
770 mtp_file_range mfr;
771 mfr.fd = open(filePath, O_RDONLY);
772 if (mfr.fd < 0) {
773 return MTP_RESPONSE_GENERAL_ERROR;
774 }
775 mfr.offset = offset;
776 mfr.length = length;
777 mResponse.setParameter(1, length);
778
779 // send data header
780 mData.setOperationCode(mRequest.getOperationCode());
781 mData.setTransactionID(mRequest.getTransactionID());
782 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
783
784 // then transfer the file
785 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
786 close(mfr.fd);
787 if (ret < 0) {
788 if (errno == ECANCELED)
789 return MTP_RESPONSE_TRANSACTION_CANCELLED;
790 else
791 return MTP_RESPONSE_GENERAL_ERROR;
792 }
793 return MTP_RESPONSE_OK;
794}
795
Mike Lockwood16864ba2010-05-11 17:16:59 -0400796MtpResponseCode MtpServer::doSendObjectInfo() {
797 MtpString path;
798 MtpStorageID storageID = mRequest.getParameter(1);
799 MtpStorage* storage = getStorage(storageID);
800 MtpObjectHandle parent = mRequest.getParameter(2);
801 if (!storage)
802 return MTP_RESPONSE_INVALID_STORAGE_ID;
803
804 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400805 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400806 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400807 parent = 0;
808 } else {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800809 int64_t length;
810 MtpObjectFormat format;
811 int result = mDatabase->getObjectFilePath(parent, path, length, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400812 if (result != MTP_RESPONSE_OK)
813 return result;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800814 if (format != MTP_FORMAT_ASSOCIATION)
815 return MTP_RESPONSE_INVALID_PARENT_OBJECT;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400816 }
817
818 // read only the fields we need
819 mData.getUInt32(); // storage ID
820 MtpObjectFormat format = mData.getUInt16();
821 mData.getUInt16(); // protection status
822 mSendObjectFileSize = mData.getUInt32();
823 mData.getUInt16(); // thumb format
824 mData.getUInt32(); // thumb compressed size
825 mData.getUInt32(); // thumb pix width
826 mData.getUInt32(); // thumb pix height
827 mData.getUInt32(); // image pix width
828 mData.getUInt32(); // image pix height
829 mData.getUInt32(); // image bit depth
830 mData.getUInt32(); // parent
831 uint16_t associationType = mData.getUInt16();
832 uint32_t associationDesc = mData.getUInt32(); // association desc
833 mData.getUInt32(); // sequence number
834 MtpStringBuffer name, created, modified;
835 mData.getString(name); // file name
836 mData.getString(created); // date created
837 mData.getString(modified); // date modified
838 // keywords follow
839
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800840 LOGV("name: %s format: %04X\n", (const char *)name, format);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400841 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400842 if (!parseDateTime(modified, modifiedTime))
843 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400844
845 if (path[path.size() - 1] != '/')
846 path += "/";
847 path += (const char *)name;
848
Mike Lockwood20c3be02010-12-12 12:17:43 -0800849 // check space first
850 if (mSendObjectFileSize > storage->getFreeSpace())
851 return MTP_RESPONSE_STORAGE_FULL;
852
Mike Lockwooda8494402011-02-18 09:07:14 -0500853LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
Mike Lockwood4714b072010-07-12 08:49:01 -0400854 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
855 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400856 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400857 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400858 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400859
860 if (format == MTP_FORMAT_ASSOCIATION) {
861 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400862 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400863 umask(mask);
864 if (ret && ret != -EEXIST)
865 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400866 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwoodaa952402011-01-18 11:06:19 -0800867
868 // SendObject does not get sent for directories, so call endSendObject here instead
869 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400870 } else {
871 mSendObjectFilePath = path;
872 // save the handle for the SendObject call, which should follow
873 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400874 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400875 }
876
877 mResponse.setParameter(1, storageID);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400878 mResponse.setParameter(2, parent);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400879 mResponse.setParameter(3, handle);
880
881 return MTP_RESPONSE_OK;
882}
883
884MtpResponseCode MtpServer::doSendObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500885 if (!hasStorage())
886 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood4714b072010-07-12 08:49:01 -0400887 MtpResponseCode result = MTP_RESPONSE_OK;
888 mode_t mask;
889 int ret;
890
Mike Lockwood16864ba2010-05-11 17:16:59 -0400891 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400892 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400893 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
894 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400895 }
896
897 // read the header
Mike Lockwood4714b072010-07-12 08:49:01 -0400898 ret = mData.readDataHeader(mFD);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400899 // FIXME - check for errors here.
900
901 // reset so we don't attempt to send this back
902 mData.reset();
903
904 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400905 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
906 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400907 result = MTP_RESPONSE_GENERAL_ERROR;
908 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400909 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400910 fchown(mfr.fd, getuid(), mFileGroup);
911 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400912 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400913 fchmod(mfr.fd, mFilePermission);
914 umask(mask);
915
Mike Lockwood16864ba2010-05-11 17:16:59 -0400916 mfr.offset = 0;
917 mfr.length = mSendObjectFileSize;
918
Mike Lockwood59d6ae52011-01-21 21:00:54 -0800919 LOGV("receiving %s\n", (const char *)mSendObjectFilePath);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400920 // transfer the file
921 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400922 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400923
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400924 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400925
Mike Lockwood916076c2010-06-04 09:49:21 -0400926 if (ret < 0) {
927 unlink(mSendObjectFilePath);
928 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400929 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400930 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400931 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400932 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400933
934done:
935 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
Mike Lockwoodaa952402011-01-18 11:06:19 -0800936 result == MTP_RESPONSE_OK);
Mike Lockwood4714b072010-07-12 08:49:01 -0400937 mSendObjectHandle = kInvalidObjectHandle;
938 mSendObjectFormat = 0;
939 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400940}
941
Mike Lockwoodd3211492010-09-13 17:15:58 -0400942static void deleteRecursive(const char* path) {
943 char pathbuf[PATH_MAX];
944 int pathLength = strlen(path);
945 if (pathLength >= sizeof(pathbuf) - 1) {
946 LOGE("path too long: %s\n", path);
947 }
948 strcpy(pathbuf, path);
949 if (pathbuf[pathLength - 1] != '/') {
950 pathbuf[pathLength++] = '/';
951 }
952 char* fileSpot = pathbuf + pathLength;
953 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
954
955 DIR* dir = opendir(path);
956 if (!dir) {
957 LOGE("opendir %s failed: %s", path, strerror(errno));
958 return;
959 }
960
961 struct dirent* entry;
962 while ((entry = readdir(dir))) {
963 const char* name = entry->d_name;
964
965 // ignore "." and ".."
966 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
967 continue;
968 }
969
970 int nameLength = strlen(name);
971 if (nameLength > pathRemaining) {
972 LOGE("path %s/%s too long\n", path, name);
973 continue;
974 }
975 strcpy(fileSpot, name);
976
977 int type = entry->d_type;
978 if (entry->d_type == DT_DIR) {
979 deleteRecursive(pathbuf);
980 rmdir(pathbuf);
981 } else {
982 unlink(pathbuf);
983 }
984 }
Mike Lockwood7ce05cf2010-11-11 11:22:32 -0500985 closedir(dir);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400986}
987
988static void deletePath(const char* path) {
989 struct stat statbuf;
990 if (stat(path, &statbuf) == 0) {
991 if (S_ISDIR(statbuf.st_mode)) {
992 deleteRecursive(path);
993 rmdir(path);
994 } else {
995 unlink(path);
996 }
997 } else {
998 LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
999 }
1000}
1001
Mike Lockwood16864ba2010-05-11 17:16:59 -04001002MtpResponseCode MtpServer::doDeleteObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -05001003 if (!hasStorage())
1004 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001005 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001006 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood16864ba2010-05-11 17:16:59 -04001007 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1008 // FIXME - implement deleting objects by format
Mike Lockwood16864ba2010-05-11 17:16:59 -04001009
1010 MtpString filePath;
1011 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -08001012 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -04001013 if (result == MTP_RESPONSE_OK) {
1014 LOGV("deleting %s", (const char *)filePath);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001015 deletePath((const char *)filePath);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -04001016 return mDatabase->deleteFile(handle);
1017 } else {
1018 return result;
1019 }
Mike Lockwood16864ba2010-05-11 17:16:59 -04001020}
1021
1022MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001023 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -04001024 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood59d6ae52011-01-21 21:00:54 -08001025 LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
Mike Lockwood8277cec2010-08-10 15:20:35 -04001026 MtpDebug::getFormatCodeName(format));
1027 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001028 if (!property)
1029 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001030 property->write(mData);
Mike Lockwood8277cec2010-08-10 15:20:35 -04001031 delete property;
1032 return MTP_RESPONSE_OK;
1033}
1034
1035MtpResponseCode MtpServer::doGetDevicePropDesc() {
1036 MtpDeviceProperty propCode = mRequest.getParameter(1);
Mike Lockwood59d6ae52011-01-21 21:00:54 -08001037 LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
Mike Lockwood8277cec2010-08-10 15:20:35 -04001038 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1039 if (!property)
1040 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1041 property->write(mData);
1042 delete property;
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001043 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001044}
Mike Lockwood7850ef92010-05-14 10:10:36 -04001045
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001046MtpResponseCode MtpServer::doSendPartialObject() {
1047 if (!hasStorage())
1048 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1049 MtpObjectHandle handle = mRequest.getParameter(1);
1050 uint64_t offset = mRequest.getParameter(2);
1051 uint64_t offset2 = mRequest.getParameter(3);
1052 offset = offset | (offset2 << 32);
1053 uint32_t length = mRequest.getParameter(4);
1054
1055 ObjectEdit* edit = getEditObject(handle);
1056 if (!edit) {
1057 LOGE("object not open for edit in doSendPartialObject");
1058 return MTP_RESPONSE_GENERAL_ERROR;
1059 }
1060
1061 // can't start writing past the end of the file
1062 if (offset > edit->size) {
1063 LOGD("writing past end of object, offset: %lld, edit->size: %lld", offset, edit->size);
1064 return MTP_RESPONSE_GENERAL_ERROR;
1065 }
1066
1067 // read the header
1068 int ret = mData.readDataHeader(mFD);
1069 // FIXME - check for errors here.
1070
1071 // reset so we don't attempt to send this back
1072 mData.reset();
1073
1074 const char* filePath = (const char *)edit->path;
1075 LOGV("receiving partial %s %lld %ld\n", filePath, offset, length);
1076 mtp_file_range mfr;
1077 mfr.fd = edit->fd;
1078 mfr.offset = offset;
1079 mfr.length = length;
1080
1081 // transfer the file
1082 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1083 LOGV("MTP_RECEIVE_FILE returned %d", ret);
1084 if (ret < 0) {
1085 mResponse.setParameter(1, 0);
1086 if (errno == ECANCELED)
1087 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1088 else
1089 return MTP_RESPONSE_GENERAL_ERROR;
1090 }
1091 mResponse.setParameter(1, length);
1092 uint64_t end = offset + length;
1093 if (end > edit->size) {
1094 edit->size = end;
1095 }
1096 return MTP_RESPONSE_OK;
1097}
1098
1099MtpResponseCode MtpServer::doTruncateObject() {
1100 MtpObjectHandle handle = mRequest.getParameter(1);
1101 ObjectEdit* edit = getEditObject(handle);
1102 if (!edit) {
1103 LOGE("object not open for edit in doTruncateObject");
1104 return MTP_RESPONSE_GENERAL_ERROR;
1105 }
1106
1107 uint64_t offset = mRequest.getParameter(2);
1108 uint64_t offset2 = mRequest.getParameter(3);
1109 offset |= (offset2 << 32);
1110 if (ftruncate(edit->fd, offset) != 0) {
1111 return MTP_RESPONSE_GENERAL_ERROR;
1112 } else {
1113 edit->size = offset;
1114 return MTP_RESPONSE_OK;
1115 }
1116}
1117
1118MtpResponseCode MtpServer::doBeginEditObject() {
1119 MtpObjectHandle handle = mRequest.getParameter(1);
1120 if (getEditObject(handle)) {
1121 LOGE("object already open for edit in doBeginEditObject");
1122 return MTP_RESPONSE_GENERAL_ERROR;
1123 }
1124
1125 MtpString path;
1126 int64_t fileLength;
1127 MtpObjectFormat format;
1128 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1129 if (result != MTP_RESPONSE_OK)
1130 return result;
1131
1132 int fd = open((const char *)path, O_RDWR | O_EXCL);
1133 if (fd < 0) {
1134 LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1135 return MTP_RESPONSE_GENERAL_ERROR;
1136 }
1137
1138 addEditObject(handle, path, fileLength, format, fd);
1139 return MTP_RESPONSE_OK;
1140}
1141
1142MtpResponseCode MtpServer::doEndEditObject() {
1143 MtpObjectHandle handle = mRequest.getParameter(1);
1144 ObjectEdit* edit = getEditObject(handle);
1145 if (!edit) {
1146 LOGE("object not open for edit in doEndEditObject");
1147 return MTP_RESPONSE_GENERAL_ERROR;
1148 }
1149
1150 commitEdit(edit);
1151 removeEditObject(handle);
1152 return MTP_RESPONSE_OK;
1153}
1154
Mike Lockwood7850ef92010-05-14 10:10:36 -04001155} // namespace android