blob: 0b1f5276583ddddc13eb00533730aa41d3a2ebf4 [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>
Mark Salyzyndb43b342014-04-04 14:47:28 -070023#include <inttypes.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040024#include <errno.h>
Mike Lockwoodd3211492010-09-13 17:15:58 -040025#include <sys/stat.h>
26#include <dirent.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040027
Mike Lockwoodc42aa122010-06-14 17:58:08 -070028#include <cutils/properties.h>
29
Mike Lockwooda881b442010-09-23 22:32:05 -040030#define LOG_TAG "MtpServer"
31
Mike Lockwood16864ba2010-05-11 17:16:59 -040032#include "MtpDebug.h"
Mike Lockwood7f53a192010-07-09 10:45:22 -040033#include "MtpDatabase.h"
Mike Lockwood7d77dcf2011-04-21 17:05:55 -070034#include "MtpObjectInfo.h"
Mike Lockwood21ef7d02010-06-30 17:00:35 -040035#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040036#include "MtpServer.h"
37#include "MtpStorage.h"
38#include "MtpStringBuffer.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040039
Mike Lockwood8065e202010-07-15 13:36:52 -040040#include <linux/usb/f_mtp.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040041
Mike Lockwood7850ef92010-05-14 10:10:36 -040042namespace android {
43
Mike Lockwood16864ba2010-05-11 17:16:59 -040044static const MtpOperationCode kSupportedOperationCodes[] = {
45 MTP_OPERATION_GET_DEVICE_INFO,
46 MTP_OPERATION_OPEN_SESSION,
47 MTP_OPERATION_CLOSE_SESSION,
48 MTP_OPERATION_GET_STORAGE_IDS,
49 MTP_OPERATION_GET_STORAGE_INFO,
50 MTP_OPERATION_GET_NUM_OBJECTS,
51 MTP_OPERATION_GET_OBJECT_HANDLES,
52 MTP_OPERATION_GET_OBJECT_INFO,
53 MTP_OPERATION_GET_OBJECT,
Mike Lockwood64000782011-04-24 18:40:17 -070054 MTP_OPERATION_GET_THUMB,
Mike Lockwood16864ba2010-05-11 17:16:59 -040055 MTP_OPERATION_DELETE_OBJECT,
56 MTP_OPERATION_SEND_OBJECT_INFO,
57 MTP_OPERATION_SEND_OBJECT,
58// MTP_OPERATION_INITIATE_CAPTURE,
59// MTP_OPERATION_FORMAT_STORE,
60// MTP_OPERATION_RESET_DEVICE,
61// MTP_OPERATION_SELF_TEST,
62// MTP_OPERATION_SET_OBJECT_PROTECTION,
63// MTP_OPERATION_POWER_DOWN,
Mike Lockwoode3e76c42010-09-02 14:57:30 -040064 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood8277cec2010-08-10 15:20:35 -040065 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
66 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
67 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040068// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
69// MTP_OPERATION_MOVE_OBJECT,
70// MTP_OPERATION_COPY_OBJECT,
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -050071 MTP_OPERATION_GET_PARTIAL_OBJECT,
Mike Lockwood16864ba2010-05-11 17:16:59 -040072// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
73 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
Mike Lockwood8277cec2010-08-10 15:20:35 -040074 MTP_OPERATION_GET_OBJECT_PROP_DESC,
Mike Lockwood677f5702010-09-23 23:04:28 -040075 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
76 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwoodb6da06e2010-10-14 18:03:25 -040077 MTP_OPERATION_GET_OBJECT_PROP_LIST,
78// MTP_OPERATION_SET_OBJECT_PROP_LIST,
79// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
80// MTP_OPERATION_SEND_OBJECT_PROP_LIST,
Mike Lockwood438344f2010-08-03 15:30:09 -040081 MTP_OPERATION_GET_OBJECT_REFERENCES,
82 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood16864ba2010-05-11 17:16:59 -040083// MTP_OPERATION_SKIP,
Mike Lockwood7d77dcf2011-04-21 17:05:55 -070084 // Android extension for direct file IO
85 MTP_OPERATION_GET_PARTIAL_OBJECT_64,
86 MTP_OPERATION_SEND_PARTIAL_OBJECT,
87 MTP_OPERATION_TRUNCATE_OBJECT,
88 MTP_OPERATION_BEGIN_EDIT_OBJECT,
89 MTP_OPERATION_END_EDIT_OBJECT,
Mike Lockwood16864ba2010-05-11 17:16:59 -040090};
91
Mike Lockwood873871f2010-07-12 18:54:16 -040092static const MtpEventCode kSupportedEventCodes[] = {
93 MTP_EVENT_OBJECT_ADDED,
94 MTP_EVENT_OBJECT_REMOVED,
Mike Lockwooda8494402011-02-18 09:07:14 -050095 MTP_EVENT_STORE_ADDED,
96 MTP_EVENT_STORE_REMOVED,
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
Mark Salyzyndb43b342014-04-04 14:47:28 -0700128 for (unsigned int i = 0; i < mStorages.size(); i++) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500129 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];
Mark Salyzyndb43b342014-04-04 14:47:28 -0700140 for (unsigned 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
265void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400266 if (mSessionOpen) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500267 mEvent.setEventCode(code);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400268 mEvent.setTransactionID(mRequest.getTransactionID());
Mike Lockwooda8494402011-02-18 09:07:14 -0500269 mEvent.setParameter(1, param1);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400270 int ret = mEvent.write(mFD);
Steve Block3856b092011-10-20 11:56:00 +0100271 ALOGV("mEvent.write returned %d\n", ret);
Mike Lockwood73ecd232010-07-19 14:29:58 -0400272 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400273}
274
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700275void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
276 uint64_t size, MtpObjectFormat format, int fd) {
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700277 ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700278 mObjectEditList.add(edit);
279}
280
281MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
282 int count = mObjectEditList.size();
283 for (int i = 0; i < count; i++) {
284 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700285 if (edit->mHandle == handle) return edit;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700286 }
287 return NULL;
288}
289
290void MtpServer::removeEditObject(MtpObjectHandle handle) {
291 int count = mObjectEditList.size();
292 for (int i = 0; i < count; i++) {
293 ObjectEdit* edit = mObjectEditList[i];
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700294 if (edit->mHandle == handle) {
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700295 delete edit;
296 mObjectEditList.removeAt(i);
297 return;
298 }
299 }
Steve Block29357bc2012-01-06 19:20:56 +0000300 ALOGE("ObjectEdit not found in removeEditObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700301}
302
303void MtpServer::commitEdit(ObjectEdit* edit) {
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700304 mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700305}
306
307
Mike Lockwood916076c2010-06-04 09:49:21 -0400308bool MtpServer::handleRequest() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500309 Mutex::Autolock autoLock(mMutex);
310
Mike Lockwood16864ba2010-05-11 17:16:59 -0400311 MtpOperationCode operation = mRequest.getOperationCode();
312 MtpResponseCode response;
313
314 mResponse.reset();
315
316 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
317 // FIXME - need to delete mSendObjectHandle from the database
Steve Block29357bc2012-01-06 19:20:56 +0000318 ALOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400319 mSendObjectHandle = kInvalidObjectHandle;
320 }
321
322 switch (operation) {
323 case MTP_OPERATION_GET_DEVICE_INFO:
324 response = doGetDeviceInfo();
325 break;
326 case MTP_OPERATION_OPEN_SESSION:
327 response = doOpenSession();
328 break;
329 case MTP_OPERATION_CLOSE_SESSION:
330 response = doCloseSession();
331 break;
332 case MTP_OPERATION_GET_STORAGE_IDS:
333 response = doGetStorageIDs();
334 break;
335 case MTP_OPERATION_GET_STORAGE_INFO:
336 response = doGetStorageInfo();
337 break;
338 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
339 response = doGetObjectPropsSupported();
340 break;
341 case MTP_OPERATION_GET_OBJECT_HANDLES:
342 response = doGetObjectHandles();
343 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400344 case MTP_OPERATION_GET_NUM_OBJECTS:
345 response = doGetNumObjects();
346 break;
Mike Lockwood438344f2010-08-03 15:30:09 -0400347 case MTP_OPERATION_GET_OBJECT_REFERENCES:
348 response = doGetObjectReferences();
349 break;
350 case MTP_OPERATION_SET_OBJECT_REFERENCES:
351 response = doSetObjectReferences();
352 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400353 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
354 response = doGetObjectPropValue();
355 break;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400356 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
357 response = doSetObjectPropValue();
358 break;
359 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
360 response = doGetDevicePropValue();
361 break;
362 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
363 response = doSetDevicePropValue();
364 break;
365 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
366 response = doResetDevicePropValue();
367 break;
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400368 case MTP_OPERATION_GET_OBJECT_PROP_LIST:
369 response = doGetObjectPropList();
370 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400371 case MTP_OPERATION_GET_OBJECT_INFO:
372 response = doGetObjectInfo();
373 break;
374 case MTP_OPERATION_GET_OBJECT:
375 response = doGetObject();
376 break;
Mike Lockwood64000782011-04-24 18:40:17 -0700377 case MTP_OPERATION_GET_THUMB:
378 response = doGetThumb();
379 break;
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500380 case MTP_OPERATION_GET_PARTIAL_OBJECT:
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700381 case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
382 response = doGetPartialObject(operation);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500383 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400384 case MTP_OPERATION_SEND_OBJECT_INFO:
385 response = doSendObjectInfo();
386 break;
387 case MTP_OPERATION_SEND_OBJECT:
388 response = doSendObject();
389 break;
390 case MTP_OPERATION_DELETE_OBJECT:
391 response = doDeleteObject();
392 break;
393 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400394 response = doGetObjectPropDesc();
395 break;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400396 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
397 response = doGetDevicePropDesc();
398 break;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700399 case MTP_OPERATION_SEND_PARTIAL_OBJECT:
400 response = doSendPartialObject();
401 break;
402 case MTP_OPERATION_TRUNCATE_OBJECT:
403 response = doTruncateObject();
404 break;
405 case MTP_OPERATION_BEGIN_EDIT_OBJECT:
406 response = doBeginEditObject();
407 break;
408 case MTP_OPERATION_END_EDIT_OBJECT:
409 response = doEndEditObject();
410 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400411 default:
Steve Block29357bc2012-01-06 19:20:56 +0000412 ALOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400413 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
414 break;
415 }
416
Mike Lockwood916076c2010-06-04 09:49:21 -0400417 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
418 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400419 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400420 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400421}
422
423MtpResponseCode MtpServer::doGetDeviceInfo() {
424 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700425 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400426
Mike Lockwood782aef12010-08-10 07:37:50 -0400427 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
428 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
429 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
430
Mike Lockwood16864ba2010-05-11 17:16:59 -0400431 // fill in device info
432 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood3d1d7762011-06-21 08:27:06 -0400433 if (mPtp) {
434 mData.putUInt32(0);
435 } else {
436 // MTP Vendor Extension ID
437 mData.putUInt32(6);
438 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400439 mData.putUInt16(MTP_STANDARD_VERSION);
Mike Lockwood3d1d7762011-06-21 08:27:06 -0400440 if (mPtp) {
441 // no extensions
442 string.set("");
443 } else {
444 // MTP extensions
445 string.set("microsoft.com: 1.0; android.com: 1.0;");
446 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400447 mData.putString(string); // MTP Extensions
448 mData.putUInt16(0); //Functional Mode
449 mData.putAUInt16(kSupportedOperationCodes,
450 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400451 mData.putAUInt16(kSupportedEventCodes,
452 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood782aef12010-08-10 07:37:50 -0400453 mData.putAUInt16(deviceProperties); // Device Properties Supported
454 mData.putAUInt16(captureFormats); // Capture Formats
455 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood8d08c5a2011-01-31 16:44:44 -0500456
457 property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
458 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400459 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700460
461 property_get("ro.product.model", prop_value, "MTP Device");
462 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400463 mData.putString(string); // Model
464 string.set("1.0");
465 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700466
467 property_get("ro.serialno", prop_value, "????????");
468 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400469 mData.putString(string); // Serial Number
470
Mike Lockwood782aef12010-08-10 07:37:50 -0400471 delete playbackFormats;
472 delete captureFormats;
473 delete deviceProperties;
474
Mike Lockwood16864ba2010-05-11 17:16:59 -0400475 return MTP_RESPONSE_OK;
476}
477
478MtpResponseCode MtpServer::doOpenSession() {
479 if (mSessionOpen) {
480 mResponse.setParameter(1, mSessionID);
481 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
482 }
483 mSessionID = mRequest.getParameter(1);
484 mSessionOpen = true;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400485
486 mDatabase->sessionStarted();
487
Mike Lockwood16864ba2010-05-11 17:16:59 -0400488 return MTP_RESPONSE_OK;
489}
490
491MtpResponseCode MtpServer::doCloseSession() {
492 if (!mSessionOpen)
493 return MTP_RESPONSE_SESSION_NOT_OPEN;
494 mSessionID = 0;
495 mSessionOpen = false;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400496 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400497 return MTP_RESPONSE_OK;
498}
499
500MtpResponseCode MtpServer::doGetStorageIDs() {
501 if (!mSessionOpen)
502 return MTP_RESPONSE_SESSION_NOT_OPEN;
503
504 int count = mStorages.size();
505 mData.putUInt32(count);
506 for (int i = 0; i < count; i++)
507 mData.putUInt32(mStorages[i]->getStorageID());
508
509 return MTP_RESPONSE_OK;
510}
511
512MtpResponseCode MtpServer::doGetStorageInfo() {
513 MtpStringBuffer string;
514
515 if (!mSessionOpen)
516 return MTP_RESPONSE_SESSION_NOT_OPEN;
517 MtpStorageID id = mRequest.getParameter(1);
518 MtpStorage* storage = getStorage(id);
519 if (!storage)
520 return MTP_RESPONSE_INVALID_STORAGE_ID;
521
522 mData.putUInt16(storage->getType());
523 mData.putUInt16(storage->getFileSystemType());
524 mData.putUInt16(storage->getAccessCapability());
525 mData.putUInt64(storage->getMaxCapacity());
526 mData.putUInt64(storage->getFreeSpace());
527 mData.putUInt32(1024*1024*1024); // Free Space in Objects
528 string.set(storage->getDescription());
529 mData.putString(string);
530 mData.putEmptyString(); // Volume Identifier
531
532 return MTP_RESPONSE_OK;
533}
534
535MtpResponseCode MtpServer::doGetObjectPropsSupported() {
536 if (!mSessionOpen)
537 return MTP_RESPONSE_SESSION_NOT_OPEN;
538 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood2e09e282010-12-07 10:51:20 -0800539 MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
Mike Lockwood782aef12010-08-10 07:37:50 -0400540 mData.putAUInt16(properties);
Mike Lockwoodbf9b2052010-08-10 15:11:32 -0400541 delete properties;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400542 return MTP_RESPONSE_OK;
543}
544
545MtpResponseCode MtpServer::doGetObjectHandles() {
546 if (!mSessionOpen)
547 return MTP_RESPONSE_SESSION_NOT_OPEN;
548 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400549 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400550 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
Mike Lockwooddc3185e2011-06-17 13:44:24 -0400551 // 0x00000000 for all objects
Mike Lockwooda8494402011-02-18 09:07:14 -0500552
553 if (!hasStorage(storageID))
554 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400555
556 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
557 mData.putAUInt32(handles);
558 delete handles;
559 return MTP_RESPONSE_OK;
560}
561
Mike Lockwood343af4e2010-08-02 10:52:20 -0400562MtpResponseCode MtpServer::doGetNumObjects() {
563 if (!mSessionOpen)
564 return MTP_RESPONSE_SESSION_NOT_OPEN;
565 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
566 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
567 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
Mike Lockwooddc3185e2011-06-17 13:44:24 -0400568 // 0x00000000 for all objects
Mike Lockwooda8494402011-02-18 09:07:14 -0500569 if (!hasStorage(storageID))
570 return MTP_RESPONSE_INVALID_STORAGE_ID;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400571
572 int count = mDatabase->getNumObjects(storageID, format, parent);
573 if (count >= 0) {
574 mResponse.setParameter(1, count);
575 return MTP_RESPONSE_OK;
576 } else {
577 mResponse.setParameter(1, 0);
578 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
579 }
580}
581
Mike Lockwood438344f2010-08-03 15:30:09 -0400582MtpResponseCode MtpServer::doGetObjectReferences() {
583 if (!mSessionOpen)
584 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwooda8494402011-02-18 09:07:14 -0500585 if (!hasStorage())
586 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
587 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400588
589 // FIXME - check for invalid object handle
Mike Lockwood438344f2010-08-03 15:30:09 -0400590 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400591 if (handles) {
592 mData.putAUInt32(handles);
593 delete handles;
594 } else {
Mike Lockwood438344f2010-08-03 15:30:09 -0400595 mData.putEmptyArray();
Mike Lockwood438344f2010-08-03 15:30:09 -0400596 }
Mike Lockwood438344f2010-08-03 15:30:09 -0400597 return MTP_RESPONSE_OK;
598}
599
600MtpResponseCode MtpServer::doSetObjectReferences() {
601 if (!mSessionOpen)
602 return MTP_RESPONSE_SESSION_NOT_OPEN;
Mike Lockwooda8494402011-02-18 09:07:14 -0500603 if (!hasStorage())
604 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood438344f2010-08-03 15:30:09 -0400605 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwooda8494402011-02-18 09:07:14 -0500606
Mike Lockwood438344f2010-08-03 15:30:09 -0400607 MtpObjectHandleList* references = mData.getAUInt32();
608 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
609 delete references;
610 return result;
611}
612
Mike Lockwood16864ba2010-05-11 17:16:59 -0400613MtpResponseCode MtpServer::doGetObjectPropValue() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500614 if (!hasStorage())
615 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400616 MtpObjectHandle handle = mRequest.getParameter(1);
617 MtpObjectProperty property = mRequest.getParameter(2);
Steve Block3856b092011-10-20 11:56:00 +0100618 ALOGV("GetObjectPropValue %d %s\n", handle,
Mike Lockwood8277cec2010-08-10 15:20:35 -0400619 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400620
Mike Lockwood8277cec2010-08-10 15:20:35 -0400621 return mDatabase->getObjectPropertyValue(handle, property, mData);
622}
623
624MtpResponseCode MtpServer::doSetObjectPropValue() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500625 if (!hasStorage())
626 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400627 MtpObjectHandle handle = mRequest.getParameter(1);
628 MtpObjectProperty property = mRequest.getParameter(2);
Steve Block3856b092011-10-20 11:56:00 +0100629 ALOGV("SetObjectPropValue %d %s\n", handle,
Mike Lockwood8277cec2010-08-10 15:20:35 -0400630 MtpDebug::getObjectPropCodeName(property));
631
632 return mDatabase->setObjectPropertyValue(handle, property, mData);
633}
634
635MtpResponseCode MtpServer::doGetDevicePropValue() {
636 MtpDeviceProperty property = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +0100637 ALOGV("GetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400638 MtpDebug::getDevicePropCodeName(property));
639
640 return mDatabase->getDevicePropertyValue(property, mData);
641}
642
643MtpResponseCode MtpServer::doSetDevicePropValue() {
644 MtpDeviceProperty property = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +0100645 ALOGV("SetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400646 MtpDebug::getDevicePropCodeName(property));
647
648 return mDatabase->setDevicePropertyValue(property, mData);
649}
650
651MtpResponseCode MtpServer::doResetDevicePropValue() {
652 MtpDeviceProperty property = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +0100653 ALOGV("ResetDevicePropValue %s\n",
Mike Lockwood8277cec2010-08-10 15:20:35 -0400654 MtpDebug::getDevicePropCodeName(property));
655
656 return mDatabase->resetDeviceProperty(property);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400657}
658
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400659MtpResponseCode MtpServer::doGetObjectPropList() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500660 if (!hasStorage())
661 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400662
663 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood40ce1f22010-12-01 18:46:23 -0500664 // use uint32_t so we can support 0xFFFFFFFF
665 uint32_t format = mRequest.getParameter(2);
666 uint32_t property = mRequest.getParameter(3);
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400667 int groupCode = mRequest.getParameter(4);
Mike Lockwoodf05ff072010-11-23 18:45:25 -0500668 int depth = mRequest.getParameter(5);
Steve Block3856b092011-10-20 11:56:00 +0100669 ALOGV("GetObjectPropList %d format: %s property: %s group: %d depth: %d\n",
Mike Lockwoodb6da06e2010-10-14 18:03:25 -0400670 handle, MtpDebug::getFormatCodeName(format),
671 MtpDebug::getObjectPropCodeName(property), groupCode, depth);
672
673 return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
674}
675
Mike Lockwood16864ba2010-05-11 17:16:59 -0400676MtpResponseCode MtpServer::doGetObjectInfo() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500677 if (!hasStorage())
678 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400679 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700680 MtpObjectInfo info(handle);
681 MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
682 if (result == MTP_RESPONSE_OK) {
683 char date[20];
684
685 mData.putUInt32(info.mStorageID);
686 mData.putUInt16(info.mFormat);
687 mData.putUInt16(info.mProtectionStatus);
688
689 // if object is being edited the database size may be out of date
690 uint32_t size = info.mCompressedSize;
691 ObjectEdit* edit = getEditObject(handle);
692 if (edit)
Mike Lockwoodc3f16e52011-04-25 12:56:21 -0700693 size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700694 mData.putUInt32(size);
695
696 mData.putUInt16(info.mThumbFormat);
697 mData.putUInt32(info.mThumbCompressedSize);
698 mData.putUInt32(info.mThumbPixWidth);
699 mData.putUInt32(info.mThumbPixHeight);
700 mData.putUInt32(info.mImagePixWidth);
701 mData.putUInt32(info.mImagePixHeight);
702 mData.putUInt32(info.mImagePixDepth);
703 mData.putUInt32(info.mParent);
704 mData.putUInt16(info.mAssociationType);
705 mData.putUInt32(info.mAssociationDesc);
706 mData.putUInt32(info.mSequenceNumber);
707 mData.putString(info.mName);
Mike Lockwoodcba6b7d2013-04-01 10:51:35 -0700708 formatDateTime(info.mDateCreated, date, sizeof(date));
709 mData.putString(date); // date created
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700710 formatDateTime(info.mDateModified, date, sizeof(date));
711 mData.putString(date); // date modified
712 mData.putEmptyString(); // keywords
713 }
714 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400715}
716
717MtpResponseCode MtpServer::doGetObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500718 if (!hasStorage())
719 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400720 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400721 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400722 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800723 MtpObjectFormat format;
724 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400725 if (result != MTP_RESPONSE_OK)
726 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400727
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400728 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400729 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400730 mfr.fd = open(filePath, O_RDONLY);
731 if (mfr.fd < 0) {
732 return MTP_RESPONSE_GENERAL_ERROR;
733 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400734 mfr.offset = 0;
735 mfr.length = fileLength;
Mike Lockwoodef441d92011-07-14 21:00:02 -0400736 mfr.command = mRequest.getOperationCode();
737 mfr.transaction_id = mRequest.getTransactionID();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400738
739 // then transfer the file
Mike Lockwoodef441d92011-07-14 21:00:02 -0400740 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +0100741 ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400742 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400743 if (ret < 0) {
744 if (errno == ECANCELED)
745 return MTP_RESPONSE_TRANSACTION_CANCELLED;
746 else
747 return MTP_RESPONSE_GENERAL_ERROR;
748 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400749 return MTP_RESPONSE_OK;
750}
751
Mike Lockwood64000782011-04-24 18:40:17 -0700752MtpResponseCode MtpServer::doGetThumb() {
753 MtpObjectHandle handle = mRequest.getParameter(1);
754 size_t thumbSize;
755 void* thumb = mDatabase->getThumbnail(handle, thumbSize);
756 if (thumb) {
757 // send data
758 mData.setOperationCode(mRequest.getOperationCode());
759 mData.setTransactionID(mRequest.getTransactionID());
760 mData.writeData(mFD, thumb, thumbSize);
761 free(thumb);
762 return MTP_RESPONSE_OK;
763 } else {
764 return MTP_RESPONSE_GENERAL_ERROR;
765 }
766}
767
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700768MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
Mike Lockwooda8494402011-02-18 09:07:14 -0500769 if (!hasStorage())
770 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500771 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -0700772 uint64_t offset;
773 uint32_t length;
774 offset = mRequest.getParameter(2);
775 if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
776 // android extension with 64 bit offset
777 uint64_t offset2 = mRequest.getParameter(3);
778 offset = offset | (offset2 << 32);
779 length = mRequest.getParameter(4);
780 } else {
781 // standard GetPartialObject
782 length = mRequest.getParameter(3);
783 }
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500784 MtpString pathBuf;
785 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800786 MtpObjectFormat format;
787 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500788 if (result != MTP_RESPONSE_OK)
789 return result;
790 if (offset + length > fileLength)
791 length = fileLength - offset;
792
793 const char* filePath = (const char *)pathBuf;
794 mtp_file_range mfr;
795 mfr.fd = open(filePath, O_RDONLY);
796 if (mfr.fd < 0) {
797 return MTP_RESPONSE_GENERAL_ERROR;
798 }
799 mfr.offset = offset;
800 mfr.length = length;
Mike Lockwoodef441d92011-07-14 21:00:02 -0400801 mfr.command = mRequest.getOperationCode();
802 mfr.transaction_id = mRequest.getTransactionID();
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500803 mResponse.setParameter(1, length);
804
Mike Lockwoodef441d92011-07-14 21:00:02 -0400805 // transfer the file
806 int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +0100807 ALOGV("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
Mike Lockwoodd81ce3c2010-11-23 09:08:01 -0500808 close(mfr.fd);
809 if (ret < 0) {
810 if (errno == ECANCELED)
811 return MTP_RESPONSE_TRANSACTION_CANCELLED;
812 else
813 return MTP_RESPONSE_GENERAL_ERROR;
814 }
815 return MTP_RESPONSE_OK;
816}
817
Mike Lockwood16864ba2010-05-11 17:16:59 -0400818MtpResponseCode MtpServer::doSendObjectInfo() {
819 MtpString path;
820 MtpStorageID storageID = mRequest.getParameter(1);
821 MtpStorage* storage = getStorage(storageID);
822 MtpObjectHandle parent = mRequest.getParameter(2);
823 if (!storage)
824 return MTP_RESPONSE_INVALID_STORAGE_ID;
825
826 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400827 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400828 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400829 parent = 0;
830 } else {
Mike Lockwoodfd346262010-12-08 16:08:01 -0800831 int64_t length;
832 MtpObjectFormat format;
833 int result = mDatabase->getObjectFilePath(parent, path, length, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400834 if (result != MTP_RESPONSE_OK)
835 return result;
Mike Lockwoodfd346262010-12-08 16:08:01 -0800836 if (format != MTP_FORMAT_ASSOCIATION)
837 return MTP_RESPONSE_INVALID_PARENT_OBJECT;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400838 }
839
840 // read only the fields we need
841 mData.getUInt32(); // storage ID
842 MtpObjectFormat format = mData.getUInt16();
843 mData.getUInt16(); // protection status
844 mSendObjectFileSize = mData.getUInt32();
845 mData.getUInt16(); // thumb format
846 mData.getUInt32(); // thumb compressed size
847 mData.getUInt32(); // thumb pix width
848 mData.getUInt32(); // thumb pix height
849 mData.getUInt32(); // image pix width
850 mData.getUInt32(); // image pix height
851 mData.getUInt32(); // image bit depth
852 mData.getUInt32(); // parent
853 uint16_t associationType = mData.getUInt16();
854 uint32_t associationDesc = mData.getUInt32(); // association desc
855 mData.getUInt32(); // sequence number
856 MtpStringBuffer name, created, modified;
857 mData.getString(name); // file name
858 mData.getString(created); // date created
859 mData.getString(modified); // date modified
860 // keywords follow
861
Steve Block3856b092011-10-20 11:56:00 +0100862 ALOGV("name: %s format: %04X\n", (const char *)name, format);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400863 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400864 if (!parseDateTime(modified, modifiedTime))
865 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400866
867 if (path[path.size() - 1] != '/')
868 path += "/";
869 path += (const char *)name;
870
Mike Lockwood20c3be02010-12-12 12:17:43 -0800871 // check space first
872 if (mSendObjectFileSize > storage->getFreeSpace())
873 return MTP_RESPONSE_STORAGE_FULL;
Mike Lockwood9b88b722011-07-11 09:18:03 -0400874 uint64_t maxFileSize = storage->getMaxFileSize();
875 // check storage max file size
876 if (maxFileSize != 0) {
877 // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
878 // is >= 0xFFFFFFFF
879 if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
880 return MTP_RESPONSE_OBJECT_TOO_LARGE;
881 }
Mike Lockwood20c3be02010-12-12 12:17:43 -0800882
Steve Blockb8a80522011-12-20 16:23:08 +0000883 ALOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
Mike Lockwood4714b072010-07-12 08:49:01 -0400884 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
885 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400886 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400887 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400888 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400889
890 if (format == MTP_FORMAT_ASSOCIATION) {
891 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400892 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400893 umask(mask);
894 if (ret && ret != -EEXIST)
895 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400896 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwoodaa952402011-01-18 11:06:19 -0800897
898 // SendObject does not get sent for directories, so call endSendObject here instead
899 mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400900 } else {
901 mSendObjectFilePath = path;
902 // save the handle for the SendObject call, which should follow
903 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400904 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400905 }
906
907 mResponse.setParameter(1, storageID);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400908 mResponse.setParameter(2, parent);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400909 mResponse.setParameter(3, handle);
910
911 return MTP_RESPONSE_OK;
912}
913
914MtpResponseCode MtpServer::doSendObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -0500915 if (!hasStorage())
916 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood4714b072010-07-12 08:49:01 -0400917 MtpResponseCode result = MTP_RESPONSE_OK;
918 mode_t mask;
Mike Lockwoodef441d92011-07-14 21:00:02 -0400919 int ret, initialData;
Mike Lockwood4714b072010-07-12 08:49:01 -0400920
Mike Lockwood16864ba2010-05-11 17:16:59 -0400921 if (mSendObjectHandle == kInvalidObjectHandle) {
Steve Block29357bc2012-01-06 19:20:56 +0000922 ALOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400923 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
924 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400925 }
926
Mike Lockwoodef441d92011-07-14 21:00:02 -0400927 // read the header, and possibly some data
928 ret = mData.read(mFD);
929 if (ret < MTP_CONTAINER_HEADER_SIZE) {
930 result = MTP_RESPONSE_GENERAL_ERROR;
931 goto done;
932 }
933 initialData = ret - MTP_CONTAINER_HEADER_SIZE;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400934
935 mtp_file_range mfr;
Nick Kralevichaf8e8aa2012-06-26 13:32:23 -0700936 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400937 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400938 result = MTP_RESPONSE_GENERAL_ERROR;
939 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400940 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400941 fchown(mfr.fd, getuid(), mFileGroup);
942 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400943 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400944 fchmod(mfr.fd, mFilePermission);
945 umask(mask);
946
Mike Lockwoodef441d92011-07-14 21:00:02 -0400947 if (initialData > 0)
948 ret = write(mfr.fd, mData.getData(), initialData);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400949
Mike Lockwoodef441d92011-07-14 21:00:02 -0400950 if (mSendObjectFileSize - initialData > 0) {
951 mfr.offset = initialData;
Mike Lockwood0cc79c62011-10-13 11:38:20 -0400952 if (mSendObjectFileSize == 0xFFFFFFFF) {
953 // tell driver to read until it receives a short packet
954 mfr.length = 0xFFFFFFFF;
955 } else {
956 mfr.length = mSendObjectFileSize - initialData;
957 }
Mike Lockwoodef441d92011-07-14 21:00:02 -0400958
Steve Block3856b092011-10-20 11:56:00 +0100959 ALOGV("receiving %s\n", (const char *)mSendObjectFilePath);
Mike Lockwoodef441d92011-07-14 21:00:02 -0400960 // transfer the file
961 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +0100962 ALOGV("MTP_RECEIVE_FILE returned %d\n", ret);
Mike Lockwoodef441d92011-07-14 21:00:02 -0400963 }
Mike Lockwoodc6588762010-06-22 15:03:53 -0400964 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400965
Mike Lockwood916076c2010-06-04 09:49:21 -0400966 if (ret < 0) {
967 unlink(mSendObjectFilePath);
968 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400969 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400970 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400971 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400972 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400973
974done:
Mike Lockwoodef441d92011-07-14 21:00:02 -0400975 // reset so we don't attempt to send the data back
976 mData.reset();
977
Mike Lockwood4714b072010-07-12 08:49:01 -0400978 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
Mike Lockwoodaa952402011-01-18 11:06:19 -0800979 result == MTP_RESPONSE_OK);
Mike Lockwood4714b072010-07-12 08:49:01 -0400980 mSendObjectHandle = kInvalidObjectHandle;
981 mSendObjectFormat = 0;
982 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400983}
984
Mike Lockwoodd3211492010-09-13 17:15:58 -0400985static void deleteRecursive(const char* path) {
986 char pathbuf[PATH_MAX];
987 int pathLength = strlen(path);
988 if (pathLength >= sizeof(pathbuf) - 1) {
Steve Block29357bc2012-01-06 19:20:56 +0000989 ALOGE("path too long: %s\n", path);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400990 }
991 strcpy(pathbuf, path);
992 if (pathbuf[pathLength - 1] != '/') {
993 pathbuf[pathLength++] = '/';
994 }
995 char* fileSpot = pathbuf + pathLength;
996 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
997
998 DIR* dir = opendir(path);
999 if (!dir) {
Steve Block29357bc2012-01-06 19:20:56 +00001000 ALOGE("opendir %s failed: %s", path, strerror(errno));
Mike Lockwoodd3211492010-09-13 17:15:58 -04001001 return;
1002 }
1003
1004 struct dirent* entry;
1005 while ((entry = readdir(dir))) {
1006 const char* name = entry->d_name;
1007
1008 // ignore "." and ".."
1009 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
1010 continue;
1011 }
1012
1013 int nameLength = strlen(name);
1014 if (nameLength > pathRemaining) {
Steve Block29357bc2012-01-06 19:20:56 +00001015 ALOGE("path %s/%s too long\n", path, name);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001016 continue;
1017 }
1018 strcpy(fileSpot, name);
1019
1020 int type = entry->d_type;
1021 if (entry->d_type == DT_DIR) {
1022 deleteRecursive(pathbuf);
1023 rmdir(pathbuf);
1024 } else {
1025 unlink(pathbuf);
1026 }
1027 }
Mike Lockwood7ce05cf2010-11-11 11:22:32 -05001028 closedir(dir);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001029}
1030
1031static void deletePath(const char* path) {
1032 struct stat statbuf;
1033 if (stat(path, &statbuf) == 0) {
1034 if (S_ISDIR(statbuf.st_mode)) {
1035 deleteRecursive(path);
1036 rmdir(path);
1037 } else {
1038 unlink(path);
1039 }
1040 } else {
Steve Block29357bc2012-01-06 19:20:56 +00001041 ALOGE("deletePath stat failed for %s: %s", path, strerror(errno));
Mike Lockwoodd3211492010-09-13 17:15:58 -04001042 }
1043}
1044
Mike Lockwood16864ba2010-05-11 17:16:59 -04001045MtpResponseCode MtpServer::doDeleteObject() {
Mike Lockwooda8494402011-02-18 09:07:14 -05001046 if (!hasStorage())
1047 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001048 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodd3211492010-09-13 17:15:58 -04001049 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood16864ba2010-05-11 17:16:59 -04001050 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
1051 // FIXME - implement deleting objects by format
Mike Lockwood16864ba2010-05-11 17:16:59 -04001052
1053 MtpString filePath;
1054 int64_t fileLength;
Mike Lockwoodfd346262010-12-08 16:08:01 -08001055 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -04001056 if (result == MTP_RESPONSE_OK) {
Steve Block3856b092011-10-20 11:56:00 +01001057 ALOGV("deleting %s", (const char *)filePath);
Mike Lockwooda9a46c12011-12-01 16:58:41 -05001058 result = mDatabase->deleteFile(handle);
1059 // Don't delete the actual files unless the database deletion is allowed
1060 if (result == MTP_RESPONSE_OK) {
1061 deletePath((const char *)filePath);
1062 }
Mike Lockwood9c04c4c2010-08-02 10:37:41 -04001063 }
Mike Lockwooda9a46c12011-12-01 16:58:41 -05001064
1065 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001066}
1067
1068MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001069 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -04001070 MtpObjectFormat format = mRequest.getParameter(2);
Steve Block3856b092011-10-20 11:56:00 +01001071 ALOGV("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
Mike Lockwood8277cec2010-08-10 15:20:35 -04001072 MtpDebug::getFormatCodeName(format));
1073 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001074 if (!property)
1075 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001076 property->write(mData);
Mike Lockwood8277cec2010-08-10 15:20:35 -04001077 delete property;
1078 return MTP_RESPONSE_OK;
1079}
1080
1081MtpResponseCode MtpServer::doGetDevicePropDesc() {
1082 MtpDeviceProperty propCode = mRequest.getParameter(1);
Steve Block3856b092011-10-20 11:56:00 +01001083 ALOGV("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
Mike Lockwood8277cec2010-08-10 15:20:35 -04001084 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
1085 if (!property)
1086 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
1087 property->write(mData);
1088 delete property;
Mike Lockwood21ef7d02010-06-30 17:00:35 -04001089 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -04001090}
Mike Lockwood7850ef92010-05-14 10:10:36 -04001091
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001092MtpResponseCode MtpServer::doSendPartialObject() {
1093 if (!hasStorage())
1094 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
1095 MtpObjectHandle handle = mRequest.getParameter(1);
1096 uint64_t offset = mRequest.getParameter(2);
1097 uint64_t offset2 = mRequest.getParameter(3);
1098 offset = offset | (offset2 << 32);
1099 uint32_t length = mRequest.getParameter(4);
1100
1101 ObjectEdit* edit = getEditObject(handle);
1102 if (!edit) {
Steve Block29357bc2012-01-06 19:20:56 +00001103 ALOGE("object not open for edit in doSendPartialObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001104 return MTP_RESPONSE_GENERAL_ERROR;
1105 }
1106
1107 // can't start writing past the end of the file
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001108 if (offset > edit->mSize) {
Steve Blockb8a80522011-12-20 16:23:08 +00001109 ALOGD("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001110 return MTP_RESPONSE_GENERAL_ERROR;
1111 }
1112
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001113 const char* filePath = (const char *)edit->mPath;
Mark Salyzyndb43b342014-04-04 14:47:28 -07001114 ALOGV("receiving partial %s %lld %" PRIu32 "\n", filePath, offset, length);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001115
Mike Lockwoodef441d92011-07-14 21:00:02 -04001116 // read the header, and possibly some data
1117 int ret = mData.read(mFD);
1118 if (ret < MTP_CONTAINER_HEADER_SIZE)
1119 return MTP_RESPONSE_GENERAL_ERROR;
1120 int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
1121
1122 if (initialData > 0) {
Mike Lockwoood38fb6292013-02-08 13:25:01 -08001123 ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
Mike Lockwoodef441d92011-07-14 21:00:02 -04001124 offset += initialData;
1125 length -= initialData;
1126 }
1127
1128 if (length > 0) {
1129 mtp_file_range mfr;
1130 mfr.fd = edit->mFD;
1131 mfr.offset = offset;
1132 mfr.length = length;
1133
1134 // transfer the file
1135 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Steve Block3856b092011-10-20 11:56:00 +01001136 ALOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwoodef441d92011-07-14 21:00:02 -04001137 }
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001138 if (ret < 0) {
1139 mResponse.setParameter(1, 0);
1140 if (errno == ECANCELED)
1141 return MTP_RESPONSE_TRANSACTION_CANCELLED;
1142 else
1143 return MTP_RESPONSE_GENERAL_ERROR;
1144 }
Mike Lockwoodef441d92011-07-14 21:00:02 -04001145
1146 // reset so we don't attempt to send this back
1147 mData.reset();
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001148 mResponse.setParameter(1, length);
1149 uint64_t end = offset + length;
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001150 if (end > edit->mSize) {
1151 edit->mSize = end;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001152 }
1153 return MTP_RESPONSE_OK;
1154}
1155
1156MtpResponseCode MtpServer::doTruncateObject() {
1157 MtpObjectHandle handle = mRequest.getParameter(1);
1158 ObjectEdit* edit = getEditObject(handle);
1159 if (!edit) {
Steve Block29357bc2012-01-06 19:20:56 +00001160 ALOGE("object not open for edit in doTruncateObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001161 return MTP_RESPONSE_GENERAL_ERROR;
1162 }
1163
1164 uint64_t offset = mRequest.getParameter(2);
1165 uint64_t offset2 = mRequest.getParameter(3);
1166 offset |= (offset2 << 32);
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001167 if (ftruncate(edit->mFD, offset) != 0) {
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001168 return MTP_RESPONSE_GENERAL_ERROR;
1169 } else {
Mike Lockwoodc3f16e52011-04-25 12:56:21 -07001170 edit->mSize = offset;
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001171 return MTP_RESPONSE_OK;
1172 }
1173}
1174
1175MtpResponseCode MtpServer::doBeginEditObject() {
1176 MtpObjectHandle handle = mRequest.getParameter(1);
1177 if (getEditObject(handle)) {
Steve Block29357bc2012-01-06 19:20:56 +00001178 ALOGE("object already open for edit in doBeginEditObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001179 return MTP_RESPONSE_GENERAL_ERROR;
1180 }
1181
1182 MtpString path;
1183 int64_t fileLength;
1184 MtpObjectFormat format;
1185 int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
1186 if (result != MTP_RESPONSE_OK)
1187 return result;
1188
1189 int fd = open((const char *)path, O_RDWR | O_EXCL);
1190 if (fd < 0) {
Steve Block29357bc2012-01-06 19:20:56 +00001191 ALOGE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001192 return MTP_RESPONSE_GENERAL_ERROR;
1193 }
1194
1195 addEditObject(handle, path, fileLength, format, fd);
1196 return MTP_RESPONSE_OK;
1197}
1198
1199MtpResponseCode MtpServer::doEndEditObject() {
1200 MtpObjectHandle handle = mRequest.getParameter(1);
1201 ObjectEdit* edit = getEditObject(handle);
1202 if (!edit) {
Steve Block29357bc2012-01-06 19:20:56 +00001203 ALOGE("object not open for edit in doEndEditObject");
Mike Lockwood7d77dcf2011-04-21 17:05:55 -07001204 return MTP_RESPONSE_GENERAL_ERROR;
1205 }
1206
1207 commitEdit(edit);
1208 removeEditObject(handle);
1209 return MTP_RESPONSE_OK;
1210}
1211
Mike Lockwood7850ef92010-05-14 10:10:36 -04001212} // namespace android