blob: b744b5b4c84ac257b7c66c7cba681c67bcba3551 [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,
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 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 Lockwoodd21eac92010-07-03 00:44:05 -040098MtpServer::MtpServer(int fd, MtpDatabase* database,
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 Lockwooddad69272010-07-02 15:15:07 -0400102 mFileGroup(fileGroup),
103 mFilePermission(filePerm),
104 mDirectoryPermission(directoryPerm),
Mike Lockwood56118b52010-05-11 17:16:59 -0400105 mSessionID(0),
106 mSessionOpen(false),
107 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwoodd815f792010-07-12 08:49:01 -0400108 mSendObjectFormat(0),
Mike Lockwood56118b52010-05-11 17:16:59 -0400109 mSendObjectFileSize(0)
110{
Mike Lockwood56118b52010-05-11 17:16:59 -0400111}
112
113MtpServer::~MtpServer() {
114}
115
Mike Lockwood467ca0d2011-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 Lockwood56118b52010-05-11 17:16:59 -0400133}
134
135MtpStorage* MtpServer::getStorage(MtpStorageID id) {
Mike Lockwood365e03e2010-12-08 16:08:01 -0800136 if (id == 0)
137 return mStorages[0];
Mike Lockwood56118b52010-05-11 17:16:59 -0400138 for (int i = 0; i < mStorages.size(); i++) {
Mike Lockwood365e03e2010-12-08 16:08:01 -0800139 MtpStorage* storage = mStorages[i];
Mike Lockwood56118b52010-05-11 17:16:59 -0400140 if (storage->getStorageID() == id)
141 return storage;
142 }
143 return NULL;
144}
145
Mike Lockwood467ca0d2011-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 Lockwood56118b52010-05-11 17:16:59 -0400152void MtpServer::run() {
153 int fd = mFD;
154
Mike Lockwood767c5e42010-06-30 17:00:35 -0400155 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400156
157 while (1) {
158 int ret = mRequest.read(fd);
159 if (ret < 0) {
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800160 LOGV("request read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400161 if (errno == ECANCELED) {
162 // return to top of loop and wait for next command
163 continue;
164 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400165 break;
166 }
167 MtpOperationCode operation = mRequest.getOperationCode();
168 MtpTransactionID transaction = mRequest.getTransactionID();
169
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400170 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400171 mRequest.dump();
172
173 // FIXME need to generalize this
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400174 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood828d19d2010-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 Lockwood56118b52010-05-11 17:16:59 -0400178 if (dataIn) {
179 int ret = mData.read(fd);
180 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400181 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400182 if (errno == ECANCELED) {
183 // return to top of loop and wait for next command
184 continue;
185 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400186 break;
187 }
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400188 LOGV("received data:");
Mike Lockwood56118b52010-05-11 17:16:59 -0400189 mData.dump();
190 } else {
191 mData.reset();
192 }
193
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400194 if (handleRequest()) {
195 if (!dataIn && mData.hasData()) {
196 mData.setOperationCode(operation);
197 mData.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400198 LOGV("sending data:");
Mike Lockwooddb774312010-10-11 17:31:44 -0400199 mData.dump();
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400200 ret = mData.write(fd);
201 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400202 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-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 Lockwood56118b52010-05-11 17:16:59 -0400210
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400211 mResponse.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400212 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400213 ret = mResponse.write(fd);
Mike Lockwooddb774312010-10-11 17:31:44 -0400214 mResponse.dump();
Mike Lockwood56118b52010-05-11 17:16:59 -0400215 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400216 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400217 if (errno == ECANCELED) {
218 // return to top of loop and wait for next command
219 continue;
220 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400221 break;
222 }
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400223 } else {
Mike Lockwood767c5e42010-06-30 17:00:35 -0400224 LOGV("skipping response\n");
Mike Lockwood56118b52010-05-11 17:16:59 -0400225 }
226 }
Mike Lockwood2837eef2010-08-31 16:25:12 -0400227
Mike Lockwood9df53fae2011-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 Lockwood2837eef2010-08-31 16:25:12 -0400237 if (mSessionOpen)
238 mDatabase->sessionEnded();
Mike Lockwood56118b52010-05-11 17:16:59 -0400239}
240
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400241void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500242 LOGV("sendObjectAdded %d\n", handle);
243 sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400244}
245
246void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwood467ca0d2011-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 Lockwooddc453d42010-07-19 14:29:58 -0400262 if (mSessionOpen) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500263 mEvent.setEventCode(code);
Mike Lockwooddc453d42010-07-19 14:29:58 -0400264 mEvent.setTransactionID(mRequest.getTransactionID());
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500265 mEvent.setParameter(1, param1);
Mike Lockwooddc453d42010-07-19 14:29:58 -0400266 int ret = mEvent.write(mFD);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800267 LOGV("mEvent.write returned %d\n", ret);
Mike Lockwooddc453d42010-07-19 14:29:58 -0400268 }
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400269}
270
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700271void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
272 uint64_t size, MtpObjectFormat format, int fd) {
Mike Lockwood10031992011-04-25 12:56:21 -0700273 ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700274 mObjectEditList.add(edit);
275}
276
277MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
278 int count = mObjectEditList.size();
279 for (int i = 0; i < count; i++) {
280 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwood10031992011-04-25 12:56:21 -0700281 if (edit->mHandle == handle) return edit;
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700282 }
283 return NULL;
284}
285
286void MtpServer::removeEditObject(MtpObjectHandle handle) {
287 int count = mObjectEditList.size();
288 for (int i = 0; i < count; i++) {
289 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwood10031992011-04-25 12:56:21 -0700290 if (edit->mHandle == handle) {
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700291 delete edit;
292 mObjectEditList.removeAt(i);
293 return;
294 }
295 }
296 LOGE("ObjectEdit not found in removeEditObject");
297}
298
299void MtpServer::commitEdit(ObjectEdit* edit) {
Mike Lockwood10031992011-04-25 12:56:21 -0700300 mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700301}
302
303
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400304bool MtpServer::handleRequest() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500305 Mutex::Autolock autoLock(mMutex);
306
Mike Lockwood56118b52010-05-11 17:16:59 -0400307 MtpOperationCode operation = mRequest.getOperationCode();
308 MtpResponseCode response;
309
310 mResponse.reset();
311
312 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
313 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400314 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood56118b52010-05-11 17:16:59 -0400315 mSendObjectHandle = kInvalidObjectHandle;
316 }
317
318 switch (operation) {
319 case MTP_OPERATION_GET_DEVICE_INFO:
320 response = doGetDeviceInfo();
321 break;
322 case MTP_OPERATION_OPEN_SESSION:
323 response = doOpenSession();
324 break;
325 case MTP_OPERATION_CLOSE_SESSION:
326 response = doCloseSession();
327 break;
328 case MTP_OPERATION_GET_STORAGE_IDS:
329 response = doGetStorageIDs();
330 break;
331 case MTP_OPERATION_GET_STORAGE_INFO:
332 response = doGetStorageInfo();
333 break;
334 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
335 response = doGetObjectPropsSupported();
336 break;
337 case MTP_OPERATION_GET_OBJECT_HANDLES:
338 response = doGetObjectHandles();
339 break;
Mike Lockwood7a047c82010-08-02 10:52:20 -0400340 case MTP_OPERATION_GET_NUM_OBJECTS:
341 response = doGetNumObjects();
342 break;
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400343 case MTP_OPERATION_GET_OBJECT_REFERENCES:
344 response = doGetObjectReferences();
345 break;
346 case MTP_OPERATION_SET_OBJECT_REFERENCES:
347 response = doSetObjectReferences();
348 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400349 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
350 response = doGetObjectPropValue();
351 break;
Mike Lockwood828d19d2010-08-10 15:20:35 -0400352 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
353 response = doSetObjectPropValue();
354 break;
355 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
356 response = doGetDevicePropValue();
357 break;
358 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
359 response = doSetDevicePropValue();
360 break;
361 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
362 response = doResetDevicePropValue();
363 break;
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400364 case MTP_OPERATION_GET_OBJECT_PROP_LIST:
365 response = doGetObjectPropList();
366 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400367 case MTP_OPERATION_GET_OBJECT_INFO:
368 response = doGetObjectInfo();
369 break;
370 case MTP_OPERATION_GET_OBJECT:
371 response = doGetObject();
372 break;
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500373 case MTP_OPERATION_GET_PARTIAL_OBJECT:
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700374 case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
375 response = doGetPartialObject(operation);
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500376 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400377 case MTP_OPERATION_SEND_OBJECT_INFO:
378 response = doSendObjectInfo();
379 break;
380 case MTP_OPERATION_SEND_OBJECT:
381 response = doSendObject();
382 break;
383 case MTP_OPERATION_DELETE_OBJECT:
384 response = doDeleteObject();
385 break;
386 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood767c5e42010-06-30 17:00:35 -0400387 response = doGetObjectPropDesc();
388 break;
Mike Lockwood59e3f0d2010-09-02 14:57:30 -0400389 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
390 response = doGetDevicePropDesc();
391 break;
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700392 case MTP_OPERATION_SEND_PARTIAL_OBJECT:
393 response = doSendPartialObject();
394 break;
395 case MTP_OPERATION_TRUNCATE_OBJECT:
396 response = doTruncateObject();
397 break;
398 case MTP_OPERATION_BEGIN_EDIT_OBJECT:
399 response = doBeginEditObject();
400 break;
401 case MTP_OPERATION_END_EDIT_OBJECT:
402 response = doEndEditObject();
403 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400404 default:
Mike Lockwood9f679242010-09-23 22:32:05 -0400405 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400406 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
407 break;
408 }
409
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400410 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
411 return false;
Mike Lockwood56118b52010-05-11 17:16:59 -0400412 mResponse.setResponseCode(response);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400413 return true;
Mike Lockwood56118b52010-05-11 17:16:59 -0400414}
415
416MtpResponseCode MtpServer::doGetDeviceInfo() {
417 MtpStringBuffer string;
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700418 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood56118b52010-05-11 17:16:59 -0400419
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400420 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
421 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
422 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
423
Mike Lockwood56118b52010-05-11 17:16:59 -0400424 // fill in device info
425 mData.putUInt16(MTP_STANDARD_VERSION);
426 mData.putUInt32(6); // MTP Vendor Extension ID
427 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700428 string.set("microsoft.com: 1.0; android.com: 1.0;");
Mike Lockwood56118b52010-05-11 17:16:59 -0400429 mData.putString(string); // MTP Extensions
430 mData.putUInt16(0); //Functional Mode
431 mData.putAUInt16(kSupportedOperationCodes,
432 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400433 mData.putAUInt16(kSupportedEventCodes,
434 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400435 mData.putAUInt16(deviceProperties); // Device Properties Supported
436 mData.putAUInt16(captureFormats); // Capture Formats
437 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood46395322011-01-31 16:44:44 -0500438
439 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
440 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400441 mData.putString(string); // Manufacturer
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700442
443 property_get("ro.product.model", prop_value, "MTP Device");
444 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400445 mData.putString(string); // Model
446 string.set("1.0");
447 mData.putString(string); // Device Version
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700448
449 property_get("ro.serialno", prop_value, "????????");
450 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400451 mData.putString(string); // Serial Number
452
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400453 delete playbackFormats;
454 delete captureFormats;
455 delete deviceProperties;
456
Mike Lockwood56118b52010-05-11 17:16:59 -0400457 return MTP_RESPONSE_OK;
458}
459
460MtpResponseCode MtpServer::doOpenSession() {
461 if (mSessionOpen) {
462 mResponse.setParameter(1, mSessionID);
463 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
464 }
465 mSessionID = mRequest.getParameter(1);
466 mSessionOpen = true;
Mike Lockwood2837eef2010-08-31 16:25:12 -0400467
468 mDatabase->sessionStarted();
469
Mike Lockwood56118b52010-05-11 17:16:59 -0400470 return MTP_RESPONSE_OK;
471}
472
473MtpResponseCode MtpServer::doCloseSession() {
474 if (!mSessionOpen)
475 return MTP_RESPONSE_SESSION_NOT_OPEN;
476 mSessionID = 0;
477 mSessionOpen = false;
Mike Lockwood2837eef2010-08-31 16:25:12 -0400478 mDatabase->sessionEnded();
Mike Lockwood56118b52010-05-11 17:16:59 -0400479 return MTP_RESPONSE_OK;
480}
481
482MtpResponseCode MtpServer::doGetStorageIDs() {
483 if (!mSessionOpen)
484 return MTP_RESPONSE_SESSION_NOT_OPEN;
485
486 int count = mStorages.size();
487 mData.putUInt32(count);
488 for (int i = 0; i < count; i++)
489 mData.putUInt32(mStorages[i]->getStorageID());
490
491 return MTP_RESPONSE_OK;
492}
493
494MtpResponseCode MtpServer::doGetStorageInfo() {
495 MtpStringBuffer string;
496
497 if (!mSessionOpen)
498 return MTP_RESPONSE_SESSION_NOT_OPEN;
499 MtpStorageID id = mRequest.getParameter(1);
500 MtpStorage* storage = getStorage(id);
501 if (!storage)
502 return MTP_RESPONSE_INVALID_STORAGE_ID;
503
504 mData.putUInt16(storage->getType());
505 mData.putUInt16(storage->getFileSystemType());
506 mData.putUInt16(storage->getAccessCapability());
507 mData.putUInt64(storage->getMaxCapacity());
508 mData.putUInt64(storage->getFreeSpace());
509 mData.putUInt32(1024*1024*1024); // Free Space in Objects
510 string.set(storage->getDescription());
511 mData.putString(string);
512 mData.putEmptyString(); // Volume Identifier
513
514 return MTP_RESPONSE_OK;
515}
516
517MtpResponseCode MtpServer::doGetObjectPropsSupported() {
518 if (!mSessionOpen)
519 return MTP_RESPONSE_SESSION_NOT_OPEN;
520 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood4cb956c2010-12-07 10:51:20 -0800521 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400522 mData.putAUInt16(properties);
Mike Lockwood33ea5a42010-08-10 15:11:32 -0400523 delete properties;
Mike Lockwood56118b52010-05-11 17:16:59 -0400524 return MTP_RESPONSE_OK;
525}
526
527MtpResponseCode MtpServer::doGetObjectHandles() {
528 if (!mSessionOpen)
529 return MTP_RESPONSE_SESSION_NOT_OPEN;
530 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwood37433652010-05-19 15:12:14 -0400531 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood56118b52010-05-11 17:16:59 -0400532 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
533 // 0x00000000 for all objects?
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500534
535 if (!hasStorage(storageID))
536 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400537 if (parent == 0xFFFFFFFF)
538 parent = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400539
540 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
541 mData.putAUInt32(handles);
542 delete handles;
543 return MTP_RESPONSE_OK;
544}
545
Mike Lockwood7a047c82010-08-02 10:52:20 -0400546MtpResponseCode MtpServer::doGetNumObjects() {
547 if (!mSessionOpen)
548 return MTP_RESPONSE_SESSION_NOT_OPEN;
549 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
550 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
551 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
552 // 0x00000000 for all objects?
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500553 if (!hasStorage(storageID))
554 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood7a047c82010-08-02 10:52:20 -0400555 if (parent == 0xFFFFFFFF)
556 parent = 0;
557
558 int count = mDatabase->getNumObjects(storageID, format, parent);
559 if (count >= 0) {
560 mResponse.setParameter(1, count);
561 return MTP_RESPONSE_OK;
562 } else {
563 mResponse.setParameter(1, 0);
564 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
565 }
566}
567
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400568MtpResponseCode MtpServer::doGetObjectReferences() {
569 if (!mSessionOpen)
570 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500571 if (!hasStorage())
572 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
573 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400574
575 // FIXME - check for invalid object handle
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400576 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400577 if (handles) {
578 mData.putAUInt32(handles);
579 delete handles;
580 } else {
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400581 mData.putEmptyArray();
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400582 }
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400583 return MTP_RESPONSE_OK;
584}
585
586MtpResponseCode MtpServer::doSetObjectReferences() {
587 if (!mSessionOpen)
588 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500589 if (!hasStorage())
590 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400591 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500592
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400593 MtpObjectHandleList* references = mData.getAUInt32();
594 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
595 delete references;
596 return result;
597}
598
Mike Lockwood56118b52010-05-11 17:16:59 -0400599MtpResponseCode MtpServer::doGetObjectPropValue() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500600 if (!hasStorage())
601 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -0400602 MtpObjectHandle handle = mRequest.getParameter(1);
603 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800604 LOGV("GetObjectPropValue %d %s\n", handle,
Mike Lockwood828d19d2010-08-10 15:20:35 -0400605 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood56118b52010-05-11 17:16:59 -0400606
Mike Lockwood828d19d2010-08-10 15:20:35 -0400607 return mDatabase->getObjectPropertyValue(handle, property, mData);
608}
609
610MtpResponseCode MtpServer::doSetObjectPropValue() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500611 if (!hasStorage())
612 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood828d19d2010-08-10 15:20:35 -0400613 MtpObjectHandle handle = mRequest.getParameter(1);
614 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800615 LOGV("SetObjectPropValue %d %s\n", handle,
Mike Lockwood828d19d2010-08-10 15:20:35 -0400616 MtpDebug::getObjectPropCodeName(property));
617
618 return mDatabase->setObjectPropertyValue(handle, property, mData);
619}
620
621MtpResponseCode MtpServer::doGetDevicePropValue() {
622 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800623 LOGV("GetDevicePropValue %s\n",
Mike Lockwood828d19d2010-08-10 15:20:35 -0400624 MtpDebug::getDevicePropCodeName(property));
625
626 return mDatabase->getDevicePropertyValue(property, mData);
627}
628
629MtpResponseCode MtpServer::doSetDevicePropValue() {
630 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800631 LOGV("SetDevicePropValue %s\n",
Mike Lockwood828d19d2010-08-10 15:20:35 -0400632 MtpDebug::getDevicePropCodeName(property));
633
634 return mDatabase->setDevicePropertyValue(property, mData);
635}
636
637MtpResponseCode MtpServer::doResetDevicePropValue() {
638 MtpDeviceProperty property = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800639 LOGV("ResetDevicePropValue %s\n",
Mike Lockwood828d19d2010-08-10 15:20:35 -0400640 MtpDebug::getDevicePropCodeName(property));
641
642 return mDatabase->resetDeviceProperty(property);
Mike Lockwood56118b52010-05-11 17:16:59 -0400643}
644
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400645MtpResponseCode MtpServer::doGetObjectPropList() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500646 if (!hasStorage())
647 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400648
649 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d7fb632010-12-01 18:46:23 -0500650 // use uint32_t so we can support 0xFFFFFFFF
651 uint32_t format = mRequest.getParameter(2);
652 uint32_t property = mRequest.getParameter(3);
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400653 int groupCode = mRequest.getParameter(4);
Mike Lockwoode3634c32010-11-23 18:45:25 -0500654 int depth = mRequest.getParameter(5);
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800655 LOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
Mike Lockwoode2ad6ec2010-10-14 18:03:25 -0400656 handle, MtpDebug::getFormatCodeName(format),
657 MtpDebug::getObjectPropCodeName(property), groupCode, depth);
658
659 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
660}
661
Mike Lockwood56118b52010-05-11 17:16:59 -0400662MtpResponseCode MtpServer::doGetObjectInfo() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500663 if (!hasStorage())
664 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -0400665 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700666 MtpObjectInfo info(handle);
667 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
668 if (result == MTP_RESPONSE_OK) {
669 char date[20];
670
671 mData.putUInt32(info.mStorageID);
672 mData.putUInt16(info.mFormat);
673 mData.putUInt16(info.mProtectionStatus);
674
675 // if object is being edited the database size may be out of date
676 uint32_t size = info.mCompressedSize;
677 ObjectEdit* edit = getEditObject(handle);
678 if (edit)
Mike Lockwood10031992011-04-25 12:56:21 -0700679 size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700680 mData.putUInt32(size);
681
682 mData.putUInt16(info.mThumbFormat);
683 mData.putUInt32(info.mThumbCompressedSize);
684 mData.putUInt32(info.mThumbPixWidth);
685 mData.putUInt32(info.mThumbPixHeight);
686 mData.putUInt32(info.mImagePixWidth);
687 mData.putUInt32(info.mImagePixHeight);
688 mData.putUInt32(info.mImagePixDepth);
689 mData.putUInt32(info.mParent);
690 mData.putUInt16(info.mAssociationType);
691 mData.putUInt32(info.mAssociationDesc);
692 mData.putUInt32(info.mSequenceNumber);
693 mData.putString(info.mName);
694 mData.putEmptyString(); // date created
695 formatDateTime(info.mDateModified, date, sizeof(date));
696 mData.putString(date); // date modified
697 mData.putEmptyString(); // keywords
698 }
699 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400700}
701
702MtpResponseCode MtpServer::doGetObject() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500703 if (!hasStorage())
704 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -0400705 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400706 MtpString pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400707 int64_t fileLength;
Mike Lockwood365e03e2010-12-08 16:08:01 -0800708 MtpObjectFormat format;
709 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood59c777a2010-08-02 10:37:41 -0400710 if (result != MTP_RESPONSE_OK)
711 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400712
Mike Lockwood59c777a2010-08-02 10:37:41 -0400713 const char* filePath = (const char *)pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400714 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400715 mfr.fd = open(filePath, O_RDONLY);
716 if (mfr.fd < 0) {
717 return MTP_RESPONSE_GENERAL_ERROR;
718 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400719 mfr.offset = 0;
720 mfr.length = fileLength;
721
722 // send data header
723 mData.setOperationCode(mRequest.getOperationCode());
724 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwooddb774312010-10-11 17:31:44 -0400725 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
Mike Lockwood56118b52010-05-11 17:16:59 -0400726
727 // then transfer the file
728 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400729 close(mfr.fd);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400730 if (ret < 0) {
731 if (errno == ECANCELED)
732 return MTP_RESPONSE_TRANSACTION_CANCELLED;
733 else
734 return MTP_RESPONSE_GENERAL_ERROR;
735 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400736 return MTP_RESPONSE_OK;
737}
738
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700739MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500740 if (!hasStorage())
741 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500742 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood9df53fae2011-04-21 17:05:55 -0700743 uint64_t offset;
744 uint32_t length;
745 offset = mRequest.getParameter(2);
746 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
747 // android extension with 64 bit offset
748 uint64_t offset2 = mRequest.getParameter(3);
749 offset = offset | (offset2 << 32);
750 length = mRequest.getParameter(4);
751 } else {
752 // standard GetPartialObject
753 length = mRequest.getParameter(3);
754 }
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500755 MtpString pathBuf;
756 int64_t fileLength;
Mike Lockwood365e03e2010-12-08 16:08:01 -0800757 MtpObjectFormat format;
758 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood44e82dd2010-11-23 09:08:01 -0500759 if (result != MTP_RESPONSE_OK)
760 return result;
761 if (offset + length > fileLength)
762 length = fileLength - offset;
763
764 const char* filePath = (const char *)pathBuf;
765 mtp_file_range mfr;
766 mfr.fd = open(filePath, O_RDONLY);
767 if (mfr.fd < 0) {
768 return MTP_RESPONSE_GENERAL_ERROR;
769 }
770 mfr.offset = offset;
771 mfr.length = length;
772 mResponse.setParameter(1, length);
773
774 // send data header
775 mData.setOperationCode(mRequest.getOperationCode());
776 mData.setTransactionID(mRequest.getTransactionID());
777 mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
778
779 // then transfer the file
780 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
781 close(mfr.fd);
782 if (ret < 0) {
783 if (errno == ECANCELED)
784 return MTP_RESPONSE_TRANSACTION_CANCELLED;
785 else
786 return MTP_RESPONSE_GENERAL_ERROR;
787 }
788 return MTP_RESPONSE_OK;
789}
790
Mike Lockwood56118b52010-05-11 17:16:59 -0400791MtpResponseCode MtpServer::doSendObjectInfo() {
792 MtpString path;
793 MtpStorageID storageID = mRequest.getParameter(1);
794 MtpStorage* storage = getStorage(storageID);
795 MtpObjectHandle parent = mRequest.getParameter(2);
796 if (!storage)
797 return MTP_RESPONSE_INVALID_STORAGE_ID;
798
799 // special case the root
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400800 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400801 path = storage->getPath();
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400802 parent = 0;
803 } else {
Mike Lockwood365e03e2010-12-08 16:08:01 -0800804 int64_t length;
805 MtpObjectFormat format;
806 int result = mDatabase->getObjectFilePath(parent, path, length, format);
Mike Lockwood59c777a2010-08-02 10:37:41 -0400807 if (result != MTP_RESPONSE_OK)
808 return result;
Mike Lockwood365e03e2010-12-08 16:08:01 -0800809 if (format != MTP_FORMAT_ASSOCIATION)
810 return MTP_RESPONSE_INVALID_PARENT_OBJECT;
Mike Lockwood56118b52010-05-11 17:16:59 -0400811 }
812
813 // read only the fields we need
814 mData.getUInt32(); // storage ID
815 MtpObjectFormat format = mData.getUInt16();
816 mData.getUInt16(); // protection status
817 mSendObjectFileSize = mData.getUInt32();
818 mData.getUInt16(); // thumb format
819 mData.getUInt32(); // thumb compressed size
820 mData.getUInt32(); // thumb pix width
821 mData.getUInt32(); // thumb pix height
822 mData.getUInt32(); // image pix width
823 mData.getUInt32(); // image pix height
824 mData.getUInt32(); // image bit depth
825 mData.getUInt32(); // parent
826 uint16_t associationType = mData.getUInt16();
827 uint32_t associationDesc = mData.getUInt32(); // association desc
828 mData.getUInt32(); // sequence number
829 MtpStringBuffer name, created, modified;
830 mData.getString(name); // file name
831 mData.getString(created); // date created
832 mData.getString(modified); // date modified
833 // keywords follow
834
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800835 LOGV("name: %s format: %04X\n", (const char *)name, format);
Mike Lockwoodd0782672010-05-14 15:35:17 -0400836 time_t modifiedTime;
Mike Lockwood56118b52010-05-11 17:16:59 -0400837 if (!parseDateTime(modified, modifiedTime))
838 modifiedTime = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400839
840 if (path[path.size() - 1] != '/')
841 path += "/";
842 path += (const char *)name;
843
Mike Lockwood7f36b192010-12-12 12:17:43 -0800844 // check space first
845 if (mSendObjectFileSize > storage->getFreeSpace())
846 return MTP_RESPONSE_STORAGE_FULL;
847
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500848LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
Mike Lockwoodd815f792010-07-12 08:49:01 -0400849 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
850 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodd0782672010-05-14 15:35:17 -0400851 if (handle == kInvalidObjectHandle) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400852 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd0782672010-05-14 15:35:17 -0400853 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400854
855 if (format == MTP_FORMAT_ASSOCIATION) {
856 mode_t mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400857 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood56118b52010-05-11 17:16:59 -0400858 umask(mask);
859 if (ret && ret != -EEXIST)
860 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooddad69272010-07-02 15:15:07 -0400861 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood7a0bd172011-01-18 11:06:19 -0800862
863 // SendObject does not get sent for directories, so call endSendObject here instead
864 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
Mike Lockwood56118b52010-05-11 17:16:59 -0400865 } else {
866 mSendObjectFilePath = path;
867 // save the handle for the SendObject call, which should follow
868 mSendObjectHandle = handle;
Mike Lockwoodd815f792010-07-12 08:49:01 -0400869 mSendObjectFormat = format;
Mike Lockwood56118b52010-05-11 17:16:59 -0400870 }
871
872 mResponse.setParameter(1, storageID);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400873 mResponse.setParameter(2, parent);
Mike Lockwood56118b52010-05-11 17:16:59 -0400874 mResponse.setParameter(3, handle);
875
876 return MTP_RESPONSE_OK;
877}
878
879MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500880 if (!hasStorage())
881 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd815f792010-07-12 08:49:01 -0400882 MtpResponseCode result = MTP_RESPONSE_OK;
883 mode_t mask;
884 int ret;
885
Mike Lockwood56118b52010-05-11 17:16:59 -0400886 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400887 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwoodd815f792010-07-12 08:49:01 -0400888 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
889 goto done;
Mike Lockwood56118b52010-05-11 17:16:59 -0400890 }
891
892 // read the header
Mike Lockwoodd815f792010-07-12 08:49:01 -0400893 ret = mData.readDataHeader(mFD);
Mike Lockwood56118b52010-05-11 17:16:59 -0400894 // FIXME - check for errors here.
895
896 // reset so we don't attempt to send this back
897 mData.reset();
898
899 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400900 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
901 if (mfr.fd < 0) {
Mike Lockwoodd815f792010-07-12 08:49:01 -0400902 result = MTP_RESPONSE_GENERAL_ERROR;
903 goto done;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400904 }
Mike Lockwooddad69272010-07-02 15:15:07 -0400905 fchown(mfr.fd, getuid(), mFileGroup);
906 // set permissions
Mike Lockwoodd815f792010-07-12 08:49:01 -0400907 mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400908 fchmod(mfr.fd, mFilePermission);
909 umask(mask);
910
Mike Lockwood56118b52010-05-11 17:16:59 -0400911 mfr.offset = 0;
912 mfr.length = mSendObjectFileSize;
913
Mike Lockwoodf26a5862011-01-21 21:00:54 -0800914 LOGV("receiving %s\n", (const char *)mSendObjectFilePath);
Mike Lockwood56118b52010-05-11 17:16:59 -0400915 // transfer the file
916 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400917 close(mfr.fd);
Mike Lockwooddad69272010-07-02 15:15:07 -0400918
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400919 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood56118b52010-05-11 17:16:59 -0400920
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400921 if (ret < 0) {
922 unlink(mSendObjectFilePath);
923 if (errno == ECANCELED)
Mike Lockwoodd815f792010-07-12 08:49:01 -0400924 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400925 else
Mike Lockwoodd815f792010-07-12 08:49:01 -0400926 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400927 }
Mike Lockwoodd815f792010-07-12 08:49:01 -0400928
929done:
930 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
Mike Lockwood7a0bd172011-01-18 11:06:19 -0800931 result == MTP_RESPONSE_OK);
Mike Lockwoodd815f792010-07-12 08:49:01 -0400932 mSendObjectHandle = kInvalidObjectHandle;
933 mSendObjectFormat = 0;
934 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400935}
936
Mike Lockwoodccb6e962010-09-13 17:15:58 -0400937static void deleteRecursive(const char* path) {
938 char pathbuf[PATH_MAX];
939 int pathLength = strlen(path);
940 if (pathLength >= sizeof(pathbuf) - 1) {
941 LOGE("path too long: %s\n", path);
942 }
943 strcpy(pathbuf, path);
944 if (pathbuf[pathLength - 1] != '/') {
945 pathbuf[pathLength++] = '/';
946 }
947 char* fileSpot = pathbuf + pathLength;
948 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
949
950 DIR* dir = opendir(path);
951 if (!dir) {
952 LOGE("opendir %s failed: %s", path, strerror(errno));
953 return;
954 }
955
956 struct dirent* entry;
957 while ((entry = readdir(dir))) {
958 const char* name = entry->d_name;
959
960 // ignore "." and ".."
961 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
962 continue;
963 }
964
965 int nameLength = strlen(name);
966 if (nameLength > pathRemaining) {
967 LOGE("path %s/%s too long\n", path, name);
968 continue;
969 }
970 strcpy(fileSpot, name);
971
972 int type = entry->d_type;
973 if (entry->d_type == DT_DIR) {
974 deleteRecursive(pathbuf);
975 rmdir(pathbuf);
976 } else {
977 unlink(pathbuf);
978 }
979 }
Mike Lockwooda108b2f2010-11-11 11:22:32 -0500980 closedir(dir);
Mike Lockwoodccb6e962010-09-13 17:15:58 -0400981}
982
983static void deletePath(const char* path) {
984 struct stat statbuf;
985 if (stat(path, &statbuf) == 0) {
986 if (S_ISDIR(statbuf.st_mode)) {
987 deleteRecursive(path);
988 rmdir(path);
989 } else {
990 unlink(path);
991 }
992 } else {
993 LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
994 }
995}
996
Mike Lockwood56118b52010-05-11 17:16:59 -0400997MtpResponseCode MtpServer::doDeleteObject() {
Mike Lockwood467ca0d2011-02-18 09:07:14 -0500998 if (!hasStorage())
999 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood56118b52010-05-11 17:16:59 -04001000 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodccb6e962010-09-13 17:15:58 -04001001 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood56118b52010-05-11 17:16:59 -04001002 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1003 // FIXME - implement deleting objects by format
Mike Lockwood56118b52010-05-11 17:16:59 -04001004
1005 MtpString filePath;
1006 int64_t fileLength;
Mike Lockwood365e03e2010-12-08 16:08:01 -08001007 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
Mike Lockwood59c777a2010-08-02 10:37:41 -04001008 if (result == MTP_RESPONSE_OK) {
1009 LOGV("deleting %s", (const char *)filePath);
Mike Lockwoodccb6e962010-09-13 17:15:58 -04001010 deletePath((const char *)filePath);
Mike Lockwood59c777a2010-08-02 10:37:41 -04001011 return mDatabase->deleteFile(handle);
1012 } else {
1013 return result;
1014 }
Mike Lockwood56118b52010-05-11 17:16:59 -04001015}
1016
1017MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood767c5e42010-06-30 17:00:35 -04001018 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood56118b52010-05-11 17:16:59 -04001019 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwoodf26a5862011-01-21 21:00:54 -08001020 LOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
Mike Lockwood828d19d2010-08-10 15:20:35 -04001021 MtpDebug::getFormatCodeName(format));
1022 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood767c5e42010-06-30 17:00:35 -04001023 if (!property)
1024 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood767c5e42010-06-30 17:00:35 -04001025 property->write(mData);
Mike Lockwood828d19d2010-08-10 15:20:35 -04001026 delete property;
1027 return MTP_RESPONSE_OK;
1028}
1029
1030MtpResponseCode MtpServer::doGetDevicePropDesc() {
1031 MtpDeviceProperty propCode = mRequest.getParameter(1);
Mike Lockwoodf26a5862011-01-21 21:00:54 -08001032 LOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
Mike Lockwood828d19d2010-08-10 15:20:35 -04001033 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1034 if (!property)
1035 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1036 property->write(mData);
1037 delete property;
Mike Lockwood767c5e42010-06-30 17:00:35 -04001038 return MTP_RESPONSE_OK;
Mike Lockwood56118b52010-05-11 17:16:59 -04001039}
Mike Lockwood8d3257a2010-05-14 10:10:36 -04001040
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001041MtpResponseCode MtpServer::doSendPartialObject() {
1042 if (!hasStorage())
1043 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1044 MtpObjectHandle handle = mRequest.getParameter(1);
1045 uint64_t offset = mRequest.getParameter(2);
1046 uint64_t offset2 = mRequest.getParameter(3);
1047 offset = offset | (offset2 << 32);
1048 uint32_t length = mRequest.getParameter(4);
1049
1050 ObjectEdit* edit = getEditObject(handle);
1051 if (!edit) {
1052 LOGE("object not open for edit in doSendPartialObject");
1053 return MTP_RESPONSE_GENERAL_ERROR;
1054 }
1055
1056 // can't start writing past the end of the file
Mike Lockwood10031992011-04-25 12:56:21 -07001057 if (offset > edit->mSize) {
1058 LOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001059 return MTP_RESPONSE_GENERAL_ERROR;
1060 }
1061
1062 // read the header
1063 int ret = mData.readDataHeader(mFD);
1064 // FIXME - check for errors here.
1065
1066 // reset so we don't attempt to send this back
1067 mData.reset();
1068
Mike Lockwood10031992011-04-25 12:56:21 -07001069 const char* filePath = (const char *)edit->mPath;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001070 LOGV("receiving partial %s %lld %ld\n", filePath, offset, length);
1071 mtp_file_range mfr;
Mike Lockwood10031992011-04-25 12:56:21 -07001072 mfr.fd = edit->mFD;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001073 mfr.offset = offset;
1074 mfr.length = length;
1075
1076 // transfer the file
1077 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
1078 LOGV("MTP_RECEIVE_FILE returned %d", ret);
1079 if (ret < 0) {
1080 mResponse.setParameter(1, 0);
1081 if (errno == ECANCELED)
1082 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1083 else
1084 return MTP_RESPONSE_GENERAL_ERROR;
1085 }
1086 mResponse.setParameter(1, length);
1087 uint64_t end = offset + length;
Mike Lockwood10031992011-04-25 12:56:21 -07001088 if (end > edit->mSize) {
1089 edit->mSize = end;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001090 }
1091 return MTP_RESPONSE_OK;
1092}
1093
1094MtpResponseCode MtpServer::doTruncateObject() {
1095 MtpObjectHandle handle = mRequest.getParameter(1);
1096 ObjectEdit* edit = getEditObject(handle);
1097 if (!edit) {
1098 LOGE("object not open for edit in doTruncateObject");
1099 return MTP_RESPONSE_GENERAL_ERROR;
1100 }
1101
1102 uint64_t offset = mRequest.getParameter(2);
1103 uint64_t offset2 = mRequest.getParameter(3);
1104 offset |= (offset2 << 32);
Mike Lockwood10031992011-04-25 12:56:21 -07001105 if (ftruncate(edit->mFD, offset) != 0) {
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001106 return MTP_RESPONSE_GENERAL_ERROR;
1107 } else {
Mike Lockwood10031992011-04-25 12:56:21 -07001108 edit->mSize = offset;
Mike Lockwood9df53fae2011-04-21 17:05:55 -07001109 return MTP_RESPONSE_OK;
1110 }
1111}
1112
1113MtpResponseCode MtpServer::doBeginEditObject() {
1114 MtpObjectHandle handle = mRequest.getParameter(1);
1115 if (getEditObject(handle)) {
1116 LOGE("object already open for edit in doBeginEditObject");
1117 return MTP_RESPONSE_GENERAL_ERROR;
1118 }
1119
1120 MtpString path;
1121 int64_t fileLength;
1122 MtpObjectFormat format;
1123 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1124 if (result != MTP_RESPONSE_OK)
1125 return result;
1126
1127 int fd = open((const char *)path, O_RDWR | O_EXCL);
1128 if (fd < 0) {
1129 LOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
1130 return MTP_RESPONSE_GENERAL_ERROR;
1131 }
1132
1133 addEditObject(handle, path, fileLength, format, fd);
1134 return MTP_RESPONSE_OK;
1135}
1136
1137MtpResponseCode MtpServer::doEndEditObject() {
1138 MtpObjectHandle handle = mRequest.getParameter(1);
1139 ObjectEdit* edit = getEditObject(handle);
1140 if (!edit) {
1141 LOGE("object not open for edit in doEndEditObject");
1142 return MTP_RESPONSE_GENERAL_ERROR;
1143 }
1144
1145 commitEdit(edit);
1146 removeEditObject(handle);
1147 return MTP_RESPONSE_OK;
1148}
1149
Mike Lockwood8d3257a2010-05-14 10:10:36 -04001150} // namespace android