blob: dadfb545ff78916c6db3733e96c19579ed4f24a3 [file] [log] [blame]
Mike Lockwood16864ba2010-05-11 17:16:59 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
Mike Lockwoodd3211492010-09-13 17:15:58 -040024#include <sys/stat.h>
25#include <dirent.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040026
Mike Lockwoodc42aa122010-06-14 17:58:08 -070027#include <cutils/properties.h>
28
Mike Lockwooda881b442010-09-23 22:32:05 -040029#define LOG_TAG "MtpServer"
30
Mike Lockwood16864ba2010-05-11 17:16:59 -040031#include "MtpDebug.h"
Mike Lockwood7f53a192010-07-09 10:45:22 -040032#include "MtpDatabase.h"
Mike Lockwood7d77dcf2011-04-21 17:05:55 -070033#include "MtpObjectInfo.h"
Mike Lockwood21ef7d02010-06-30 17:00:35 -040034#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040035#include "MtpServer.h"
36#include "MtpStorage.h"
37#include "MtpStringBuffer.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040038
Mike Lockwood8065e202010-07-15 13:36:52 -040039#include <linux/usb/f_mtp.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040040
Mike Lockwood7850ef92010-05-14 10:10:36 -040041namespace android {
42
Mike Lockwood16864ba2010-05-11 17:16:59 -040043static const MtpOperationCode kSupportedOperationCodes[] = {
44 MTP_OPERATION_GET_DEVICE_INFO,
45 MTP_OPERATION_OPEN_SESSION,
46 MTP_OPERATION_CLOSE_SESSION,
47 MTP_OPERATION_GET_STORAGE_IDS,
48 MTP_OPERATION_GET_STORAGE_INFO,
49 MTP_OPERATION_GET_NUM_OBJECTS,
50 MTP_OPERATION_GET_OBJECT_HANDLES,
51 MTP_OPERATION_GET_OBJECT_INFO,
52 MTP_OPERATION_GET_OBJECT,
Mike Lockwood64000782011-04-24 18:40:17 -070053 MTP_OPERATION_GET_THUMB,
Mike Lockwood16864ba2010-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 Lockwoode3e76c42010-09-02 14:57:30 -040063 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood8277cec2010-08-10 15:20:35 -040064 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
65 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
66 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040067// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
68// MTP_OPERATION_MOVE_OBJECT,
69// MTP_OPERATION_COPY_OBJECT,
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -050070 MTP_OPERATION_GET_PARTIAL_OBJECT,
Mike Lockwood16864ba2010-05-11 17:16:59 -040071// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
72 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
Mike Lockwood8277cec2010-08-10 15:20:35 -040073 MTP_OPERATION_GET_OBJECT_PROP_DESC,
Mike Lockwood677f5702010-09-23 23:04:28 -040074 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
75 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwoodb6da06e2010-10-14 18:03:25 -040076 MTP_OPERATION_GET_OBJECT_PROP_LIST,
77// MTP_OPERATION_SET_OBJECT_PROP_LIST,
78// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
79// MTP_OPERATION_SEND_OBJECT_PROP_LIST,
Mike Lockwood438344f2010-08-03 15:30:09 -040080 MTP_OPERATION_GET_OBJECT_REFERENCES,
81 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood16864ba2010-05-11 17:16:59 -040082// MTP_OPERATION_SKIP,
Mike Lockwood7d77dcf2011-04-21 17:05:55 -070083 // Android extension for direct file IO
84 MTP_OPERATION_GET_PARTIAL_OBJECT_64,
85 MTP_OPERATION_SEND_PARTIAL_OBJECT,
86 MTP_OPERATION_TRUNCATE_OBJECT,
87 MTP_OPERATION_BEGIN_EDIT_OBJECT,
88 MTP_OPERATION_END_EDIT_OBJECT,
Mike Lockwood16864ba2010-05-11 17:16:59 -040089};
90
Mike Lockwood873871f2010-07-12 18:54:16 -040091static const MtpEventCode kSupportedEventCodes[] = {
92 MTP_EVENT_OBJECT_ADDED,
93 MTP_EVENT_OBJECT_REMOVED,
Mike Lockwooda8494402011-02-18 09:07:14 -050094 MTP_EVENT_STORE_ADDED,
95 MTP_EVENT_STORE_REMOVED,
Mike Lockwood0fa848d2014-03-07 13:29:59 -080096 MTP_EVENT_DEVICE_PROP_CHANGED,
Mike Lockwood873871f2010-07-12 18:54:16 -040097};
98
Mike Lockwood3d1d7762011-06-21 08:27:06 -040099MtpServer::MtpServer(int fd, MtpDatabase* database, bool ptp,
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400100 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -0400101 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400102 mDatabase(database),
Mike Lockwood3d1d7762011-06-21 08:27:06 -0400103 mPtp(ptp),
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400104 mFileGroup(fileGroup),
105 mFilePermission(filePerm),
106 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400107 mSessionID(0),
108 mSessionOpen(false),
109 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -0400110 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -0400111 mSendObjectFileSize(0)
112{
Mike Lockwood16864ba2010-05-11 17:16:59 -0400113}
114
115MtpServer::~MtpServer() {
116}
117
Mike Lockwooda8494402011-02-18 09:07:14 -0500118void MtpServer::addStorage(MtpStorage* storage) {
119 Mutex::Autolock autoLock(mMutex);
120
121 mStorages.push(storage);
122 sendStoreAdded(storage->getStorageID());
123}
124
125void MtpServer::removeStorage(MtpStorage* storage) {
126 Mutex::Autolock autoLock(mMutex);
127
128 for (int i = 0; i < mStorages.size(); i++) {
129 if (mStorages[i] == storage) {
130 mStorages.removeAt(i);
131 sendStoreRemoved(storage->getStorageID());
132 break;
133 }
134 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400135}
136
137MtpStorage* MtpServer::getStorage(MtpStorageID id) {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800138 if (id == 0)
139 return mStorages[0];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400140 for (int i = 0; i < mStorages.size(); i++) {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800141 MtpStorage* storage = mStorages[i];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400142 if (storage->getStorageID() == id)
143 return storage;
144 }
145 return NULL;
146}
147
Mike Lockwooda8494402011-02-18 09:07:14 -0500148bool MtpServer::hasStorage(MtpStorageID id) {
149 if (id == 0 || id == 0xFFFFFFFF)
150 return mStorages.size() > 0;
151 return (getStorage(id) != NULL);
152}
153
Mike Lockwood16864ba2010-05-11 17:16:59 -0400154void MtpServer::run() {
155 int fd = mFD;
156
Steve Block3856b092011-10-20 11:56:00 +0100157 ALOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400158
159 while (1) {
160 int ret = mRequest.read(fd);
161 if (ret < 0) {
Steve Block3856b092011-10-20 11:56:00 +0100162 ALOGV("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400163 if (errno == ECANCELED) {
164 // return to top of loop and wait for next command
165 continue;
166 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400167 break;
168 }
169 MtpOperationCode operation = mRequest.getOperationCode();
170 MtpTransactionID transaction = mRequest.getTransactionID();
171
Steve Block3856b092011-10-20 11:56:00 +0100172 ALOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400173 mRequest.dump();
174
175 // FIXME need to generalize this
Mike Lockwood438344f2010-08-03 15:30:09 -0400176 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood8277cec2010-08-10 15:20:35 -0400177 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
178 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
179 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400180 if (dataIn) {
181 int ret = mData.read(fd);
182 if (ret < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000183 ALOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400184 if (errno == ECANCELED) {
185 // return to top of loop and wait for next command
186 continue;
187 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400188 break;
189 }
Steve Block3856b092011-10-20 11:56:00 +0100190 ALOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400191 mData.dump();
192 } else {
193 mData.reset();
194 }
195
Mike Lockwood916076c2010-06-04 09:49:21 -0400196 if (handleRequest()) {
197 if (!dataIn && mData.hasData()) {
198 mData.setOperationCode(operation);
199 mData.setTransactionID(transaction);
Steve Block3856b092011-10-20 11:56:00 +0100200 ALOGV("sending data:");
Mike Lockwood23d20712010-10-11 17:31:44 -0400201 mData.dump();
Mike Lockwood916076c2010-06-04 09:49:21 -0400202 ret = mData.write(fd);
203 if (ret < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000204 ALOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400205 if (errno == ECANCELED) {
206 // return to top of loop and wait for next command
207 continue;
208 }
209 break;
210 }
211 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400212
Mike Lockwood916076c2010-06-04 09:49:21 -0400213 mResponse.setTransactionID(transaction);
Steve Block3856b092011-10-20 11:56:00 +0100214 ALOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400215 ret = mResponse.write(fd);
Mike Lockwood23d20712010-10-11 17:31:44 -0400216 mResponse.dump();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400217 if (ret < 0) {
Steve Block29357bc2012-01-06 19:20:56 +0000218 ALOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400219 if (errno == ECANCELED) {
220 // return to top of loop and wait for next command
221 continue;
222 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400223 break;
224 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400225 } else {
Steve Block3856b092011-10-20 11:56:00 +0100226 ALOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400227 }
228 }
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400229
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700230 // commit any open edits
231 int count = mObjectEditList.size();
232 for (int i = 0; i < count; i++) {
233 ObjectEdit* edit = mObjectEditList[i];
234 commitEdit(edit);
235 delete edit;
236 }
237 mObjectEditList.clear();
238
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400239 if (mSessionOpen)
240 mDatabase->sessionEnded();
Mike Lockwooddec73882011-07-11 15:04:38 -0400241 close(fd);
242 mFD = -1;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400243}
244
Mike Lockwood873871f2010-07-12 18:54:16 -0400245void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Steve Block3856b092011-10-20 11:56:00 +0100246 ALOGV("sendObjectAdded %d\n", handle);
Mike Lockwooda8494402011-02-18 09:07:14 -0500247 sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
Mike Lockwood873871f2010-07-12 18:54:16 -0400248}
249
250void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Steve Block3856b092011-10-20 11:56:00 +0100251 ALOGV("sendObjectRemoved %d\n", handle);
Mike Lockwooda8494402011-02-18 09:07:14 -0500252 sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
253}
254
255void MtpServer::sendStoreAdded(MtpStorageID id) {
Steve Block3856b092011-10-20 11:56:00 +0100256 ALOGV("sendStoreAdded %08X\n", id);
Mike Lockwooda8494402011-02-18 09:07:14 -0500257 sendEvent(MTP_EVENT_STORE_ADDED, id);
258}
259
260void MtpServer::sendStoreRemoved(MtpStorageID id) {
Steve Block3856b092011-10-20 11:56:00 +0100261 ALOGV("sendStoreRemoved %08X\n", id);
Mike Lockwooda8494402011-02-18 09:07:14 -0500262 sendEvent(MTP_EVENT_STORE_REMOVED, id);
263}
264
Mike Lockwood0fa848d2014-03-07 13:29:59 -0800265void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) {
266 ALOGV("sendDevicePropertyChanged %d\n", property);
267 sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property);
268}
269
Mike Lockwooda8494402011-02-18 09:07:14 -0500270void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400271 if (mSessionOpen) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500272 mEvent.setEventCode(code);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400273 mEvent.setTransactionID(mRequest.getTransactionID());
Mike Lockwooda8494402011-02-18 09:07:14 -0500274 mEvent.setParameter(1, param1);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400275 int ret = mEvent.write(mFD);
Steve Block3856b092011-10-20 11:56:00 +0100276 ALOGV("mEvent.write returned %d\n", ret);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400277 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400278}
279
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700280void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
281 uint64_t size, MtpObjectFormat format, int fd) {
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700282 ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700283 mObjectEditList.add(edit);
284}
285
286MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
287 int count = mObjectEditList.size();
288 for (int i = 0; i < count; i++) {
289 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700290 if (edit->mHandle == handle) return edit;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700291 }
292 return NULL;
293}
294
295void MtpServer::removeEditObject(MtpObjectHandle handle) {
296 int count = mObjectEditList.size();
297 for (int i = 0; i < count; i++) {
298 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700299 if (edit->mHandle == handle) {
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700300 delete edit;
301 mObjectEditList.removeAt(i);
302 return;
303 }
304 }
Steve Block29357bc2012-01-06 19:20:56 +0000305 ALOGE("ObjectEdit not found in removeEditObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700306}
307
308void MtpServer::commitEdit(ObjectEdit* edit) {
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700309 mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700310}
311
312
Mike Lockwood916076c2010-06-04 09:49:21 -0400313bool MtpServer::handleRequest() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500314 Mutex::Autolock autoLock(mMutex);
315
Mike Lockwood16864ba2010-05-11 17:16:59 -0400316 MtpOperationCode operation = mRequest.getOperationCode();
317 MtpResponseCode response;
318
319 mResponse.reset();
320
321 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
322 // FIXME - need to delete mSendObjectHandle from the database
Steve Block29357bc2012-01-06 19:20:56 +0000323 ALOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400324 mSendObjectHandle = kInvalidObjectHandle;
325 }
326
327 switch (operation) {
328 case MTP_OPERATION_GET_DEVICE_INFO:
329 response = doGetDeviceInfo();
330 break;
331 case MTP_OPERATION_OPEN_SESSION:
332 response = doOpenSession();
333 break;
334 case MTP_OPERATION_CLOSE_SESSION:
335 response = doCloseSession();
336 break;
337 case MTP_OPERATION_GET_STORAGE_IDS:
338 response = doGetStorageIDs();
339 break;
340 case MTP_OPERATION_GET_STORAGE_INFO:
341 response = doGetStorageInfo();
342 break;
343 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
344 response = doGetObjectPropsSupported();
345 break;
346 case MTP_OPERATION_GET_OBJECT_HANDLES:
347 response = doGetObjectHandles();
348 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400349 case MTP_OPERATION_GET_NUM_OBJECTS:
350 response = doGetNumObjects();
351 break;
Mike Lockwood438344f2010-08-03 15:30:09 -0400352 case MTP_OPERATION_GET_OBJECT_REFERENCES:
353 response = doGetObjectReferences();
354 break;
355 case MTP_OPERATION_SET_OBJECT_REFERENCES:
356 response = doSetObjectReferences();
357 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400358 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
359 response = doGetObjectPropValue();
360 break;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400361 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
362 response = doSetObjectPropValue();
363 break;
364 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
365 response = doGetDevicePropValue();
366 break;
367 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
368 response = doSetDevicePropValue();
369 break;
370 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
371 response = doResetDevicePropValue();
372 break;
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400373 case MTP_OPERATION_GET_OBJECT_PROP_LIST:
374 response = doGetObjectPropList();
375 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400376 case MTP_OPERATION_GET_OBJECT_INFO:
377 response = doGetObjectInfo();
378 break;
379 case MTP_OPERATION_GET_OBJECT:
380 response = doGetObject();
381 break;
Mike Lockwood64000782011-04-24 18:40:17 -0700382 case MTP_OPERATION_GET_THUMB:
383 response = doGetThumb();
384 break;
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500385 case MTP_OPERATION_GET_PARTIAL_OBJECT:
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700386 case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
387 response = doGetPartialObject(operation);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500388 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400389 case MTP_OPERATION_SEND_OBJECT_INFO:
390 response = doSendObjectInfo();
391 break;
392 case MTP_OPERATION_SEND_OBJECT:
393 response = doSendObject();
394 break;
395 case MTP_OPERATION_DELETE_OBJECT:
396 response = doDeleteObject();
397 break;
398 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400399 response = doGetObjectPropDesc();
400 break;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400401 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
402 response = doGetDevicePropDesc();
403 break;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700404 case MTP_OPERATION_SEND_PARTIAL_OBJECT:
405 response = doSendPartialObject();
406 break;
407 case MTP_OPERATION_TRUNCATE_OBJECT:
408 response = doTruncateObject();
409 break;
410 case MTP_OPERATION_BEGIN_EDIT_OBJECT:
411 response = doBeginEditObject();
412 break;
413 case MTP_OPERATION_END_EDIT_OBJECT:
414 response = doEndEditObject();
415 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400416 default:
Steve Block29357bc2012-01-06 19:20:56 +0000417 ALOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400418 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
419 break;
420 }
421
Mike Lockwood916076c2010-06-04 09:49:21 -0400422 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
423 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400424 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400425 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400426}
427
428MtpResponseCode MtpServer::doGetDeviceInfo() {
429 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700430 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400431
Mike Lockwood782aef12010-08-10 07:37:50 -0400432 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
433 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
434 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
435
Mike Lockwood16864ba2010-05-11 17:16:59 -0400436 // fill in device info
437 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood3d1d7762011-06-21 08:27:06 -0400438 if (mPtp) {
439 mData.putUInt32(0);
440 } else {
441 // MTP Vendor Extension ID
442 mData.putUInt32(6);
443 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400444 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood3d1d7762011-06-21 08:27:06 -0400445 if (mPtp) {
446 // no extensions
447 string.set("");
448 } else {
449 // MTP extensions
450 string.set("microsoft.com: 1.0; android.com: 1.0;");
451 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400452 mData.putString(string); // MTP Extensions
453 mData.putUInt16(0); //Functional Mode
454 mData.putAUInt16(kSupportedOperationCodes,
455 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400456 mData.putAUInt16(kSupportedEventCodes,
457 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood782aef12010-08-10 07:37:50 -0400458 mData.putAUInt16(deviceProperties); // Device Properties Supported
459 mData.putAUInt16(captureFormats); // Capture Formats
460 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood8d08c5a2011-01-31 16:44:44 -0500461
462 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
463 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400464 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700465
466 property_get("ro.product.model", prop_value, "MTP Device");
467 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400468 mData.putString(string); // Model
469 string.set("1.0");
470 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700471
472 property_get("ro.serialno", prop_value, "????????");
473 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400474 mData.putString(string); // Serial Number
475
Mike Lockwood782aef12010-08-10 07:37:50 -0400476 delete playbackFormats;
477 delete captureFormats;
478 delete deviceProperties;
479
Mike Lockwood16864ba2010-05-11 17:16:59 -0400480 return MTP_RESPONSE_OK;
481}
482
483MtpResponseCode MtpServer::doOpenSession() {
484 if (mSessionOpen) {
485 mResponse.setParameter(1, mSessionID);
486 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
487 }
488 mSessionID = mRequest.getParameter(1);
489 mSessionOpen = true;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400490
491 mDatabase->sessionStarted();
492
Mike Lockwood16864ba2010-05-11 17:16:59 -0400493 return MTP_RESPONSE_OK;
494}
495
496MtpResponseCode MtpServer::doCloseSession() {
497 if (!mSessionOpen)
498 return MTP_RESPONSE_SESSION_NOT_OPEN;
499 mSessionID = 0;
500 mSessionOpen = false;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400501 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400502 return MTP_RESPONSE_OK;
503}
504
505MtpResponseCode MtpServer::doGetStorageIDs() {
506 if (!mSessionOpen)
507 return MTP_RESPONSE_SESSION_NOT_OPEN;
508
509 int count = mStorages.size();
510 mData.putUInt32(count);
511 for (int i = 0; i < count; i++)
512 mData.putUInt32(mStorages[i]->getStorageID());
513
514 return MTP_RESPONSE_OK;
515}
516
517MtpResponseCode MtpServer::doGetStorageInfo() {
518 MtpStringBuffer string;
519
520 if (!mSessionOpen)
521 return MTP_RESPONSE_SESSION_NOT_OPEN;
522 MtpStorageID id = mRequest.getParameter(1);
523 MtpStorage* storage = getStorage(id);
524 if (!storage)
525 return MTP_RESPONSE_INVALID_STORAGE_ID;
526
527 mData.putUInt16(storage->getType());
528 mData.putUInt16(storage->getFileSystemType());
529 mData.putUInt16(storage->getAccessCapability());
530 mData.putUInt64(storage->getMaxCapacity());
531 mData.putUInt64(storage->getFreeSpace());
532 mData.putUInt32(1024*1024*1024); // Free Space in Objects
533 string.set(storage->getDescription());
534 mData.putString(string);
535 mData.putEmptyString(); // Volume Identifier
536
537 return MTP_RESPONSE_OK;
538}
539
540MtpResponseCode MtpServer::doGetObjectPropsSupported() {
541 if (!mSessionOpen)
542 return MTP_RESPONSE_SESSION_NOT_OPEN;
543 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood2e09e282010-12-07 10:51:20 -0800544 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
Mike Lockwood782aef12010-08-10 07:37:50 -0400545 mData.putAUInt16(properties);
Mike Lockwoodbf9b2052010-08-10 15:11:32 -0400546 delete properties;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400547 return MTP_RESPONSE_OK;
548}
549
550MtpResponseCode MtpServer::doGetObjectHandles() {
551 if (!mSessionOpen)
552 return MTP_RESPONSE_SESSION_NOT_OPEN;
553 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400554 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400555 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
Mike Lockwooddc3185e2011-06-17 13:44:24 -0400556 // 0x00000000 for all objects
Mike Lockwooda8494402011-02-18 09:07:14 -0500557
558 if (!hasStorage(storageID))
559 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400560
561 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
562 mData.putAUInt32(handles);
563 delete handles;
564 return MTP_RESPONSE_OK;
565}
566
Mike Lockwood343af4e2010-08-02 10:52:20 -0400567MtpResponseCode MtpServer::doGetNumObjects() {
568 if (!mSessionOpen)
569 return MTP_RESPONSE_SESSION_NOT_OPEN;
570 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
571 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
572 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
Mike Lockwooddc3185e2011-06-17 13:44:24 -0400573 // 0x00000000 for all objects
Mike Lockwooda8494402011-02-18 09:07:14 -0500574 if (!hasStorage(storageID))
575 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400576
577 int count = mDatabase->getNumObjects(storageID, format, parent);
578 if (count >= 0) {
579 mResponse.setParameter(1, count);
580 return MTP_RESPONSE_OK;
581 } else {
582 mResponse.setParameter(1, 0);
583 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
584 }
585}
586
Mike Lockwood438344f2010-08-03 15:30:09 -0400587MtpResponseCode MtpServer::doGetObjectReferences() {
588 if (!mSessionOpen)
589 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwooda8494402011-02-18 09:07:14 -0500590 if (!hasStorage())
591 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
592 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400593
594 // FIXME - check for invalid object handle
Mike Lockwood438344f2010-08-03 15:30:09 -0400595 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400596 if (handles) {
597 mData.putAUInt32(handles);
598 delete handles;
599 } else {
Mike Lockwood438344f2010-08-03 15:30:09 -0400600 mData.putEmptyArray();
Mike Lockwood438344f2010-08-03 15:30:09 -0400601 }
Mike Lockwood438344f2010-08-03 15:30:09 -0400602 return MTP_RESPONSE_OK;
603}
604
605MtpResponseCode MtpServer::doSetObjectReferences() {
606 if (!mSessionOpen)
607 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwooda8494402011-02-18 09:07:14 -0500608 if (!hasStorage())
609 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood438344f2010-08-03 15:30:09 -0400610 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwooda8494402011-02-18 09:07:14 -0500611
Mike Lockwood438344f2010-08-03 15:30:09 -0400612 MtpObjectHandleList* references = mData.getAUInt32();
613 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
614 delete references;
615 return result;
616}
617
Mike Lockwood16864ba2010-05-11 17:16:59 -0400618MtpResponseCode MtpServer::doGetObjectPropValue() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500619 if (!hasStorage())
620 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400621 MtpObjectHandle handle = mRequest.getParameter(1);
622 MtpObjectProperty property = mRequest.getParameter(2);
Steve Block3856b092011-10-20 11:56:00 +0100623 ALOGV("GetObjectPropValue %d %s\n", handle,
Mike Lockwood8277cec2010-08-10 15:20:35 -0400624 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400625
Mike Lockwood8277cec2010-08-10 15:20:35 -0400626 return mDatabase->getObjectPropertyValue(handle, property, mData);
627}
628
629MtpResponseCode MtpServer::doSetObjectPropValue() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500630 if (!hasStorage())
631 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400632 MtpObjectHandle handle = mRequest.getParameter(1);
633 MtpObjectProperty property = mRequest.getParameter(2);
Steve Block3856b092011-10-20 11:56:00 +0100634 ALOGV("SetObjectPropValue %d %s\n", handle,
Mike Lockwood8277cec2010-08-10 15:20:35 -0400635 MtpDebug::getObjectPropCodeName(property));
636
637 return mDatabase->setObjectPropertyValue(handle, property, mData);
638}
639
640MtpResponseCode MtpServer::doGetDevicePropValue() {
641 MtpDeviceProperty property = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +0100642 ALOGV("GetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400643 MtpDebug::getDevicePropCodeName(property));
644
645 return mDatabase->getDevicePropertyValue(property, mData);
646}
647
648MtpResponseCode MtpServer::doSetDevicePropValue() {
649 MtpDeviceProperty property = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +0100650 ALOGV("SetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400651 MtpDebug::getDevicePropCodeName(property));
652
653 return mDatabase->setDevicePropertyValue(property, mData);
654}
655
656MtpResponseCode MtpServer::doResetDevicePropValue() {
657 MtpDeviceProperty property = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +0100658 ALOGV("ResetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400659 MtpDebug::getDevicePropCodeName(property));
660
661 return mDatabase->resetDeviceProperty(property);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400662}
663
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400664MtpResponseCode MtpServer::doGetObjectPropList() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500665 if (!hasStorage())
666 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400667
668 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood40ce1f22010-12-01 18:46:23 -0500669 // use uint32_t so we can support 0xFFFFFFFF
670 uint32_t format = mRequest.getParameter(2);
671 uint32_t property = mRequest.getParameter(3);
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400672 int groupCode = mRequest.getParameter(4);
Mike Lockwoodf05ff072010-11-23 18:45:25 -0500673 int depth = mRequest.getParameter(5);
Steve Block3856b092011-10-20 11:56:00 +0100674 ALOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400675 handle, MtpDebug::getFormatCodeName(format),
676 MtpDebug::getObjectPropCodeName(property), groupCode, depth);
677
678 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
679}
680
Mike Lockwood16864ba2010-05-11 17:16:59 -0400681MtpResponseCode MtpServer::doGetObjectInfo() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500682 if (!hasStorage())
683 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400684 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700685 MtpObjectInfo info(handle);
686 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
687 if (result == MTP_RESPONSE_OK) {
688 char date[20];
689
690 mData.putUInt32(info.mStorageID);
691 mData.putUInt16(info.mFormat);
692 mData.putUInt16(info.mProtectionStatus);
693
694 // if object is being edited the database size may be out of date
695 uint32_t size = info.mCompressedSize;
696 ObjectEdit* edit = getEditObject(handle);
697 if (edit)
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700698 size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700699 mData.putUInt32(size);
700
701 mData.putUInt16(info.mThumbFormat);
702 mData.putUInt32(info.mThumbCompressedSize);
703 mData.putUInt32(info.mThumbPixWidth);
704 mData.putUInt32(info.mThumbPixHeight);
705 mData.putUInt32(info.mImagePixWidth);
706 mData.putUInt32(info.mImagePixHeight);
707 mData.putUInt32(info.mImagePixDepth);
708 mData.putUInt32(info.mParent);
709 mData.putUInt16(info.mAssociationType);
710 mData.putUInt32(info.mAssociationDesc);
711 mData.putUInt32(info.mSequenceNumber);
712 mData.putString(info.mName);
Mike Lockwoodec24fa42013-04-01 10:51:35 -0700713 formatDateTime(info.mDateCreated, date, sizeof(date));
714 mData.putString(date); // date created
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700715 formatDateTime(info.mDateModified, date, sizeof(date));
716 mData.putString(date); // date modified
717 mData.putEmptyString(); // keywords
718 }
719 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400720}
721
722MtpResponseCode MtpServer::doGetObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500723 if (!hasStorage())
724 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400725 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400726 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400727 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800728 MtpObjectFormat format;
729 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400730 if (result != MTP_RESPONSE_OK)
731 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400732
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400733 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400734 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400735 mfr.fd = open(filePath, O_RDONLY);
736 if (mfr.fd < 0) {
737 return MTP_RESPONSE_GENERAL_ERROR;
738 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400739 mfr.offset = 0;
740 mfr.length = fileLength;
Mike Lockwoodef441d92011-07-14 21:00:02 -0400741 mfr.command = mRequest.getOperationCode();
742 mfr.transaction_id = mRequest.getTransactionID();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400743
744 // then transfer the file
Mike Lockwoodef441d92011-07-14 21:00:02 -0400745 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +0100746 ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400747 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400748 if (ret < 0) {
749 if (errno == ECANCELED)
750 return MTP_RESPONSE_TRANSACTION_CANCELLED;
751 else
752 return MTP_RESPONSE_GENERAL_ERROR;
753 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400754 return MTP_RESPONSE_OK;
755}
756
Mike Lockwood64000782011-04-24 18:40:17 -0700757MtpResponseCode MtpServer::doGetThumb() {
758 MtpObjectHandle handle = mRequest.getParameter(1);
759 size_t thumbSize;
760 void* thumb = mDatabase->getThumbnail(handle, thumbSize);
761 if (thumb) {
762 // send data
763 mData.setOperationCode(mRequest.getOperationCode());
764 mData.setTransactionID(mRequest.getTransactionID());
765 mData.writeData(mFD, thumb, thumbSize);
766 free(thumb);
767 return MTP_RESPONSE_OK;
768 } else {
769 return MTP_RESPONSE_GENERAL_ERROR;
770 }
771}
772
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700773MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500774 if (!hasStorage())
775 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500776 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700777 uint64_t offset;
778 uint32_t length;
779 offset = mRequest.getParameter(2);
780 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
781 // android extension with 64 bit offset
782 uint64_t offset2 = mRequest.getParameter(3);
783 offset = offset | (offset2 << 32);
784 length = mRequest.getParameter(4);
785 } else {
786 // standard GetPartialObject
787 length = mRequest.getParameter(3);
788 }
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500789 MtpString pathBuf;
790 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800791 MtpObjectFormat format;
792 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500793 if (result != MTP_RESPONSE_OK)
794 return result;
795 if (offset + length > fileLength)
796 length = fileLength - offset;
797
798 const char* filePath = (const char *)pathBuf;
799 mtp_file_range mfr;
800 mfr.fd = open(filePath, O_RDONLY);
801 if (mfr.fd < 0) {
802 return MTP_RESPONSE_GENERAL_ERROR;
803 }
804 mfr.offset = offset;
805 mfr.length = length;
Mike Lockwoodef441d92011-07-14 21:00:02 -0400806 mfr.command = mRequest.getOperationCode();
807 mfr.transaction_id = mRequest.getTransactionID();
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500808 mResponse.setParameter(1, length);
809
Mike Lockwoodef441d92011-07-14 21:00:02 -0400810 // transfer the file
811 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +0100812 ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500813 close(mfr.fd);
814 if (ret < 0) {
815 if (errno == ECANCELED)
816 return MTP_RESPONSE_TRANSACTION_CANCELLED;
817 else
818 return MTP_RESPONSE_GENERAL_ERROR;
819 }
820 return MTP_RESPONSE_OK;
821}
822
Mike Lockwood16864ba2010-05-11 17:16:59 -0400823MtpResponseCode MtpServer::doSendObjectInfo() {
824 MtpString path;
825 MtpStorageID storageID = mRequest.getParameter(1);
826 MtpStorage* storage = getStorage(storageID);
827 MtpObjectHandle parent = mRequest.getParameter(2);
828 if (!storage)
829 return MTP_RESPONSE_INVALID_STORAGE_ID;
830
831 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400832 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400833 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400834 parent = 0;
835 } else {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800836 int64_t length;
837 MtpObjectFormat format;
838 int result = mDatabase->getObjectFilePath(parent, path, length, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400839 if (result != MTP_RESPONSE_OK)
840 return result;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800841 if (format != MTP_FORMAT_ASSOCIATION)
842 return MTP_RESPONSE_INVALID_PARENT_OBJECT;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400843 }
844
845 // read only the fields we need
846 mData.getUInt32(); // storage ID
847 MtpObjectFormat format = mData.getUInt16();
848 mData.getUInt16(); // protection status
849 mSendObjectFileSize = mData.getUInt32();
850 mData.getUInt16(); // thumb format
851 mData.getUInt32(); // thumb compressed size
852 mData.getUInt32(); // thumb pix width
853 mData.getUInt32(); // thumb pix height
854 mData.getUInt32(); // image pix width
855 mData.getUInt32(); // image pix height
856 mData.getUInt32(); // image bit depth
857 mData.getUInt32(); // parent
858 uint16_t associationType = mData.getUInt16();
859 uint32_t associationDesc = mData.getUInt32(); // association desc
860 mData.getUInt32(); // sequence number
861 MtpStringBuffer name, created, modified;
862 mData.getString(name); // file name
863 mData.getString(created); // date created
864 mData.getString(modified); // date modified
865 // keywords follow
866
Steve Block3856b092011-10-20 11:56:00 +0100867 ALOGV("name: %s format: %04X\n", (const char *)name, format);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400868 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400869 if (!parseDateTime(modified, modifiedTime))
870 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400871
872 if (path[path.size() - 1] != '/')
873 path += "/";
874 path += (const char *)name;
875
Mike Lockwood20c3be02010-12-12 12:17:43 -0800876 // check space first
877 if (mSendObjectFileSize > storage->getFreeSpace())
878 return MTP_RESPONSE_STORAGE_FULL;
Mike Lockwood9b88b722011-07-11 09:18:03 -0400879 uint64_t maxFileSize = storage->getMaxFileSize();
880 // check storage max file size
881 if (maxFileSize != 0) {
882 // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
883 // is >= 0xFFFFFFFF
884 if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
885 return MTP_RESPONSE_OBJECT_TOO_LARGE;
886 }
Mike Lockwood20c3be02010-12-12 12:17:43 -0800887
Steve Blockb8a80522011-12-20 16:23:08 +0000888 ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
Mike Lockwood4714b072010-07-12 08:49:01 -0400889 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
890 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400891 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400892 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400893 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400894
895 if (format == MTP_FORMAT_ASSOCIATION) {
896 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400897 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400898 umask(mask);
899 if (ret && ret != -EEXIST)
900 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400901 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwoodaa952402011-01-18 11:06:19 -0800902
903 // SendObject does not get sent for directories, so call endSendObject here instead
904 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400905 } else {
906 mSendObjectFilePath = path;
907 // save the handle for the SendObject call, which should follow
908 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400909 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400910 }
911
912 mResponse.setParameter(1, storageID);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400913 mResponse.setParameter(2, parent);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400914 mResponse.setParameter(3, handle);
915
916 return MTP_RESPONSE_OK;
917}
918
919MtpResponseCode MtpServer::doSendObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500920 if (!hasStorage())
921 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood4714b072010-07-12 08:49:01 -0400922 MtpResponseCode result = MTP_RESPONSE_OK;
923 mode_t mask;
Mike Lockwoodef441d92011-07-14 21:00:02 -0400924 int ret, initialData;
Mike Lockwood4714b072010-07-12 08:49:01 -0400925
Mike Lockwood16864ba2010-05-11 17:16:59 -0400926 if (mSendObjectHandle == kInvalidObjectHandle) {
Steve Block29357bc2012-01-06 19:20:56 +0000927 ALOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400928 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
929 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400930 }
931
Mike Lockwoodef441d92011-07-14 21:00:02 -0400932 // read the header, and possibly some data
933 ret = mData.read(mFD);
934 if (ret < MTP_CONTAINER_HEADER_SIZE) {
935 result = MTP_RESPONSE_GENERAL_ERROR;
936 goto done;
937 }
938 initialData = ret - MTP_CONTAINER_HEADER_SIZE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400939
940 mtp_file_range mfr;
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700941 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400942 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400943 result = MTP_RESPONSE_GENERAL_ERROR;
944 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400945 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400946 fchown(mfr.fd, getuid(), mFileGroup);
947 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400948 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400949 fchmod(mfr.fd, mFilePermission);
950 umask(mask);
951
Mike Lockwoodef441d92011-07-14 21:00:02 -0400952 if (initialData > 0)
953 ret = write(mfr.fd, mData.getData(), initialData);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400954
Mike Lockwoodef441d92011-07-14 21:00:02 -0400955 if (mSendObjectFileSize - initialData > 0) {
956 mfr.offset = initialData;
Mike Lockwood0cc79c62011-10-13 11:38:20 -0400957 if (mSendObjectFileSize == 0xFFFFFFFF) {
958 // tell driver to read until it receives a short packet
959 mfr.length = 0xFFFFFFFF;
960 } else {
961 mfr.length = mSendObjectFileSize - initialData;
962 }
Mike Lockwoodef441d92011-07-14 21:00:02 -0400963
Steve Block3856b092011-10-20 11:56:00 +0100964 ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
Mike Lockwoodef441d92011-07-14 21:00:02 -0400965 // transfer the file
966 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +0100967 ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
Mike Lockwoodef441d92011-07-14 21:00:02 -0400968 }
Mike Lockwoodc6588762010-06-22 15:03:53 -0400969 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400970
Mike Lockwood916076c2010-06-04 09:49:21 -0400971 if (ret < 0) {
972 unlink(mSendObjectFilePath);
973 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400974 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400975 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400976 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400977 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400978
979done:
Mike Lockwoodef441d92011-07-14 21:00:02 -0400980 // reset so we don't attempt to send the data back
981 mData.reset();
982
Mike Lockwood4714b072010-07-12 08:49:01 -0400983 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
Mike Lockwoodaa952402011-01-18 11:06:19 -0800984 result == MTP_RESPONSE_OK);
Mike Lockwood4714b072010-07-12 08:49:01 -0400985 mSendObjectHandle = kInvalidObjectHandle;
986 mSendObjectFormat = 0;
987 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400988}
989
Mike Lockwoodd3211492010-09-13 17:15:58 -0400990static void deleteRecursive(const char* path) {
991 char pathbuf[PATH_MAX];
992 int pathLength = strlen(path);
993 if (pathLength >= sizeof(pathbuf) - 1) {
Steve Block29357bc2012-01-06 19:20:56 +0000994 ALOGE("path too long: %s\n", path);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400995 }
996 strcpy(pathbuf, path);
997 if (pathbuf[pathLength - 1] != '/') {
998 pathbuf[pathLength++] = '/';
999 }
1000 char* fileSpot = pathbuf + pathLength;
1001 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
1002
1003 DIR* dir = opendir(path);
1004 if (!dir) {
Steve Block29357bc2012-01-06 19:20:56 +00001005 ALOGE("opendir %s failed: %s", path, strerror(errno));
Mike Lockwoodd3211492010-09-13 17:15:58 -04001006 return;
1007 }
1008
1009 struct dirent* entry;
1010 while ((entry = readdir(dir))) {
1011 const char* name = entry->d_name;
1012
1013 // ignore "." and ".."
1014 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1015 continue;
1016 }
1017
1018 int nameLength = strlen(name);
1019 if (nameLength > pathRemaining) {
Steve Block29357bc2012-01-06 19:20:56 +00001020 ALOGE("path %s/%s too long\n", path, name);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001021 continue;
1022 }
1023 strcpy(fileSpot, name);
1024
1025 int type = entry->d_type;
1026 if (entry->d_type == DT_DIR) {
1027 deleteRecursive(pathbuf);
1028 rmdir(pathbuf);
1029 } else {
1030 unlink(pathbuf);
1031 }
1032 }
Mike Lockwood7ce05cf2010-11-11 11:22:32 -05001033 closedir(dir);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001034}
1035
1036static void deletePath(const char* path) {
1037 struct stat statbuf;
1038 if (stat(path, &statbuf) == 0) {
1039 if (S_ISDIR(statbuf.st_mode)) {
1040 deleteRecursive(path);
1041 rmdir(path);
1042 } else {
1043 unlink(path);
1044 }
1045 } else {
Steve Block29357bc2012-01-06 19:20:56 +00001046 ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
Mike Lockwoodd3211492010-09-13 17:15:58 -04001047 }
1048}
1049
Mike Lockwood16864ba2010-05-11 17:16:59 -04001050MtpResponseCode MtpServer::doDeleteObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -05001051 if (!hasStorage())
1052 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001053 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001054 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood16864ba2010-05-11 17:16:59 -04001055 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1056 // FIXME - implement deleting objects by format
Mike Lockwood16864ba2010-05-11 17:16:59 -04001057
1058 MtpString filePath;
1059 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -08001060 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -04001061 if (result == MTP_RESPONSE_OK) {
Steve Block3856b092011-10-20 11:56:00 +01001062 ALOGV("deleting %s", (const char *)filePath);
Mike Lockwooda9a46c12011-12-01 16:58:41 -05001063 result = mDatabase->deleteFile(handle);
1064 // Don't delete the actual files unless the database deletion is allowed
1065 if (result == MTP_RESPONSE_OK) {
1066 deletePath((const char *)filePath);
1067 }
Mike Lockwood9c04c4c2010-08-02 10:37:41 -04001068 }
Mike Lockwooda9a46c12011-12-01 16:58:41 -05001069
1070 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001071}
1072
1073MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001074 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -04001075 MtpObjectFormat format = mRequest.getParameter(2);
Steve Block3856b092011-10-20 11:56:00 +01001076 ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
Mike Lockwood8277cec2010-08-10 15:20:35 -04001077 MtpDebug::getFormatCodeName(format));
1078 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001079 if (!property)
1080 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001081 property->write(mData);
Mike Lockwood8277cec2010-08-10 15:20:35 -04001082 delete property;
1083 return MTP_RESPONSE_OK;
1084}
1085
1086MtpResponseCode MtpServer::doGetDevicePropDesc() {
1087 MtpDeviceProperty propCode = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +01001088 ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
Mike Lockwood8277cec2010-08-10 15:20:35 -04001089 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1090 if (!property)
1091 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1092 property->write(mData);
1093 delete property;
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001094 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001095}
Mike Lockwood7850ef92010-05-14 10:10:36 -04001096
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001097MtpResponseCode MtpServer::doSendPartialObject() {
1098 if (!hasStorage())
1099 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1100 MtpObjectHandle handle = mRequest.getParameter(1);
1101 uint64_t offset = mRequest.getParameter(2);
1102 uint64_t offset2 = mRequest.getParameter(3);
1103 offset = offset | (offset2 << 32);
1104 uint32_t length = mRequest.getParameter(4);
1105
1106 ObjectEdit* edit = getEditObject(handle);
1107 if (!edit) {
Steve Block29357bc2012-01-06 19:20:56 +00001108 ALOGE("object not open for edit in doSendPartialObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001109 return MTP_RESPONSE_GENERAL_ERROR;
1110 }
1111
1112 // can't start writing past the end of the file
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001113 if (offset > edit->mSize) {
Steve Blockb8a80522011-12-20 16:23:08 +00001114 ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001115 return MTP_RESPONSE_GENERAL_ERROR;
1116 }
1117
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001118 const char* filePath = (const char *)edit->mPath;
Steve Block3856b092011-10-20 11:56:00 +01001119 ALOGV("receiving partial %s %lld %lld\n", filePath, offset, length);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001120
Mike Lockwoodef441d92011-07-14 21:00:02 -04001121 // read the header, and possibly some data
1122 int ret = mData.read(mFD);
1123 if (ret < MTP_CONTAINER_HEADER_SIZE)
1124 return MTP_RESPONSE_GENERAL_ERROR;
1125 int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1126
1127 if (initialData > 0) {
Mike Lockwoood0a694952013-02-08 13:25:01 -08001128 ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
Mike Lockwoodef441d92011-07-14 21:00:02 -04001129 offset += initialData;
1130 length -= initialData;
1131 }
1132
1133 if (length > 0) {
1134 mtp_file_range mfr;
1135 mfr.fd = edit->mFD;
1136 mfr.offset = offset;
1137 mfr.length = length;
1138
1139 // transfer the file
1140 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +01001141 ALOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwoodef441d92011-07-14 21:00:02 -04001142 }
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001143 if (ret < 0) {
1144 mResponse.setParameter(1, 0);
1145 if (errno == ECANCELED)
1146 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1147 else
1148 return MTP_RESPONSE_GENERAL_ERROR;
1149 }
Mike Lockwoodef441d92011-07-14 21:00:02 -04001150
1151 // reset so we don't attempt to send this back
1152 mData.reset();
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001153 mResponse.setParameter(1, length);
1154 uint64_t end = offset + length;
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001155 if (end > edit->mSize) {
1156 edit->mSize = end;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001157 }
1158 return MTP_RESPONSE_OK;
1159}
1160
1161MtpResponseCode MtpServer::doTruncateObject() {
1162 MtpObjectHandle handle = mRequest.getParameter(1);
1163 ObjectEdit* edit = getEditObject(handle);
1164 if (!edit) {
Steve Block29357bc2012-01-06 19:20:56 +00001165 ALOGE("object not open for edit in doTruncateObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001166 return MTP_RESPONSE_GENERAL_ERROR;
1167 }
1168
1169 uint64_t offset = mRequest.getParameter(2);
1170 uint64_t offset2 = mRequest.getParameter(3);
1171 offset |= (offset2 << 32);
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001172 if (ftruncate(edit->mFD, offset) != 0) {
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001173 return MTP_RESPONSE_GENERAL_ERROR;
1174 } else {
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001175 edit->mSize = offset;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001176 return MTP_RESPONSE_OK;
1177 }
1178}
1179
1180MtpResponseCode MtpServer::doBeginEditObject() {
1181 MtpObjectHandle handle = mRequest.getParameter(1);
1182 if (getEditObject(handle)) {
Steve Block29357bc2012-01-06 19:20:56 +00001183 ALOGE("object already open for edit in doBeginEditObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001184 return MTP_RESPONSE_GENERAL_ERROR;
1185 }
1186
1187 MtpString path;
1188 int64_t fileLength;
1189 MtpObjectFormat format;
1190 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1191 if (result != MTP_RESPONSE_OK)
1192 return result;
1193
1194 int fd = open((const char *)path, O_RDWR | O_EXCL);
1195 if (fd < 0) {
Steve Block29357bc2012-01-06 19:20:56 +00001196 ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001197 return MTP_RESPONSE_GENERAL_ERROR;
1198 }
1199
1200 addEditObject(handle, path, fileLength, format, fd);
1201 return MTP_RESPONSE_OK;
1202}
1203
1204MtpResponseCode MtpServer::doEndEditObject() {
1205 MtpObjectHandle handle = mRequest.getParameter(1);
1206 ObjectEdit* edit = getEditObject(handle);
1207 if (!edit) {
Steve Block29357bc2012-01-06 19:20:56 +00001208 ALOGE("object not open for edit in doEndEditObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001209 return MTP_RESPONSE_GENERAL_ERROR;
1210 }
1211
1212 commitEdit(edit);
1213 removeEditObject(handle);
1214 return MTP_RESPONSE_OK;
1215}
1216
Mike Lockwood7850ef92010-05-14 10:10:36 -04001217} // namespace android