blob: bc04e8ca64c136788d10d4a6684906b716666e65 [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>
Mike Lockwoodccb6e962010-09-13 17:15:58 -040024#include <sys/stat.h>
25#include <dirent.h>
Mike Lockwood56118b52010-05-11 17:16:59 -040026
Mike Lockwood622ccdc2010-06-14 17:58:08 -070027#include <cutils/properties.h>
28
Mike Lockwood9f679242010-09-23 22:32:05 -040029#define LOG_TAG "MtpServer"
30
Mike Lockwood56118b52010-05-11 17:16:59 -040031#include "MtpDebug.h"
Mike Lockwoodc5c78532010-07-09 10:45:22 -040032#include "MtpDatabase.h"
Mike Lockwood9df53fae2011-04-21 17:05:55 -070033#include "MtpObjectInfo.h"
Mike Lockwood767c5e42010-06-30 17:00:35 -040034#include "MtpProperty.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040035#include "MtpServer.h"
36#include "MtpStorage.h"
37#include "MtpStringBuffer.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040038
Mike Lockwood9c7fdf52010-07-15 13:36:52 -040039#include <linux/usb/f_mtp.h>
Mike Lockwood56118b52010-05-11 17:16:59 -040040
Mike Lockwood8d3257a2010-05-14 10:10:36 -040041namespace android {
42
Mike Lockwood56118b52010-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,
Mike Lockwoodc89f2222011-04-24 18:40:17 -070053 MTP_OPERATION_GET_THUMB,
Mike Lockwood56118b52010-05-11 17:16:59 -040054 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 Lockwood59e3f0d2010-09-02 14:57:30 -040063 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood828d19d2010-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 Lockwood56118b52010-05-11 17:16:59 -040067// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
68// MTP_OPERATION_MOVE_OBJECT,
69// MTP_OPERATION_COPY_OBJECT,
Mike Lockwood44e82dd2010-11-23 09:08:01 -050070 MTP_OPERATION_GET_PARTIAL_OBJECT,
Mike Lockwood56118b52010-05-11 17:16:59 -040071// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
72 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
Mike Lockwood828d19d2010-08-10 15:20:35 -040073 MTP_OPERATION_GET_OBJECT_PROP_DESC,
Mike Lockwoodd3bfecb2010-09-23 23:04:28 -040074 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
75 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwoode2ad6ec2010-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 Lockwood9a2046f2010-08-03 15:30:09 -040080 MTP_OPERATION_GET_OBJECT_REFERENCES,
81 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood56118b52010-05-11 17:16:59 -040082// MTP_OPERATION_SKIP,
Mike Lockwood9df53fae2011-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 Lockwood56118b52010-05-11 17:16:59 -040089};
90
Mike Lockwoodbe125a52010-07-12 18:54:16 -040091static const MtpEventCode kSupportedEventCodes[] = {
92 MTP_EVENT_OBJECT_ADDED,
93 MTP_EVENT_OBJECT_REMOVED,
Mike Lockwood467ca0d2011-02-18 09:07:14 -050094 MTP_EVENT_STORE_ADDED,
95 MTP_EVENT_STORE_REMOVED,
Mike Lockwoodbe125a52010-07-12 18:54:16 -040096};
97
Mike Lockwood7d40d422011-06-21 08:27:06 -040098MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
Mike Lockwooddad69272010-07-02 15:15:07 -040099 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood56118b52010-05-11 17:16:59 -0400100 : mFD(fd),
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400101 mDatabase(database),
Mike Lockwood7d40d422011-06-21 08:27:06 -0400102 mPtp(ptp),
Mike Lockwooddad69272010-07-02 15:15:07 -0400103 mFileGroup(fileGroup),
104 mFilePermission(filePerm),
105 mDirectoryPermission(directoryPerm),
Mike Lockwood56118b52010-05-11 17:16:59 -0400106 mSessionID(0),
107 mSessionOpen(false),
108 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwoodd815f792010-07-12 08:49:01 -0400109 mSendObjectFormat(0),
Mike Lockwood56118b52010-05-11 17:16:59 -0400110 mSendObjectFileSize(0)
111{
Mike Lockwood56118b52010-05-11 17:16:59 -0400112}
113
114MtpServer::~MtpServer() {
115}
116
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500117void MtpServer::addStorage(MtpStorage* storage) {
118 Mutex::Autolock autoLock(mMutex);
119
120 mStorages.push(storage);
121 sendStoreAdded(storage->getStorageID());
122}
123
124void MtpServer::removeStorage(MtpStorage* storage) {
125 Mutex::Autolock autoLock(mMutex);
126
127 for (int i = 0; i < mStorages.size(); i++) {
128 if (mStorages[i] == storage) {
129 mStorages.removeAt(i);
130 sendStoreRemoved(storage->getStorageID());
131 break;
132 }
133 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400134}
135
136MtpStorage* MtpServer::getStorage(MtpStorageID id) {
Mike Lockwood365e03e2010-12-08 16:08:01 -0800137 if (id == 0)
138 return mStorages[0];
Mike Lockwood56118b52010-05-11 17:16:59 -0400139 for (int i = 0; i < mStorages.size(); i++) {
Mike Lockwood365e03e2010-12-08 16:08:01 -0800140 MtpStorage* storage = mStorages[i];
Mike Lockwood56118b52010-05-11 17:16:59 -0400141 if (storage->getStorageID() == id)
142 return storage;
143 }
144 return NULL;
145}
146
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500147bool MtpServer::hasStorage(MtpStorageID id) {
148 if (id == 0 || id == 0xFFFFFFFF)
149 return mStorages.size() > 0;
150 return (getStorage(id) != NULL);
151}
152
Mike Lockwood56118b52010-05-11 17:16:59 -0400153void MtpServer::run() {
154 int fd = mFD;
155
Mike Lockwood767c5e42010-06-30 17:00:35 -0400156 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400157
158 while (1) {
159 int ret = mRequest.read(fd);
160 if (ret < 0) {
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800161 LOGV("request read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400162 if (errno == ECANCELED) {
163 // return to top of loop and wait for next command
164 continue;
165 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400166 break;
167 }
168 MtpOperationCode operation = mRequest.getOperationCode();
169 MtpTransactionID transaction = mRequest.getTransactionID();
170
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400171 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400172 mRequest.dump();
173
174 // FIXME need to generalize this
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400175 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood828d19d2010-08-10 15:20:35 -0400176 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
177 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
178 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
Mike Lockwood56118b52010-05-11 17:16:59 -0400179 if (dataIn) {
180 int ret = mData.read(fd);
181 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400182 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400183 if (errno == ECANCELED) {
184 // return to top of loop and wait for next command
185 continue;
186 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400187 break;
188 }
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400189 LOGV("received data:");
Mike Lockwood56118b52010-05-11 17:16:59 -0400190 mData.dump();
191 } else {
192 mData.reset();
193 }
194
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400195 if (handleRequest()) {
196 if (!dataIn && mData.hasData()) {
197 mData.setOperationCode(operation);
198 mData.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400199 LOGV("sending data:");
Mike Lockwooddb774312010-10-11 17:31:44 -0400200 mData.dump();
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400201 ret = mData.write(fd);
202 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400203 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400204 if (errno == ECANCELED) {
205 // return to top of loop and wait for next command
206 continue;
207 }
208 break;
209 }
210 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400211
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400212 mResponse.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400213 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400214 ret = mResponse.write(fd);
Mike Lockwooddb774312010-10-11 17:31:44 -0400215 mResponse.dump();
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 }
Mike Lockwood2837eef2010-08-31 16:25:12 -0400228
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700229 // commit any open edits
230 int count = mObjectEditList.size();
231 for (int i = 0; i < count; i++) {
232 ObjectEdit* edit = mObjectEditList[i];
233 commitEdit(edit);
234 delete edit;
235 }
236 mObjectEditList.clear();
237
Mike Lockwood2837eef2010-08-31 16:25:12 -0400238 if (mSessionOpen)
239 mDatabase->sessionEnded();
Mike Lockwood56118b52010-05-11 17:16:59 -0400240}
241
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400242void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500243 LOGV("sendObjectAdded %d\n", handle);
244 sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400245}
246
247void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500248 LOGV("sendObjectRemoved %d\n", handle);
249 sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
250}
251
252void MtpServer::sendStoreAdded(MtpStorageID id) {
253 LOGV("sendStoreAdded %08X\n", id);
254 sendEvent(MTP_EVENT_STORE_ADDED, id);
255}
256
257void MtpServer::sendStoreRemoved(MtpStorageID id) {
258 LOGV("sendStoreRemoved %08X\n", id);
259 sendEvent(MTP_EVENT_STORE_REMOVED, id);
260}
261
262void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
Mike Lockwooddc453d42010-07-19 14:29:58 -0400263 if (mSessionOpen) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500264 mEvent.setEventCode(code);
Mike Lockwooddc453d42010-07-19 14:29:58 -0400265 mEvent.setTransactionID(mRequest.getTransactionID());
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500266 mEvent.setParameter(1, param1);
Mike Lockwooddc453d42010-07-19 14:29:58 -0400267 int ret = mEvent.write(mFD);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800268 LOGV("mEvent.write returned %d\n", ret);
Mike Lockwooddc453d42010-07-19 14:29:58 -0400269 }
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400270}
271
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700272void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
273 uint64_t size, MtpObjectFormat format, int fd) {
Mike Lockwood10031992011-04-25 12:56:21 -0700274 ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700275 mObjectEditList.add(edit);
276}
277
278MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
279 int count = mObjectEditList.size();
280 for (int i = 0; i < count; i++) {
281 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwood10031992011-04-25 12:56:21 -0700282 if (edit->mHandle == handle) return edit;
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700283 }
284 return NULL;
285}
286
287void MtpServer::removeEditObject(MtpObjectHandle handle) {
288 int count = mObjectEditList.size();
289 for (int i = 0; i < count; i++) {
290 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwood10031992011-04-25 12:56:21 -0700291 if (edit->mHandle == handle) {
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700292 delete edit;
293 mObjectEditList.removeAt(i);
294 return;
295 }
296 }
297 LOGE("ObjectEdit not found in removeEditObject");
298}
299
300void MtpServer::commitEdit(ObjectEdit* edit) {
Mike Lockwood10031992011-04-25 12:56:21 -0700301 mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700302}
303
304
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400305bool MtpServer::handleRequest() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500306 Mutex::Autolock autoLock(mMutex);
307
Mike Lockwood56118b52010-05-11 17:16:59 -0400308 MtpOperationCode operation = mRequest.getOperationCode();
309 MtpResponseCode response;
310
311 mResponse.reset();
312
313 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
314 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400315 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood56118b52010-05-11 17:16:59 -0400316 mSendObjectHandle = kInvalidObjectHandle;
317 }
318
319 switch (operation) {
320 case MTP_OPERATION_GET_DEVICE_INFO:
321 response = doGetDeviceInfo();
322 break;
323 case MTP_OPERATION_OPEN_SESSION:
324 response = doOpenSession();
325 break;
326 case MTP_OPERATION_CLOSE_SESSION:
327 response = doCloseSession();
328 break;
329 case MTP_OPERATION_GET_STORAGE_IDS:
330 response = doGetStorageIDs();
331 break;
332 case MTP_OPERATION_GET_STORAGE_INFO:
333 response = doGetStorageInfo();
334 break;
335 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
336 response = doGetObjectPropsSupported();
337 break;
338 case MTP_OPERATION_GET_OBJECT_HANDLES:
339 response = doGetObjectHandles();
340 break;
Mike Lockwood7a047c82010-08-02 10:52:20 -0400341 case MTP_OPERATION_GET_NUM_OBJECTS:
342 response = doGetNumObjects();
343 break;
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400344 case MTP_OPERATION_GET_OBJECT_REFERENCES:
345 response = doGetObjectReferences();
346 break;
347 case MTP_OPERATION_SET_OBJECT_REFERENCES:
348 response = doSetObjectReferences();
349 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400350 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
351 response = doGetObjectPropValue();
352 break;
Mike Lockwood828d19d2010-08-10 15:20:35 -0400353 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
354 response = doSetObjectPropValue();
355 break;
356 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
357 response = doGetDevicePropValue();
358 break;
359 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
360 response = doSetDevicePropValue();
361 break;
362 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
363 response = doResetDevicePropValue();
364 break;
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400365 case MTP_OPERATION_GET_OBJECT_PROP_LIST:
366 response = doGetObjectPropList();
367 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400368 case MTP_OPERATION_GET_OBJECT_INFO:
369 response = doGetObjectInfo();
370 break;
371 case MTP_OPERATION_GET_OBJECT:
372 response = doGetObject();
373 break;
Mike Lockwoodc89f2222011-04-24 18:40:17 -0700374 case MTP_OPERATION_GET_THUMB:
375 response = doGetThumb();
376 break;
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500377 case MTP_OPERATION_GET_PARTIAL_OBJECT:
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700378 case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
379 response = doGetPartialObject(operation);
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500380 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400381 case MTP_OPERATION_SEND_OBJECT_INFO:
382 response = doSendObjectInfo();
383 break;
384 case MTP_OPERATION_SEND_OBJECT:
385 response = doSendObject();
386 break;
387 case MTP_OPERATION_DELETE_OBJECT:
388 response = doDeleteObject();
389 break;
390 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood767c5e42010-06-30 17:00:35 -0400391 response = doGetObjectPropDesc();
392 break;
Mike Lockwood59e3f0d2010-09-02 14:57:30 -0400393 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
394 response = doGetDevicePropDesc();
395 break;
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700396 case MTP_OPERATION_SEND_PARTIAL_OBJECT:
397 response = doSendPartialObject();
398 break;
399 case MTP_OPERATION_TRUNCATE_OBJECT:
400 response = doTruncateObject();
401 break;
402 case MTP_OPERATION_BEGIN_EDIT_OBJECT:
403 response = doBeginEditObject();
404 break;
405 case MTP_OPERATION_END_EDIT_OBJECT:
406 response = doEndEditObject();
407 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400408 default:
Mike Lockwood9f679242010-09-23 22:32:05 -0400409 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400410 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
411 break;
412 }
413
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400414 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
415 return false;
Mike Lockwood56118b52010-05-11 17:16:59 -0400416 mResponse.setResponseCode(response);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400417 return true;
Mike Lockwood56118b52010-05-11 17:16:59 -0400418}
419
420MtpResponseCode MtpServer::doGetDeviceInfo() {
421 MtpStringBuffer string;
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700422 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood56118b52010-05-11 17:16:59 -0400423
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400424 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
425 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
426 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
427
Mike Lockwood56118b52010-05-11 17:16:59 -0400428 // fill in device info
429 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood7d40d422011-06-21 08:27:06 -0400430 if (mPtp) {
431 mData.putUInt32(0);
432 } else {
433 // MTP Vendor Extension ID
434 mData.putUInt32(6);
435 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400436 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood7d40d422011-06-21 08:27:06 -0400437 if (mPtp) {
438 // no extensions
439 string.set("");
440 } else {
441 // MTP extensions
442 string.set("microsoft.com: 1.0; android.com: 1.0;");
443 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400444 mData.putString(string); // MTP Extensions
445 mData.putUInt16(0); //Functional Mode
446 mData.putAUInt16(kSupportedOperationCodes,
447 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400448 mData.putAUInt16(kSupportedEventCodes,
449 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400450 mData.putAUInt16(deviceProperties); // Device Properties Supported
451 mData.putAUInt16(captureFormats); // Capture Formats
452 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood46395322011-01-31 16:44:44 -0500453
454 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
455 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400456 mData.putString(string); // Manufacturer
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700457
458 property_get("ro.product.model", prop_value, "MTP Device");
459 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400460 mData.putString(string); // Model
461 string.set("1.0");
462 mData.putString(string); // Device Version
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700463
464 property_get("ro.serialno", prop_value, "????????");
465 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400466 mData.putString(string); // Serial Number
467
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400468 delete playbackFormats;
469 delete captureFormats;
470 delete deviceProperties;
471
Mike Lockwood56118b52010-05-11 17:16:59 -0400472 return MTP_RESPONSE_OK;
473}
474
475MtpResponseCode MtpServer::doOpenSession() {
476 if (mSessionOpen) {
477 mResponse.setParameter(1, mSessionID);
478 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
479 }
480 mSessionID = mRequest.getParameter(1);
481 mSessionOpen = true;
Mike Lockwood2837eef2010-08-31 16:25:12 -0400482
483 mDatabase->sessionStarted();
484
Mike Lockwood56118b52010-05-11 17:16:59 -0400485 return MTP_RESPONSE_OK;
486}
487
488MtpResponseCode MtpServer::doCloseSession() {
489 if (!mSessionOpen)
490 return MTP_RESPONSE_SESSION_NOT_OPEN;
491 mSessionID = 0;
492 mSessionOpen = false;
Mike Lockwood2837eef2010-08-31 16:25:12 -0400493 mDatabase->sessionEnded();
Mike Lockwood56118b52010-05-11 17:16:59 -0400494 return MTP_RESPONSE_OK;
495}
496
497MtpResponseCode MtpServer::doGetStorageIDs() {
498 if (!mSessionOpen)
499 return MTP_RESPONSE_SESSION_NOT_OPEN;
500
501 int count = mStorages.size();
502 mData.putUInt32(count);
503 for (int i = 0; i < count; i++)
504 mData.putUInt32(mStorages[i]->getStorageID());
505
506 return MTP_RESPONSE_OK;
507}
508
509MtpResponseCode MtpServer::doGetStorageInfo() {
510 MtpStringBuffer string;
511
512 if (!mSessionOpen)
513 return MTP_RESPONSE_SESSION_NOT_OPEN;
514 MtpStorageID id = mRequest.getParameter(1);
515 MtpStorage* storage = getStorage(id);
516 if (!storage)
517 return MTP_RESPONSE_INVALID_STORAGE_ID;
518
519 mData.putUInt16(storage->getType());
520 mData.putUInt16(storage->getFileSystemType());
521 mData.putUInt16(storage->getAccessCapability());
522 mData.putUInt64(storage->getMaxCapacity());
523 mData.putUInt64(storage->getFreeSpace());
524 mData.putUInt32(1024*1024*1024); // Free Space in Objects
525 string.set(storage->getDescription());
526 mData.putString(string);
527 mData.putEmptyString(); // Volume Identifier
528
529 return MTP_RESPONSE_OK;
530}
531
532MtpResponseCode MtpServer::doGetObjectPropsSupported() {
533 if (!mSessionOpen)
534 return MTP_RESPONSE_SESSION_NOT_OPEN;
535 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood4cb956c2010-12-07 10:51:20 -0800536 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400537 mData.putAUInt16(properties);
Mike Lockwood33ea5a42010-08-10 15:11:32 -0400538 delete properties;
Mike Lockwood56118b52010-05-11 17:16:59 -0400539 return MTP_RESPONSE_OK;
540}
541
542MtpResponseCode MtpServer::doGetObjectHandles() {
543 if (!mSessionOpen)
544 return MTP_RESPONSE_SESSION_NOT_OPEN;
545 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwood37433652010-05-19 15:12:14 -0400546 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood56118b52010-05-11 17:16:59 -0400547 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
Mike Lockwood6acc90f2011-06-17 13:44:24 -0400548 // 0x00000000 for all objects
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500549
550 if (!hasStorage(storageID))
551 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood56118b52010-05-11 17:16:59 -0400552
553 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
554 mData.putAUInt32(handles);
555 delete handles;
556 return MTP_RESPONSE_OK;
557}
558
Mike Lockwood7a047c82010-08-02 10:52:20 -0400559MtpResponseCode MtpServer::doGetNumObjects() {
560 if (!mSessionOpen)
561 return MTP_RESPONSE_SESSION_NOT_OPEN;
562 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
563 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
564 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
Mike Lockwood6acc90f2011-06-17 13:44:24 -0400565 // 0x00000000 for all objects
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500566 if (!hasStorage(storageID))
567 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood7a047c82010-08-02 10:52:20 -0400568
569 int count = mDatabase->getNumObjects(storageID, format, parent);
570 if (count >= 0) {
571 mResponse.setParameter(1, count);
572 return MTP_RESPONSE_OK;
573 } else {
574 mResponse.setParameter(1, 0);
575 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
576 }
577}
578
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400579MtpResponseCode MtpServer::doGetObjectReferences() {
580 if (!mSessionOpen)
581 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500582 if (!hasStorage())
583 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
584 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400585
586 // FIXME - check for invalid object handle
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400587 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400588 if (handles) {
589 mData.putAUInt32(handles);
590 delete handles;
591 } else {
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400592 mData.putEmptyArray();
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400593 }
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400594 return MTP_RESPONSE_OK;
595}
596
597MtpResponseCode MtpServer::doSetObjectReferences() {
598 if (!mSessionOpen)
599 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500600 if (!hasStorage())
601 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400602 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500603
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400604 MtpObjectHandleList* references = mData.getAUInt32();
605 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
606 delete references;
607 return result;
608}
609
Mike Lockwood56118b52010-05-11 17:16:59 -0400610MtpResponseCode MtpServer::doGetObjectPropValue() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500611 if (!hasStorage())
612 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -0400613 MtpObjectHandle handle = mRequest.getParameter(1);
614 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800615 LOGV("GetObjectPropValue %d %s\n", handle,
Mike Lockwood828d19d2010-08-10 15:20:35 -0400616 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood56118b52010-05-11 17:16:59 -0400617
Mike Lockwood828d19d2010-08-10 15:20:35 -0400618 return mDatabase->getObjectPropertyValue(handle, property, mData);
619}
620
621MtpResponseCode MtpServer::doSetObjectPropValue() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500622 if (!hasStorage())
623 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood828d19d2010-08-10 15:20:35 -0400624 MtpObjectHandle handle = mRequest.getParameter(1);
625 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800626 LOGV("SetObjectPropValue %d %s\n", handle,
Mike Lockwood828d19d2010-08-10 15:20:35 -0400627 MtpDebug::getObjectPropCodeName(property));
628
629 return mDatabase->setObjectPropertyValue(handle, property, mData);
630}
631
632MtpResponseCode MtpServer::doGetDevicePropValue() {
633 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800634 LOGV("GetDevicePropValue %s\n",
Mike Lockwood828d19d2010-08-10 15:20:35 -0400635 MtpDebug::getDevicePropCodeName(property));
636
637 return mDatabase->getDevicePropertyValue(property, mData);
638}
639
640MtpResponseCode MtpServer::doSetDevicePropValue() {
641 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800642 LOGV("SetDevicePropValue %s\n",
Mike Lockwood828d19d2010-08-10 15:20:35 -0400643 MtpDebug::getDevicePropCodeName(property));
644
645 return mDatabase->setDevicePropertyValue(property, mData);
646}
647
648MtpResponseCode MtpServer::doResetDevicePropValue() {
649 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800650 LOGV("ResetDevicePropValue %s\n",
Mike Lockwood828d19d2010-08-10 15:20:35 -0400651 MtpDebug::getDevicePropCodeName(property));
652
653 return mDatabase->resetDeviceProperty(property);
Mike Lockwood56118b52010-05-11 17:16:59 -0400654}
655
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400656MtpResponseCode MtpServer::doGetObjectPropList() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500657 if (!hasStorage())
658 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400659
660 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d7fb632010-12-01 18:46:23 -0500661 // use uint32_t so we can support 0xFFFFFFFF
662 uint32_t format = mRequest.getParameter(2);
663 uint32_t property = mRequest.getParameter(3);
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400664 int groupCode = mRequest.getParameter(4);
Mike Lockwoode3634c32010-11-23 18:45:25 -0500665 int depth = mRequest.getParameter(5);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800666 LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400667 handle, MtpDebug::getFormatCodeName(format),
668 MtpDebug::getObjectPropCodeName(property), groupCode, depth);
669
670 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
671}
672
Mike Lockwood56118b52010-05-11 17:16:59 -0400673MtpResponseCode MtpServer::doGetObjectInfo() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500674 if (!hasStorage())
675 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -0400676 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700677 MtpObjectInfo info(handle);
678 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
679 if (result == MTP_RESPONSE_OK) {
680 char date[20];
681
682 mData.putUInt32(info.mStorageID);
683 mData.putUInt16(info.mFormat);
684 mData.putUInt16(info.mProtectionStatus);
685
686 // if object is being edited the database size may be out of date
687 uint32_t size = info.mCompressedSize;
688 ObjectEdit* edit = getEditObject(handle);
689 if (edit)
Mike Lockwood10031992011-04-25 12:56:21 -0700690 size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700691 mData.putUInt32(size);
692
693 mData.putUInt16(info.mThumbFormat);
694 mData.putUInt32(info.mThumbCompressedSize);
695 mData.putUInt32(info.mThumbPixWidth);
696 mData.putUInt32(info.mThumbPixHeight);
697 mData.putUInt32(info.mImagePixWidth);
698 mData.putUInt32(info.mImagePixHeight);
699 mData.putUInt32(info.mImagePixDepth);
700 mData.putUInt32(info.mParent);
701 mData.putUInt16(info.mAssociationType);
702 mData.putUInt32(info.mAssociationDesc);
703 mData.putUInt32(info.mSequenceNumber);
704 mData.putString(info.mName);
705 mData.putEmptyString(); // date created
706 formatDateTime(info.mDateModified, date, sizeof(date));
707 mData.putString(date); // date modified
708 mData.putEmptyString(); // keywords
709 }
710 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400711}
712
713MtpResponseCode MtpServer::doGetObject() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500714 if (!hasStorage())
715 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -0400716 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400717 MtpString pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400718 int64_t fileLength;
Mike Lockwood365e03e2010-12-08 16:08:01 -0800719 MtpObjectFormat format;
720 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood59c777a2010-08-02 10:37:41 -0400721 if (result != MTP_RESPONSE_OK)
722 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400723
Mike Lockwood59c777a2010-08-02 10:37:41 -0400724 const char* filePath = (const char *)pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400725 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400726 mfr.fd = open(filePath, O_RDONLY);
727 if (mfr.fd < 0) {
728 return MTP_RESPONSE_GENERAL_ERROR;
729 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400730 mfr.offset = 0;
731 mfr.length = fileLength;
732
733 // send data header
734 mData.setOperationCode(mRequest.getOperationCode());
735 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwooddb774312010-10-11 17:31:44 -0400736 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
Mike Lockwood56118b52010-05-11 17:16:59 -0400737
738 // then transfer the file
739 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400740 close(mfr.fd);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400741 if (ret < 0) {
742 if (errno == ECANCELED)
743 return MTP_RESPONSE_TRANSACTION_CANCELLED;
744 else
745 return MTP_RESPONSE_GENERAL_ERROR;
746 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400747 return MTP_RESPONSE_OK;
748}
749
Mike Lockwoodc89f2222011-04-24 18:40:17 -0700750MtpResponseCode MtpServer::doGetThumb() {
751 MtpObjectHandle handle = mRequest.getParameter(1);
752 size_t thumbSize;
753 void* thumb = mDatabase->getThumbnail(handle, thumbSize);
754 if (thumb) {
755 // send data
756 mData.setOperationCode(mRequest.getOperationCode());
757 mData.setTransactionID(mRequest.getTransactionID());
758 mData.writeData(mFD, thumb, thumbSize);
759 free(thumb);
760 return MTP_RESPONSE_OK;
761 } else {
762 return MTP_RESPONSE_GENERAL_ERROR;
763 }
764}
765
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700766MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500767 if (!hasStorage())
768 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500769 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700770 uint64_t offset;
771 uint32_t length;
772 offset = mRequest.getParameter(2);
773 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
774 // android extension with 64 bit offset
775 uint64_t offset2 = mRequest.getParameter(3);
776 offset = offset | (offset2 << 32);
777 length = mRequest.getParameter(4);
778 } else {
779 // standard GetPartialObject
780 length = mRequest.getParameter(3);
781 }
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500782 MtpString pathBuf;
783 int64_t fileLength;
Mike Lockwood365e03e2010-12-08 16:08:01 -0800784 MtpObjectFormat format;
785 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500786 if (result != MTP_RESPONSE_OK)
787 return result;
788 if (offset + length > fileLength)
789 length = fileLength - offset;
790
791 const char* filePath = (const char *)pathBuf;
792 mtp_file_range mfr;
793 mfr.fd = open(filePath, O_RDONLY);
794 if (mfr.fd < 0) {
795 return MTP_RESPONSE_GENERAL_ERROR;
796 }
797 mfr.offset = offset;
798 mfr.length = length;
799 mResponse.setParameter(1, length);
800
801 // send data header
802 mData.setOperationCode(mRequest.getOperationCode());
803 mData.setTransactionID(mRequest.getTransactionID());
804 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
805
806 // then transfer the file
807 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
808 close(mfr.fd);
809 if (ret < 0) {
810 if (errno == ECANCELED)
811 return MTP_RESPONSE_TRANSACTION_CANCELLED;
812 else
813 return MTP_RESPONSE_GENERAL_ERROR;
814 }
815 return MTP_RESPONSE_OK;
816}
817
Mike Lockwood56118b52010-05-11 17:16:59 -0400818MtpResponseCode MtpServer::doSendObjectInfo() {
819 MtpString path;
820 MtpStorageID storageID = mRequest.getParameter(1);
821 MtpStorage* storage = getStorage(storageID);
822 MtpObjectHandle parent = mRequest.getParameter(2);
823 if (!storage)
824 return MTP_RESPONSE_INVALID_STORAGE_ID;
825
826 // special case the root
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400827 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400828 path = storage->getPath();
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400829 parent = 0;
830 } else {
Mike Lockwood365e03e2010-12-08 16:08:01 -0800831 int64_t length;
832 MtpObjectFormat format;
833 int result = mDatabase->getObjectFilePath(parent, path, length, format);
Mike Lockwood59c777a2010-08-02 10:37:41 -0400834 if (result != MTP_RESPONSE_OK)
835 return result;
Mike Lockwood365e03e2010-12-08 16:08:01 -0800836 if (format != MTP_FORMAT_ASSOCIATION)
837 return MTP_RESPONSE_INVALID_PARENT_OBJECT;
Mike Lockwood56118b52010-05-11 17:16:59 -0400838 }
839
840 // read only the fields we need
841 mData.getUInt32(); // storage ID
842 MtpObjectFormat format = mData.getUInt16();
843 mData.getUInt16(); // protection status
844 mSendObjectFileSize = mData.getUInt32();
845 mData.getUInt16(); // thumb format
846 mData.getUInt32(); // thumb compressed size
847 mData.getUInt32(); // thumb pix width
848 mData.getUInt32(); // thumb pix height
849 mData.getUInt32(); // image pix width
850 mData.getUInt32(); // image pix height
851 mData.getUInt32(); // image bit depth
852 mData.getUInt32(); // parent
853 uint16_t associationType = mData.getUInt16();
854 uint32_t associationDesc = mData.getUInt32(); // association desc
855 mData.getUInt32(); // sequence number
856 MtpStringBuffer name, created, modified;
857 mData.getString(name); // file name
858 mData.getString(created); // date created
859 mData.getString(modified); // date modified
860 // keywords follow
861
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800862 LOGV("name: %s format: %04X\n", (const char *)name, format);
Mike Lockwoodd0782672010-05-14 15:35:17 -0400863 time_t modifiedTime;
Mike Lockwood56118b52010-05-11 17:16:59 -0400864 if (!parseDateTime(modified, modifiedTime))
865 modifiedTime = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400866
867 if (path[path.size() - 1] != '/')
868 path += "/";
869 path += (const char *)name;
870
Mike Lockwood7f36b192010-12-12 12:17:43 -0800871 // check space first
872 if (mSendObjectFileSize > storage->getFreeSpace())
873 return MTP_RESPONSE_STORAGE_FULL;
874
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500875LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
Mike Lockwoodd815f792010-07-12 08:49:01 -0400876 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
877 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodd0782672010-05-14 15:35:17 -0400878 if (handle == kInvalidObjectHandle) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400879 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd0782672010-05-14 15:35:17 -0400880 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400881
882 if (format == MTP_FORMAT_ASSOCIATION) {
883 mode_t mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400884 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood56118b52010-05-11 17:16:59 -0400885 umask(mask);
886 if (ret && ret != -EEXIST)
887 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooddad69272010-07-02 15:15:07 -0400888 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood7a0bd172011-01-18 11:06:19 -0800889
890 // SendObject does not get sent for directories, so call endSendObject here instead
891 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
Mike Lockwood56118b52010-05-11 17:16:59 -0400892 } else {
893 mSendObjectFilePath = path;
894 // save the handle for the SendObject call, which should follow
895 mSendObjectHandle = handle;
Mike Lockwoodd815f792010-07-12 08:49:01 -0400896 mSendObjectFormat = format;
Mike Lockwood56118b52010-05-11 17:16:59 -0400897 }
898
899 mResponse.setParameter(1, storageID);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400900 mResponse.setParameter(2, parent);
Mike Lockwood56118b52010-05-11 17:16:59 -0400901 mResponse.setParameter(3, handle);
902
903 return MTP_RESPONSE_OK;
904}
905
906MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500907 if (!hasStorage())
908 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd815f792010-07-12 08:49:01 -0400909 MtpResponseCode result = MTP_RESPONSE_OK;
910 mode_t mask;
911 int ret;
912
Mike Lockwood56118b52010-05-11 17:16:59 -0400913 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400914 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwoodd815f792010-07-12 08:49:01 -0400915 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
916 goto done;
Mike Lockwood56118b52010-05-11 17:16:59 -0400917 }
918
919 // read the header
Mike Lockwoodd815f792010-07-12 08:49:01 -0400920 ret = mData.readDataHeader(mFD);
Mike Lockwood56118b52010-05-11 17:16:59 -0400921 // FIXME - check for errors here.
922
923 // reset so we don't attempt to send this back
924 mData.reset();
925
926 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400927 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
928 if (mfr.fd < 0) {
Mike Lockwoodd815f792010-07-12 08:49:01 -0400929 result = MTP_RESPONSE_GENERAL_ERROR;
930 goto done;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400931 }
Mike Lockwooddad69272010-07-02 15:15:07 -0400932 fchown(mfr.fd, getuid(), mFileGroup);
933 // set permissions
Mike Lockwoodd815f792010-07-12 08:49:01 -0400934 mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400935 fchmod(mfr.fd, mFilePermission);
936 umask(mask);
937
Mike Lockwood56118b52010-05-11 17:16:59 -0400938 mfr.offset = 0;
939 mfr.length = mSendObjectFileSize;
940
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800941 LOGV("receiving %s\n", (const char *)mSendObjectFilePath);
Mike Lockwood56118b52010-05-11 17:16:59 -0400942 // transfer the file
943 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400944 close(mfr.fd);
Mike Lockwooddad69272010-07-02 15:15:07 -0400945
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400946 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood56118b52010-05-11 17:16:59 -0400947
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400948 if (ret < 0) {
949 unlink(mSendObjectFilePath);
950 if (errno == ECANCELED)
Mike Lockwoodd815f792010-07-12 08:49:01 -0400951 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400952 else
Mike Lockwoodd815f792010-07-12 08:49:01 -0400953 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400954 }
Mike Lockwoodd815f792010-07-12 08:49:01 -0400955
956done:
957 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
Mike Lockwood7a0bd172011-01-18 11:06:19 -0800958 result == MTP_RESPONSE_OK);
Mike Lockwoodd815f792010-07-12 08:49:01 -0400959 mSendObjectHandle = kInvalidObjectHandle;
960 mSendObjectFormat = 0;
961 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400962}
963
Mike Lockwoodccb6e962010-09-13 17:15:58 -0400964static void deleteRecursive(const char* path) {
965 char pathbuf[PATH_MAX];
966 int pathLength = strlen(path);
967 if (pathLength >= sizeof(pathbuf) - 1) {
968 LOGE("path too long: %s\n", path);
969 }
970 strcpy(pathbuf, path);
971 if (pathbuf[pathLength - 1] != '/') {
972 pathbuf[pathLength++] = '/';
973 }
974 char* fileSpot = pathbuf + pathLength;
975 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
976
977 DIR* dir = opendir(path);
978 if (!dir) {
979 LOGE("opendir %s failed: %s", path, strerror(errno));
980 return;
981 }
982
983 struct dirent* entry;
984 while ((entry = readdir(dir))) {
985 const char* name = entry->d_name;
986
987 // ignore "." and ".."
988 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
989 continue;
990 }
991
992 int nameLength = strlen(name);
993 if (nameLength > pathRemaining) {
994 LOGE("path %s/%s too long\n", path, name);
995 continue;
996 }
997 strcpy(fileSpot, name);
998
999 int type = entry->d_type;
1000 if (entry->d_type == DT_DIR) {
1001 deleteRecursive(pathbuf);
1002 rmdir(pathbuf);
1003 } else {
1004 unlink(pathbuf);
1005 }
1006 }
Mike Lockwooda108b2f2010-11-11 11:22:32 -05001007 closedir(dir);
Mike Lockwoodccb6e962010-09-13 17:15:58 -04001008}
1009
1010static void deletePath(const char* path) {
1011 struct stat statbuf;
1012 if (stat(path, &statbuf) == 0) {
1013 if (S_ISDIR(statbuf.st_mode)) {
1014 deleteRecursive(path);
1015 rmdir(path);
1016 } else {
1017 unlink(path);
1018 }
1019 } else {
1020 LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
1021 }
1022}
1023
Mike Lockwood56118b52010-05-11 17:16:59 -04001024MtpResponseCode MtpServer::doDeleteObject() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -05001025 if (!hasStorage())
1026 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -04001027 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodccb6e962010-09-13 17:15:58 -04001028 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood56118b52010-05-11 17:16:59 -04001029 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1030 // FIXME - implement deleting objects by format
Mike Lockwood56118b52010-05-11 17:16:59 -04001031
1032 MtpString filePath;
1033 int64_t fileLength;
Mike Lockwood365e03e2010-12-08 16:08:01 -08001034 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
Mike Lockwood59c777a2010-08-02 10:37:41 -04001035 if (result == MTP_RESPONSE_OK) {
1036 LOGV("deleting %s", (const char *)filePath);
Mike Lockwoodccb6e962010-09-13 17:15:58 -04001037 deletePath((const char *)filePath);
Mike Lockwood59c777a2010-08-02 10:37:41 -04001038 return mDatabase->deleteFile(handle);
1039 } else {
1040 return result;
1041 }
Mike Lockwood56118b52010-05-11 17:16:59 -04001042}
1043
1044MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood767c5e42010-06-30 17:00:35 -04001045 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood56118b52010-05-11 17:16:59 -04001046 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwoodf26a5862011-01-21 21:00:54 -08001047 LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
Mike Lockwood828d19d2010-08-10 15:20:35 -04001048 MtpDebug::getFormatCodeName(format));
1049 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood767c5e42010-06-30 17:00:35 -04001050 if (!property)
1051 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood767c5e42010-06-30 17:00:35 -04001052 property->write(mData);
Mike Lockwood828d19d2010-08-10 15:20:35 -04001053 delete property;
1054 return MTP_RESPONSE_OK;
1055}
1056
1057MtpResponseCode MtpServer::doGetDevicePropDesc() {
1058 MtpDeviceProperty propCode = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -08001059 LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
Mike Lockwood828d19d2010-08-10 15:20:35 -04001060 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1061 if (!property)
1062 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1063 property->write(mData);
1064 delete property;
Mike Lockwood767c5e42010-06-30 17:00:35 -04001065 return MTP_RESPONSE_OK;
Mike Lockwood56118b52010-05-11 17:16:59 -04001066}
Mike Lockwood8d3257a2010-05-14 10:10:36 -04001067
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001068MtpResponseCode MtpServer::doSendPartialObject() {
1069 if (!hasStorage())
1070 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1071 MtpObjectHandle handle = mRequest.getParameter(1);
1072 uint64_t offset = mRequest.getParameter(2);
1073 uint64_t offset2 = mRequest.getParameter(3);
1074 offset = offset | (offset2 << 32);
1075 uint32_t length = mRequest.getParameter(4);
1076
1077 ObjectEdit* edit = getEditObject(handle);
1078 if (!edit) {
1079 LOGE("object not open for edit in doSendPartialObject");
1080 return MTP_RESPONSE_GENERAL_ERROR;
1081 }
1082
1083 // can't start writing past the end of the file
Mike Lockwood10031992011-04-25 12:56:21 -07001084 if (offset > edit->mSize) {
1085 LOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001086 return MTP_RESPONSE_GENERAL_ERROR;
1087 }
1088
1089 // read the header
1090 int ret = mData.readDataHeader(mFD);
1091 // FIXME - check for errors here.
1092
1093 // reset so we don't attempt to send this back
1094 mData.reset();
1095
Mike Lockwood10031992011-04-25 12:56:21 -07001096 const char* filePath = (const char *)edit->mPath;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001097 LOGV("receiving partial %s %lld %ld\n", filePath, offset, length);
1098 mtp_file_range mfr;
Mike Lockwood10031992011-04-25 12:56:21 -07001099 mfr.fd = edit->mFD;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001100 mfr.offset = offset;
1101 mfr.length = length;
1102
1103 // transfer the file
1104 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1105 LOGV("MTP_RECEIVE_FILE returned %d", ret);
1106 if (ret < 0) {
1107 mResponse.setParameter(1, 0);
1108 if (errno == ECANCELED)
1109 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1110 else
1111 return MTP_RESPONSE_GENERAL_ERROR;
1112 }
1113 mResponse.setParameter(1, length);
1114 uint64_t end = offset + length;
Mike Lockwood10031992011-04-25 12:56:21 -07001115 if (end > edit->mSize) {
1116 edit->mSize = end;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001117 }
1118 return MTP_RESPONSE_OK;
1119}
1120
1121MtpResponseCode MtpServer::doTruncateObject() {
1122 MtpObjectHandle handle = mRequest.getParameter(1);
1123 ObjectEdit* edit = getEditObject(handle);
1124 if (!edit) {
1125 LOGE("object not open for edit in doTruncateObject");
1126 return MTP_RESPONSE_GENERAL_ERROR;
1127 }
1128
1129 uint64_t offset = mRequest.getParameter(2);
1130 uint64_t offset2 = mRequest.getParameter(3);
1131 offset |= (offset2 << 32);
Mike Lockwood10031992011-04-25 12:56:21 -07001132 if (ftruncate(edit->mFD, offset) != 0) {
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001133 return MTP_RESPONSE_GENERAL_ERROR;
1134 } else {
Mike Lockwood10031992011-04-25 12:56:21 -07001135 edit->mSize = offset;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001136 return MTP_RESPONSE_OK;
1137 }
1138}
1139
1140MtpResponseCode MtpServer::doBeginEditObject() {
1141 MtpObjectHandle handle = mRequest.getParameter(1);
1142 if (getEditObject(handle)) {
1143 LOGE("object already open for edit in doBeginEditObject");
1144 return MTP_RESPONSE_GENERAL_ERROR;
1145 }
1146
1147 MtpString path;
1148 int64_t fileLength;
1149 MtpObjectFormat format;
1150 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1151 if (result != MTP_RESPONSE_OK)
1152 return result;
1153
1154 int fd = open((const char *)path, O_RDWR | O_EXCL);
1155 if (fd < 0) {
1156 LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1157 return MTP_RESPONSE_GENERAL_ERROR;
1158 }
1159
1160 addEditObject(handle, path, fileLength, format, fd);
1161 return MTP_RESPONSE_OK;
1162}
1163
1164MtpResponseCode MtpServer::doEndEditObject() {
1165 MtpObjectHandle handle = mRequest.getParameter(1);
1166 ObjectEdit* edit = getEditObject(handle);
1167 if (!edit) {
1168 LOGE("object not open for edit in doEndEditObject");
1169 return MTP_RESPONSE_GENERAL_ERROR;
1170 }
1171
1172 commitEdit(edit);
1173 removeEditObject(handle);
1174 return MTP_RESPONSE_OK;
1175}
1176
Mike Lockwood8d3257a2010-05-14 10:10:36 -04001177} // namespace android