blob: f74f395e5f01117e161960adefb874a63a6b5886 [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 Lockwood21ef7d02010-06-30 17:00:35 -040033#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040034#include "MtpServer.h"
35#include "MtpStorage.h"
36#include "MtpStringBuffer.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040037
Mike Lockwood8065e202010-07-15 13:36:52 -040038#include <linux/usb/f_mtp.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040039
Mike Lockwood7850ef92010-05-14 10:10:36 -040040namespace android {
41
Mike Lockwood16864ba2010-05-11 17:16:59 -040042static const MtpOperationCode kSupportedOperationCodes[] = {
43 MTP_OPERATION_GET_DEVICE_INFO,
44 MTP_OPERATION_OPEN_SESSION,
45 MTP_OPERATION_CLOSE_SESSION,
46 MTP_OPERATION_GET_STORAGE_IDS,
47 MTP_OPERATION_GET_STORAGE_INFO,
48 MTP_OPERATION_GET_NUM_OBJECTS,
49 MTP_OPERATION_GET_OBJECT_HANDLES,
50 MTP_OPERATION_GET_OBJECT_INFO,
51 MTP_OPERATION_GET_OBJECT,
52// MTP_OPERATION_GET_THUMB,
53 MTP_OPERATION_DELETE_OBJECT,
54 MTP_OPERATION_SEND_OBJECT_INFO,
55 MTP_OPERATION_SEND_OBJECT,
56// MTP_OPERATION_INITIATE_CAPTURE,
57// MTP_OPERATION_FORMAT_STORE,
58// MTP_OPERATION_RESET_DEVICE,
59// MTP_OPERATION_SELF_TEST,
60// MTP_OPERATION_SET_OBJECT_PROTECTION,
61// MTP_OPERATION_POWER_DOWN,
Mike Lockwoode3e76c42010-09-02 14:57:30 -040062 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood8277cec2010-08-10 15:20:35 -040063 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
64 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
65 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040066// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
67// MTP_OPERATION_MOVE_OBJECT,
68// MTP_OPERATION_COPY_OBJECT,
69// MTP_OPERATION_GET_PARTIAL_OBJECT,
70// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
71 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
Mike Lockwood8277cec2010-08-10 15:20:35 -040072 MTP_OPERATION_GET_OBJECT_PROP_DESC,
Mike Lockwood677f5702010-09-23 23:04:28 -040073 MTP_OPERATION_GET_OBJECT_PROP_VALUE,
74 MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwood438344f2010-08-03 15:30:09 -040075 MTP_OPERATION_GET_OBJECT_REFERENCES,
76 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood16864ba2010-05-11 17:16:59 -040077// MTP_OPERATION_SKIP,
78};
79
Mike Lockwood873871f2010-07-12 18:54:16 -040080static const MtpEventCode kSupportedEventCodes[] = {
81 MTP_EVENT_OBJECT_ADDED,
82 MTP_EVENT_OBJECT_REMOVED,
83};
84
Mike Lockwood1865a5d2010-07-03 00:44:05 -040085MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwood8e2a2802010-07-02 15:15:07 -040086 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -040087 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -040088 mDatabase(database),
Mike Lockwood8e2a2802010-07-02 15:15:07 -040089 mFileGroup(fileGroup),
90 mFilePermission(filePerm),
91 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -040092 mSessionID(0),
93 mSessionOpen(false),
94 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -040095 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -040096 mSendObjectFileSize(0)
97{
Mike Lockwood16864ba2010-05-11 17:16:59 -040098}
99
100MtpServer::~MtpServer() {
101}
102
103void MtpServer::addStorage(const char* filePath) {
104 int index = mStorages.size() + 1;
105 index |= index << 16; // set high and low part to our index
106 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
107 addStorage(storage);
108}
109
110MtpStorage* MtpServer::getStorage(MtpStorageID id) {
111 for (int i = 0; i < mStorages.size(); i++) {
112 MtpStorage* storage = mStorages[i];
113 if (storage->getStorageID() == id)
114 return storage;
115 }
116 return NULL;
117}
118
Mike Lockwood16864ba2010-05-11 17:16:59 -0400119void MtpServer::run() {
120 int fd = mFD;
121
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400122 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400123
124 while (1) {
125 int ret = mRequest.read(fd);
126 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400127 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400128 if (errno == ECANCELED) {
129 // return to top of loop and wait for next command
130 continue;
131 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400132 break;
133 }
134 MtpOperationCode operation = mRequest.getOperationCode();
135 MtpTransactionID transaction = mRequest.getTransactionID();
136
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400137 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400138 mRequest.dump();
139
140 // FIXME need to generalize this
Mike Lockwood438344f2010-08-03 15:30:09 -0400141 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood8277cec2010-08-10 15:20:35 -0400142 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
143 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
144 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400145 if (dataIn) {
146 int ret = mData.read(fd);
147 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400148 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400149 if (errno == ECANCELED) {
150 // return to top of loop and wait for next command
151 continue;
152 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400153 break;
154 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400155 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400156 mData.dump();
157 } else {
158 mData.reset();
159 }
160
Mike Lockwood916076c2010-06-04 09:49:21 -0400161 if (handleRequest()) {
162 if (!dataIn && mData.hasData()) {
163 mData.setOperationCode(operation);
164 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400165 LOGV("sending data:");
Mike Lockwood23d20712010-10-11 17:31:44 -0400166 mData.dump();
Mike Lockwood916076c2010-06-04 09:49:21 -0400167 ret = mData.write(fd);
168 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400169 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400170 if (errno == ECANCELED) {
171 // return to top of loop and wait for next command
172 continue;
173 }
174 break;
175 }
176 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400177
Mike Lockwood916076c2010-06-04 09:49:21 -0400178 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400179 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400180 ret = mResponse.write(fd);
Mike Lockwood23d20712010-10-11 17:31:44 -0400181 mResponse.dump();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400182 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400183 LOGE("request write 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 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400190 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400191 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400192 }
193 }
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400194
195 if (mSessionOpen)
196 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400197}
198
Mike Lockwood873871f2010-07-12 18:54:16 -0400199void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400200 if (mSessionOpen) {
201 LOGD("sendObjectAdded %d\n", handle);
202 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
203 mEvent.setTransactionID(mRequest.getTransactionID());
204 mEvent.setParameter(1, handle);
205 int ret = mEvent.write(mFD);
206 LOGD("mEvent.write returned %d\n", ret);
207 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400208}
209
210void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400211 if (mSessionOpen) {
212 LOGD("sendObjectRemoved %d\n", handle);
213 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
214 mEvent.setTransactionID(mRequest.getTransactionID());
215 mEvent.setParameter(1, handle);
216 int ret = mEvent.write(mFD);
217 LOGD("mEvent.write returned %d\n", ret);
218 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400219}
220
Mike Lockwood916076c2010-06-04 09:49:21 -0400221bool MtpServer::handleRequest() {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400222 MtpOperationCode operation = mRequest.getOperationCode();
223 MtpResponseCode response;
224
225 mResponse.reset();
226
227 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
228 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400229 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400230 mSendObjectHandle = kInvalidObjectHandle;
231 }
232
233 switch (operation) {
234 case MTP_OPERATION_GET_DEVICE_INFO:
235 response = doGetDeviceInfo();
236 break;
237 case MTP_OPERATION_OPEN_SESSION:
238 response = doOpenSession();
239 break;
240 case MTP_OPERATION_CLOSE_SESSION:
241 response = doCloseSession();
242 break;
243 case MTP_OPERATION_GET_STORAGE_IDS:
244 response = doGetStorageIDs();
245 break;
246 case MTP_OPERATION_GET_STORAGE_INFO:
247 response = doGetStorageInfo();
248 break;
249 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
250 response = doGetObjectPropsSupported();
251 break;
252 case MTP_OPERATION_GET_OBJECT_HANDLES:
253 response = doGetObjectHandles();
254 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400255 case MTP_OPERATION_GET_NUM_OBJECTS:
256 response = doGetNumObjects();
257 break;
Mike Lockwood438344f2010-08-03 15:30:09 -0400258 case MTP_OPERATION_GET_OBJECT_REFERENCES:
259 response = doGetObjectReferences();
260 break;
261 case MTP_OPERATION_SET_OBJECT_REFERENCES:
262 response = doSetObjectReferences();
263 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400264 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
265 response = doGetObjectPropValue();
266 break;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400267 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
268 response = doSetObjectPropValue();
269 break;
270 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
271 response = doGetDevicePropValue();
272 break;
273 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
274 response = doSetDevicePropValue();
275 break;
276 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
277 response = doResetDevicePropValue();
278 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400279 case MTP_OPERATION_GET_OBJECT_INFO:
280 response = doGetObjectInfo();
281 break;
282 case MTP_OPERATION_GET_OBJECT:
283 response = doGetObject();
284 break;
285 case MTP_OPERATION_SEND_OBJECT_INFO:
286 response = doSendObjectInfo();
287 break;
288 case MTP_OPERATION_SEND_OBJECT:
289 response = doSendObject();
290 break;
291 case MTP_OPERATION_DELETE_OBJECT:
292 response = doDeleteObject();
293 break;
294 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400295 response = doGetObjectPropDesc();
296 break;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400297 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
298 response = doGetDevicePropDesc();
299 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400300 default:
Mike Lockwooda881b442010-09-23 22:32:05 -0400301 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400302 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
303 break;
304 }
305
Mike Lockwood916076c2010-06-04 09:49:21 -0400306 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
307 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400308 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400309 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400310}
311
312MtpResponseCode MtpServer::doGetDeviceInfo() {
313 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700314 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400315
Mike Lockwood782aef12010-08-10 07:37:50 -0400316 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
317 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
318 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
319
Mike Lockwood16864ba2010-05-11 17:16:59 -0400320 // fill in device info
321 mData.putUInt16(MTP_STANDARD_VERSION);
322 mData.putUInt32(6); // MTP Vendor Extension ID
323 mData.putUInt16(MTP_STANDARD_VERSION);
324 string.set("microsoft.com: 1.0;");
325 mData.putString(string); // MTP Extensions
326 mData.putUInt16(0); //Functional Mode
327 mData.putAUInt16(kSupportedOperationCodes,
328 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400329 mData.putAUInt16(kSupportedEventCodes,
330 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood782aef12010-08-10 07:37:50 -0400331 mData.putAUInt16(deviceProperties); // Device Properties Supported
332 mData.putAUInt16(captureFormats); // Capture Formats
333 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400334 // FIXME
335 string.set("Google, Inc.");
336 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700337
338 property_get("ro.product.model", prop_value, "MTP Device");
339 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400340 mData.putString(string); // Model
341 string.set("1.0");
342 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700343
344 property_get("ro.serialno", prop_value, "????????");
345 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400346 mData.putString(string); // Serial Number
347
Mike Lockwood782aef12010-08-10 07:37:50 -0400348 delete playbackFormats;
349 delete captureFormats;
350 delete deviceProperties;
351
Mike Lockwood16864ba2010-05-11 17:16:59 -0400352 return MTP_RESPONSE_OK;
353}
354
355MtpResponseCode MtpServer::doOpenSession() {
356 if (mSessionOpen) {
357 mResponse.setParameter(1, mSessionID);
358 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
359 }
360 mSessionID = mRequest.getParameter(1);
361 mSessionOpen = true;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400362
363 mDatabase->sessionStarted();
364
Mike Lockwood16864ba2010-05-11 17:16:59 -0400365 return MTP_RESPONSE_OK;
366}
367
368MtpResponseCode MtpServer::doCloseSession() {
369 if (!mSessionOpen)
370 return MTP_RESPONSE_SESSION_NOT_OPEN;
371 mSessionID = 0;
372 mSessionOpen = false;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400373 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400374 return MTP_RESPONSE_OK;
375}
376
377MtpResponseCode MtpServer::doGetStorageIDs() {
378 if (!mSessionOpen)
379 return MTP_RESPONSE_SESSION_NOT_OPEN;
380
381 int count = mStorages.size();
382 mData.putUInt32(count);
383 for (int i = 0; i < count; i++)
384 mData.putUInt32(mStorages[i]->getStorageID());
385
386 return MTP_RESPONSE_OK;
387}
388
389MtpResponseCode MtpServer::doGetStorageInfo() {
390 MtpStringBuffer string;
391
392 if (!mSessionOpen)
393 return MTP_RESPONSE_SESSION_NOT_OPEN;
394 MtpStorageID id = mRequest.getParameter(1);
395 MtpStorage* storage = getStorage(id);
396 if (!storage)
397 return MTP_RESPONSE_INVALID_STORAGE_ID;
398
399 mData.putUInt16(storage->getType());
400 mData.putUInt16(storage->getFileSystemType());
401 mData.putUInt16(storage->getAccessCapability());
402 mData.putUInt64(storage->getMaxCapacity());
403 mData.putUInt64(storage->getFreeSpace());
404 mData.putUInt32(1024*1024*1024); // Free Space in Objects
405 string.set(storage->getDescription());
406 mData.putString(string);
407 mData.putEmptyString(); // Volume Identifier
408
409 return MTP_RESPONSE_OK;
410}
411
412MtpResponseCode MtpServer::doGetObjectPropsSupported() {
413 if (!mSessionOpen)
414 return MTP_RESPONSE_SESSION_NOT_OPEN;
415 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood782aef12010-08-10 07:37:50 -0400416 MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
417 mData.putAUInt16(properties);
Mike Lockwoodbf9b2052010-08-10 15:11:32 -0400418 delete properties;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400419 return MTP_RESPONSE_OK;
420}
421
422MtpResponseCode MtpServer::doGetObjectHandles() {
423 if (!mSessionOpen)
424 return MTP_RESPONSE_SESSION_NOT_OPEN;
425 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400426 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400427 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
428 // 0x00000000 for all objects?
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400429 if (parent == 0xFFFFFFFF)
430 parent = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400431
432 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
433 mData.putAUInt32(handles);
434 delete handles;
435 return MTP_RESPONSE_OK;
436}
437
Mike Lockwood343af4e2010-08-02 10:52:20 -0400438MtpResponseCode MtpServer::doGetNumObjects() {
439 if (!mSessionOpen)
440 return MTP_RESPONSE_SESSION_NOT_OPEN;
441 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
442 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
443 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
444 // 0x00000000 for all objects?
445 if (parent == 0xFFFFFFFF)
446 parent = 0;
447
448 int count = mDatabase->getNumObjects(storageID, format, parent);
449 if (count >= 0) {
450 mResponse.setParameter(1, count);
451 return MTP_RESPONSE_OK;
452 } else {
453 mResponse.setParameter(1, 0);
454 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
455 }
456}
457
Mike Lockwood438344f2010-08-03 15:30:09 -0400458MtpResponseCode MtpServer::doGetObjectReferences() {
459 if (!mSessionOpen)
460 return MTP_RESPONSE_SESSION_NOT_OPEN;
461 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400462
463 // FIXME - check for invalid object handle
Mike Lockwood438344f2010-08-03 15:30:09 -0400464 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400465 if (handles) {
466 mData.putAUInt32(handles);
467 delete handles;
468 } else {
Mike Lockwood438344f2010-08-03 15:30:09 -0400469 mData.putEmptyArray();
Mike Lockwood438344f2010-08-03 15:30:09 -0400470 }
Mike Lockwood438344f2010-08-03 15:30:09 -0400471 return MTP_RESPONSE_OK;
472}
473
474MtpResponseCode MtpServer::doSetObjectReferences() {
475 if (!mSessionOpen)
476 return MTP_RESPONSE_SESSION_NOT_OPEN;
477 MtpStorageID handle = mRequest.getParameter(1);
478 MtpObjectHandleList* references = mData.getAUInt32();
479 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
480 delete references;
481 return result;
482}
483
Mike Lockwood16864ba2010-05-11 17:16:59 -0400484MtpResponseCode MtpServer::doGetObjectPropValue() {
485 MtpObjectHandle handle = mRequest.getParameter(1);
486 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400487 LOGD("GetObjectPropValue %d %s\n", handle,
488 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400489
Mike Lockwood8277cec2010-08-10 15:20:35 -0400490 return mDatabase->getObjectPropertyValue(handle, property, mData);
491}
492
493MtpResponseCode MtpServer::doSetObjectPropValue() {
494 MtpObjectHandle handle = mRequest.getParameter(1);
495 MtpObjectProperty property = mRequest.getParameter(2);
496 LOGD("SetObjectPropValue %d %s\n", handle,
497 MtpDebug::getObjectPropCodeName(property));
498
499 return mDatabase->setObjectPropertyValue(handle, property, mData);
500}
501
502MtpResponseCode MtpServer::doGetDevicePropValue() {
503 MtpDeviceProperty property = mRequest.getParameter(1);
504 LOGD("GetDevicePropValue %s\n",
505 MtpDebug::getDevicePropCodeName(property));
506
507 return mDatabase->getDevicePropertyValue(property, mData);
508}
509
510MtpResponseCode MtpServer::doSetDevicePropValue() {
511 MtpDeviceProperty property = mRequest.getParameter(1);
512 LOGD("SetDevicePropValue %s\n",
513 MtpDebug::getDevicePropCodeName(property));
514
515 return mDatabase->setDevicePropertyValue(property, mData);
516}
517
518MtpResponseCode MtpServer::doResetDevicePropValue() {
519 MtpDeviceProperty property = mRequest.getParameter(1);
520 LOGD("ResetDevicePropValue %s\n",
521 MtpDebug::getDevicePropCodeName(property));
522
523 return mDatabase->resetDeviceProperty(property);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400524}
525
526MtpResponseCode MtpServer::doGetObjectInfo() {
527 MtpObjectHandle handle = mRequest.getParameter(1);
528 return mDatabase->getObjectInfo(handle, mData);
529}
530
531MtpResponseCode MtpServer::doGetObject() {
532 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400533 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400534 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400535 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
536 if (result != MTP_RESPONSE_OK)
537 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400538
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400539 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400540 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400541 mfr.fd = open(filePath, O_RDONLY);
542 if (mfr.fd < 0) {
543 return MTP_RESPONSE_GENERAL_ERROR;
544 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400545 mfr.offset = 0;
546 mfr.length = fileLength;
547
548 // send data header
549 mData.setOperationCode(mRequest.getOperationCode());
550 mData.setTransactionID(mRequest.getTransactionID());
Mike Lockwood23d20712010-10-11 17:31:44 -0400551 mData.writeDataHeader(mFD, fileLength + MTP_CONTAINER_HEADER_SIZE);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400552
553 // then transfer the file
554 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400555 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400556 if (ret < 0) {
557 if (errno == ECANCELED)
558 return MTP_RESPONSE_TRANSACTION_CANCELLED;
559 else
560 return MTP_RESPONSE_GENERAL_ERROR;
561 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400562 return MTP_RESPONSE_OK;
563}
564
565MtpResponseCode MtpServer::doSendObjectInfo() {
566 MtpString path;
567 MtpStorageID storageID = mRequest.getParameter(1);
568 MtpStorage* storage = getStorage(storageID);
569 MtpObjectHandle parent = mRequest.getParameter(2);
570 if (!storage)
571 return MTP_RESPONSE_INVALID_STORAGE_ID;
572
573 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400574 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400575 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400576 parent = 0;
577 } else {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400578 int64_t dummy;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400579 int result = mDatabase->getObjectFilePath(parent, path, dummy);
580 if (result != MTP_RESPONSE_OK)
581 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400582 }
583
584 // read only the fields we need
585 mData.getUInt32(); // storage ID
586 MtpObjectFormat format = mData.getUInt16();
587 mData.getUInt16(); // protection status
588 mSendObjectFileSize = mData.getUInt32();
589 mData.getUInt16(); // thumb format
590 mData.getUInt32(); // thumb compressed size
591 mData.getUInt32(); // thumb pix width
592 mData.getUInt32(); // thumb pix height
593 mData.getUInt32(); // image pix width
594 mData.getUInt32(); // image pix height
595 mData.getUInt32(); // image bit depth
596 mData.getUInt32(); // parent
597 uint16_t associationType = mData.getUInt16();
598 uint32_t associationDesc = mData.getUInt32(); // association desc
599 mData.getUInt32(); // sequence number
600 MtpStringBuffer name, created, modified;
601 mData.getString(name); // file name
602 mData.getString(created); // date created
603 mData.getString(modified); // date modified
604 // keywords follow
605
Mike Lockwoodfceef462010-05-14 15:35:17 -0400606 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400607 if (!parseDateTime(modified, modifiedTime))
608 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400609
610 if (path[path.size() - 1] != '/')
611 path += "/";
612 path += (const char *)name;
613
Mike Lockwood4714b072010-07-12 08:49:01 -0400614 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
615 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400616 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400617 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400618 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400619
620 if (format == MTP_FORMAT_ASSOCIATION) {
621 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400622 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400623 umask(mask);
624 if (ret && ret != -EEXIST)
625 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400626 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400627 } else {
628 mSendObjectFilePath = path;
629 // save the handle for the SendObject call, which should follow
630 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400631 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400632 }
633
634 mResponse.setParameter(1, storageID);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400635 mResponse.setParameter(2, parent);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400636 mResponse.setParameter(3, handle);
637
638 return MTP_RESPONSE_OK;
639}
640
641MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood4714b072010-07-12 08:49:01 -0400642 MtpResponseCode result = MTP_RESPONSE_OK;
643 mode_t mask;
644 int ret;
645
Mike Lockwood16864ba2010-05-11 17:16:59 -0400646 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400647 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400648 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
649 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400650 }
651
652 // read the header
Mike Lockwood4714b072010-07-12 08:49:01 -0400653 ret = mData.readDataHeader(mFD);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400654 // FIXME - check for errors here.
655
656 // reset so we don't attempt to send this back
657 mData.reset();
658
659 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400660 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
661 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400662 result = MTP_RESPONSE_GENERAL_ERROR;
663 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400664 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400665 fchown(mfr.fd, getuid(), mFileGroup);
666 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400667 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400668 fchmod(mfr.fd, mFilePermission);
669 umask(mask);
670
Mike Lockwood16864ba2010-05-11 17:16:59 -0400671 mfr.offset = 0;
672 mfr.length = mSendObjectFileSize;
673
674 // transfer the file
675 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400676 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400677
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400678 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400679
Mike Lockwood916076c2010-06-04 09:49:21 -0400680 if (ret < 0) {
681 unlink(mSendObjectFilePath);
682 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400683 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400684 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400685 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400686 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400687
688done:
689 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
690 result == MTP_RESPONSE_OK);
691 mSendObjectHandle = kInvalidObjectHandle;
692 mSendObjectFormat = 0;
693 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400694}
695
Mike Lockwoodd3211492010-09-13 17:15:58 -0400696static void deleteRecursive(const char* path) {
697 char pathbuf[PATH_MAX];
698 int pathLength = strlen(path);
699 if (pathLength >= sizeof(pathbuf) - 1) {
700 LOGE("path too long: %s\n", path);
701 }
702 strcpy(pathbuf, path);
703 if (pathbuf[pathLength - 1] != '/') {
704 pathbuf[pathLength++] = '/';
705 }
706 char* fileSpot = pathbuf + pathLength;
707 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
708
709 DIR* dir = opendir(path);
710 if (!dir) {
711 LOGE("opendir %s failed: %s", path, strerror(errno));
712 return;
713 }
714
715 struct dirent* entry;
716 while ((entry = readdir(dir))) {
717 const char* name = entry->d_name;
718
719 // ignore "." and ".."
720 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
721 continue;
722 }
723
724 int nameLength = strlen(name);
725 if (nameLength > pathRemaining) {
726 LOGE("path %s/%s too long\n", path, name);
727 continue;
728 }
729 strcpy(fileSpot, name);
730
731 int type = entry->d_type;
732 if (entry->d_type == DT_DIR) {
733 deleteRecursive(pathbuf);
734 rmdir(pathbuf);
735 } else {
736 unlink(pathbuf);
737 }
738 }
739}
740
741static void deletePath(const char* path) {
742 struct stat statbuf;
743 if (stat(path, &statbuf) == 0) {
744 if (S_ISDIR(statbuf.st_mode)) {
745 deleteRecursive(path);
746 rmdir(path);
747 } else {
748 unlink(path);
749 }
750 } else {
751 LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
752 }
753}
754
Mike Lockwood16864ba2010-05-11 17:16:59 -0400755MtpResponseCode MtpServer::doDeleteObject() {
756 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400757 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400758 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
759 // FIXME - implement deleting objects by format
Mike Lockwood16864ba2010-05-11 17:16:59 -0400760
761 MtpString filePath;
762 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400763 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
764 if (result == MTP_RESPONSE_OK) {
765 LOGV("deleting %s", (const char *)filePath);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400766 deletePath((const char *)filePath);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400767 return mDatabase->deleteFile(handle);
768 } else {
769 return result;
770 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400771}
772
773MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400774 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400775 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400776 LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
777 MtpDebug::getFormatCodeName(format));
778 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400779 if (!property)
780 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400781 property->write(mData);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400782 delete property;
783 return MTP_RESPONSE_OK;
784}
785
786MtpResponseCode MtpServer::doGetDevicePropDesc() {
787 MtpDeviceProperty propCode = mRequest.getParameter(1);
788 LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
789 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
790 if (!property)
791 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
792 property->write(mData);
793 delete property;
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400794 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400795}
Mike Lockwood7850ef92010-05-14 10:10:36 -0400796
797} // namespace android