blob: 1a18c3bb87adf0b20812cb0914747d94722581d4 [file] [log] [blame]
Mike Lockwood56118b52010-05-11 17:16:59 -04001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <sys/types.h>
20#include <sys/ioctl.h>
21#include <sys/stat.h>
22#include <fcntl.h>
23#include <errno.h>
Mike Lockwoodccb6e962010-09-13 17:15:58 -040024#include <sys/stat.h>
25#include <dirent.h>
Mike Lockwood56118b52010-05-11 17:16:59 -040026
Mike Lockwood622ccdc2010-06-14 17:58:08 -070027#include <cutils/properties.h>
28
Mike Lockwood9f679242010-09-23 22:32:05 -040029#define LOG_TAG "MtpServer"
30
Mike Lockwood56118b52010-05-11 17:16:59 -040031#include "MtpDebug.h"
Mike Lockwoodc5c78532010-07-09 10:45:22 -040032#include "MtpDatabase.h"
Mike Lockwood767c5e42010-06-30 17:00:35 -040033#include "MtpProperty.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040034#include "MtpServer.h"
35#include "MtpStorage.h"
36#include "MtpStringBuffer.h"
Mike Lockwood56118b52010-05-11 17:16:59 -040037
Mike Lockwood9c7fdf52010-07-15 13:36:52 -040038#include <linux/usb/f_mtp.h>
Mike Lockwood56118b52010-05-11 17:16:59 -040039
Mike Lockwood8d3257a2010-05-14 10:10:36 -040040namespace android {
41
Mike Lockwood56118b52010-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 Lockwood59e3f0d2010-09-02 14:57:30 -040062 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood828d19d2010-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 Lockwood56118b52010-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 Lockwood828d19d2010-08-10 15:20:35 -040072 MTP_OPERATION_GET_OBJECT_PROP_DESC,
73// MTP_OPERATION_GET_OBJECT_PROP_VALUE,
Mike Lockwood7a047c82010-08-02 10:52:20 -040074// MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwood9a2046f2010-08-03 15:30:09 -040075 MTP_OPERATION_GET_OBJECT_REFERENCES,
76 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood56118b52010-05-11 17:16:59 -040077// MTP_OPERATION_SKIP,
78};
79
Mike Lockwoodbe125a52010-07-12 18:54:16 -040080static const MtpEventCode kSupportedEventCodes[] = {
81 MTP_EVENT_OBJECT_ADDED,
82 MTP_EVENT_OBJECT_REMOVED,
83};
84
Mike Lockwoodd21eac92010-07-03 00:44:05 -040085MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwooddad69272010-07-02 15:15:07 -040086 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood56118b52010-05-11 17:16:59 -040087 : mFD(fd),
Mike Lockwoodd21eac92010-07-03 00:44:05 -040088 mDatabase(database),
Mike Lockwooddad69272010-07-02 15:15:07 -040089 mFileGroup(fileGroup),
90 mFilePermission(filePerm),
91 mDirectoryPermission(directoryPerm),
Mike Lockwood56118b52010-05-11 17:16:59 -040092 mSessionID(0),
93 mSessionOpen(false),
94 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwoodd815f792010-07-12 08:49:01 -040095 mSendObjectFormat(0),
Mike Lockwood56118b52010-05-11 17:16:59 -040096 mSendObjectFileSize(0)
97{
Mike Lockwood56118b52010-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 Lockwood56118b52010-05-11 17:16:59 -0400119void MtpServer::run() {
120 int fd = mFD;
121
Mike Lockwood767c5e42010-06-30 17:00:35 -0400122 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400123
124 while (1) {
125 int ret = mRequest.read(fd);
126 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400127 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400128 if (errno == ECANCELED) {
129 // return to top of loop and wait for next command
130 continue;
131 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400132 break;
133 }
134 MtpOperationCode operation = mRequest.getOperationCode();
135 MtpTransactionID transaction = mRequest.getTransactionID();
136
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400137 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400138 mRequest.dump();
139
140 // FIXME need to generalize this
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400141 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood828d19d2010-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 Lockwood56118b52010-05-11 17:16:59 -0400145 if (dataIn) {
146 int ret = mData.read(fd);
147 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400148 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400149 if (errno == ECANCELED) {
150 // return to top of loop and wait for next command
151 continue;
152 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400153 break;
154 }
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400155 LOGV("received data:");
Mike Lockwood56118b52010-05-11 17:16:59 -0400156 mData.dump();
157 } else {
158 mData.reset();
159 }
160
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400161 if (handleRequest()) {
162 if (!dataIn && mData.hasData()) {
163 mData.setOperationCode(operation);
164 mData.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400165 LOGV("sending data:");
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400166 ret = mData.write(fd);
167 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400168 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400169 if (errno == ECANCELED) {
170 // return to top of loop and wait for next command
171 continue;
172 }
173 break;
174 }
175 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400176
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400177 mResponse.setTransactionID(transaction);
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400178 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400179 ret = mResponse.write(fd);
Mike Lockwood56118b52010-05-11 17:16:59 -0400180 if (ret < 0) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400181 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400182 if (errno == ECANCELED) {
183 // return to top of loop and wait for next command
184 continue;
185 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400186 break;
187 }
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400188 } else {
Mike Lockwood767c5e42010-06-30 17:00:35 -0400189 LOGV("skipping response\n");
Mike Lockwood56118b52010-05-11 17:16:59 -0400190 }
191 }
Mike Lockwood2837eef2010-08-31 16:25:12 -0400192
193 if (mSessionOpen)
194 mDatabase->sessionEnded();
Mike Lockwood56118b52010-05-11 17:16:59 -0400195}
196
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400197void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwooddc453d42010-07-19 14:29:58 -0400198 if (mSessionOpen) {
199 LOGD("sendObjectAdded %d\n", handle);
200 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
201 mEvent.setTransactionID(mRequest.getTransactionID());
202 mEvent.setParameter(1, handle);
203 int ret = mEvent.write(mFD);
204 LOGD("mEvent.write returned %d\n", ret);
205 }
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400206}
207
208void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwooddc453d42010-07-19 14:29:58 -0400209 if (mSessionOpen) {
210 LOGD("sendObjectRemoved %d\n", handle);
211 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
212 mEvent.setTransactionID(mRequest.getTransactionID());
213 mEvent.setParameter(1, handle);
214 int ret = mEvent.write(mFD);
215 LOGD("mEvent.write returned %d\n", ret);
216 }
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400217}
218
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400219bool MtpServer::handleRequest() {
Mike Lockwood56118b52010-05-11 17:16:59 -0400220 MtpOperationCode operation = mRequest.getOperationCode();
221 MtpResponseCode response;
222
223 mResponse.reset();
224
225 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
226 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400227 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood56118b52010-05-11 17:16:59 -0400228 mSendObjectHandle = kInvalidObjectHandle;
229 }
230
231 switch (operation) {
232 case MTP_OPERATION_GET_DEVICE_INFO:
233 response = doGetDeviceInfo();
234 break;
235 case MTP_OPERATION_OPEN_SESSION:
236 response = doOpenSession();
237 break;
238 case MTP_OPERATION_CLOSE_SESSION:
239 response = doCloseSession();
240 break;
241 case MTP_OPERATION_GET_STORAGE_IDS:
242 response = doGetStorageIDs();
243 break;
244 case MTP_OPERATION_GET_STORAGE_INFO:
245 response = doGetStorageInfo();
246 break;
247 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
248 response = doGetObjectPropsSupported();
249 break;
250 case MTP_OPERATION_GET_OBJECT_HANDLES:
251 response = doGetObjectHandles();
252 break;
Mike Lockwood7a047c82010-08-02 10:52:20 -0400253 case MTP_OPERATION_GET_NUM_OBJECTS:
254 response = doGetNumObjects();
255 break;
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400256 case MTP_OPERATION_GET_OBJECT_REFERENCES:
257 response = doGetObjectReferences();
258 break;
259 case MTP_OPERATION_SET_OBJECT_REFERENCES:
260 response = doSetObjectReferences();
261 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400262 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
263 response = doGetObjectPropValue();
264 break;
Mike Lockwood828d19d2010-08-10 15:20:35 -0400265 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
266 response = doSetObjectPropValue();
267 break;
268 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
269 response = doGetDevicePropValue();
270 break;
271 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
272 response = doSetDevicePropValue();
273 break;
274 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
275 response = doResetDevicePropValue();
276 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400277 case MTP_OPERATION_GET_OBJECT_INFO:
278 response = doGetObjectInfo();
279 break;
280 case MTP_OPERATION_GET_OBJECT:
281 response = doGetObject();
282 break;
283 case MTP_OPERATION_SEND_OBJECT_INFO:
284 response = doSendObjectInfo();
285 break;
286 case MTP_OPERATION_SEND_OBJECT:
287 response = doSendObject();
288 break;
289 case MTP_OPERATION_DELETE_OBJECT:
290 response = doDeleteObject();
291 break;
292 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood767c5e42010-06-30 17:00:35 -0400293 response = doGetObjectPropDesc();
294 break;
Mike Lockwood59e3f0d2010-09-02 14:57:30 -0400295 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
296 response = doGetDevicePropDesc();
297 break;
Mike Lockwood56118b52010-05-11 17:16:59 -0400298 default:
Mike Lockwood9f679242010-09-23 22:32:05 -0400299 LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood56118b52010-05-11 17:16:59 -0400300 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
301 break;
302 }
303
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400304 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
305 return false;
Mike Lockwood56118b52010-05-11 17:16:59 -0400306 mResponse.setResponseCode(response);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400307 return true;
Mike Lockwood56118b52010-05-11 17:16:59 -0400308}
309
310MtpResponseCode MtpServer::doGetDeviceInfo() {
311 MtpStringBuffer string;
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700312 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood56118b52010-05-11 17:16:59 -0400313
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400314 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
315 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
316 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
317
Mike Lockwood56118b52010-05-11 17:16:59 -0400318 // fill in device info
319 mData.putUInt16(MTP_STANDARD_VERSION);
320 mData.putUInt32(6); // MTP Vendor Extension ID
321 mData.putUInt16(MTP_STANDARD_VERSION);
322 string.set("microsoft.com: 1.0;");
323 mData.putString(string); // MTP Extensions
324 mData.putUInt16(0); //Functional Mode
325 mData.putAUInt16(kSupportedOperationCodes,
326 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwoodbe125a52010-07-12 18:54:16 -0400327 mData.putAUInt16(kSupportedEventCodes,
328 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400329 mData.putAUInt16(deviceProperties); // Device Properties Supported
330 mData.putAUInt16(captureFormats); // Capture Formats
331 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood56118b52010-05-11 17:16:59 -0400332 // FIXME
333 string.set("Google, Inc.");
334 mData.putString(string); // Manufacturer
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700335
336 property_get("ro.product.model", prop_value, "MTP Device");
337 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400338 mData.putString(string); // Model
339 string.set("1.0");
340 mData.putString(string); // Device Version
Mike Lockwood622ccdc2010-06-14 17:58:08 -0700341
342 property_get("ro.serialno", prop_value, "????????");
343 string.set(prop_value);
Mike Lockwood56118b52010-05-11 17:16:59 -0400344 mData.putString(string); // Serial Number
345
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400346 delete playbackFormats;
347 delete captureFormats;
348 delete deviceProperties;
349
Mike Lockwood56118b52010-05-11 17:16:59 -0400350 return MTP_RESPONSE_OK;
351}
352
353MtpResponseCode MtpServer::doOpenSession() {
354 if (mSessionOpen) {
355 mResponse.setParameter(1, mSessionID);
356 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
357 }
358 mSessionID = mRequest.getParameter(1);
359 mSessionOpen = true;
Mike Lockwood2837eef2010-08-31 16:25:12 -0400360
361 mDatabase->sessionStarted();
362
Mike Lockwood56118b52010-05-11 17:16:59 -0400363 return MTP_RESPONSE_OK;
364}
365
366MtpResponseCode MtpServer::doCloseSession() {
367 if (!mSessionOpen)
368 return MTP_RESPONSE_SESSION_NOT_OPEN;
369 mSessionID = 0;
370 mSessionOpen = false;
Mike Lockwood2837eef2010-08-31 16:25:12 -0400371 mDatabase->sessionEnded();
Mike Lockwood56118b52010-05-11 17:16:59 -0400372 return MTP_RESPONSE_OK;
373}
374
375MtpResponseCode MtpServer::doGetStorageIDs() {
376 if (!mSessionOpen)
377 return MTP_RESPONSE_SESSION_NOT_OPEN;
378
379 int count = mStorages.size();
380 mData.putUInt32(count);
381 for (int i = 0; i < count; i++)
382 mData.putUInt32(mStorages[i]->getStorageID());
383
384 return MTP_RESPONSE_OK;
385}
386
387MtpResponseCode MtpServer::doGetStorageInfo() {
388 MtpStringBuffer string;
389
390 if (!mSessionOpen)
391 return MTP_RESPONSE_SESSION_NOT_OPEN;
392 MtpStorageID id = mRequest.getParameter(1);
393 MtpStorage* storage = getStorage(id);
394 if (!storage)
395 return MTP_RESPONSE_INVALID_STORAGE_ID;
396
397 mData.putUInt16(storage->getType());
398 mData.putUInt16(storage->getFileSystemType());
399 mData.putUInt16(storage->getAccessCapability());
400 mData.putUInt64(storage->getMaxCapacity());
401 mData.putUInt64(storage->getFreeSpace());
402 mData.putUInt32(1024*1024*1024); // Free Space in Objects
403 string.set(storage->getDescription());
404 mData.putString(string);
405 mData.putEmptyString(); // Volume Identifier
406
407 return MTP_RESPONSE_OK;
408}
409
410MtpResponseCode MtpServer::doGetObjectPropsSupported() {
411 if (!mSessionOpen)
412 return MTP_RESPONSE_SESSION_NOT_OPEN;
413 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood4b322ce2010-08-10 07:37:50 -0400414 MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
415 mData.putAUInt16(properties);
Mike Lockwood33ea5a42010-08-10 15:11:32 -0400416 delete properties;
Mike Lockwood56118b52010-05-11 17:16:59 -0400417 return MTP_RESPONSE_OK;
418}
419
420MtpResponseCode MtpServer::doGetObjectHandles() {
421 if (!mSessionOpen)
422 return MTP_RESPONSE_SESSION_NOT_OPEN;
423 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwood37433652010-05-19 15:12:14 -0400424 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood56118b52010-05-11 17:16:59 -0400425 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
426 // 0x00000000 for all objects?
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400427 if (parent == 0xFFFFFFFF)
428 parent = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400429
430 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
431 mData.putAUInt32(handles);
432 delete handles;
433 return MTP_RESPONSE_OK;
434}
435
Mike Lockwood7a047c82010-08-02 10:52:20 -0400436MtpResponseCode MtpServer::doGetNumObjects() {
437 if (!mSessionOpen)
438 return MTP_RESPONSE_SESSION_NOT_OPEN;
439 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
440 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
441 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
442 // 0x00000000 for all objects?
443 if (parent == 0xFFFFFFFF)
444 parent = 0;
445
446 int count = mDatabase->getNumObjects(storageID, format, parent);
447 if (count >= 0) {
448 mResponse.setParameter(1, count);
449 return MTP_RESPONSE_OK;
450 } else {
451 mResponse.setParameter(1, 0);
452 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
453 }
454}
455
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400456MtpResponseCode MtpServer::doGetObjectReferences() {
457 if (!mSessionOpen)
458 return MTP_RESPONSE_SESSION_NOT_OPEN;
459 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400460
461 // FIXME - check for invalid object handle
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400462 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400463 if (handles) {
464 mData.putAUInt32(handles);
465 delete handles;
466 } else {
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400467 mData.putEmptyArray();
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400468 }
Mike Lockwood9a2046f2010-08-03 15:30:09 -0400469 return MTP_RESPONSE_OK;
470}
471
472MtpResponseCode MtpServer::doSetObjectReferences() {
473 if (!mSessionOpen)
474 return MTP_RESPONSE_SESSION_NOT_OPEN;
475 MtpStorageID handle = mRequest.getParameter(1);
476 MtpObjectHandleList* references = mData.getAUInt32();
477 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
478 delete references;
479 return result;
480}
481
Mike Lockwood56118b52010-05-11 17:16:59 -0400482MtpResponseCode MtpServer::doGetObjectPropValue() {
483 MtpObjectHandle handle = mRequest.getParameter(1);
484 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400485 LOGD("GetObjectPropValue %d %s\n", handle,
486 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood56118b52010-05-11 17:16:59 -0400487
Mike Lockwood828d19d2010-08-10 15:20:35 -0400488 return mDatabase->getObjectPropertyValue(handle, property, mData);
489}
490
491MtpResponseCode MtpServer::doSetObjectPropValue() {
492 MtpObjectHandle handle = mRequest.getParameter(1);
493 MtpObjectProperty property = mRequest.getParameter(2);
494 LOGD("SetObjectPropValue %d %s\n", handle,
495 MtpDebug::getObjectPropCodeName(property));
496
497 return mDatabase->setObjectPropertyValue(handle, property, mData);
498}
499
500MtpResponseCode MtpServer::doGetDevicePropValue() {
501 MtpDeviceProperty property = mRequest.getParameter(1);
502 LOGD("GetDevicePropValue %s\n",
503 MtpDebug::getDevicePropCodeName(property));
504
505 return mDatabase->getDevicePropertyValue(property, mData);
506}
507
508MtpResponseCode MtpServer::doSetDevicePropValue() {
509 MtpDeviceProperty property = mRequest.getParameter(1);
510 LOGD("SetDevicePropValue %s\n",
511 MtpDebug::getDevicePropCodeName(property));
512
513 return mDatabase->setDevicePropertyValue(property, mData);
514}
515
516MtpResponseCode MtpServer::doResetDevicePropValue() {
517 MtpDeviceProperty property = mRequest.getParameter(1);
518 LOGD("ResetDevicePropValue %s\n",
519 MtpDebug::getDevicePropCodeName(property));
520
521 return mDatabase->resetDeviceProperty(property);
Mike Lockwood56118b52010-05-11 17:16:59 -0400522}
523
524MtpResponseCode MtpServer::doGetObjectInfo() {
525 MtpObjectHandle handle = mRequest.getParameter(1);
526 return mDatabase->getObjectInfo(handle, mData);
527}
528
529MtpResponseCode MtpServer::doGetObject() {
530 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400531 MtpString pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400532 int64_t fileLength;
Mike Lockwood59c777a2010-08-02 10:37:41 -0400533 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
534 if (result != MTP_RESPONSE_OK)
535 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400536
Mike Lockwood59c777a2010-08-02 10:37:41 -0400537 const char* filePath = (const char *)pathBuf;
Mike Lockwood56118b52010-05-11 17:16:59 -0400538 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400539 mfr.fd = open(filePath, O_RDONLY);
540 if (mfr.fd < 0) {
541 return MTP_RESPONSE_GENERAL_ERROR;
542 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400543 mfr.offset = 0;
544 mfr.length = fileLength;
545
546 // send data header
547 mData.setOperationCode(mRequest.getOperationCode());
548 mData.setTransactionID(mRequest.getTransactionID());
549 mData.writeDataHeader(mFD, fileLength);
550
551 // then transfer the file
552 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400553 close(mfr.fd);
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400554 if (ret < 0) {
555 if (errno == ECANCELED)
556 return MTP_RESPONSE_TRANSACTION_CANCELLED;
557 else
558 return MTP_RESPONSE_GENERAL_ERROR;
559 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400560 return MTP_RESPONSE_OK;
561}
562
563MtpResponseCode MtpServer::doSendObjectInfo() {
564 MtpString path;
565 MtpStorageID storageID = mRequest.getParameter(1);
566 MtpStorage* storage = getStorage(storageID);
567 MtpObjectHandle parent = mRequest.getParameter(2);
568 if (!storage)
569 return MTP_RESPONSE_INVALID_STORAGE_ID;
570
571 // special case the root
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400572 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400573 path = storage->getPath();
Mike Lockwoodd21eac92010-07-03 00:44:05 -0400574 parent = 0;
575 } else {
Mike Lockwood56118b52010-05-11 17:16:59 -0400576 int64_t dummy;
Mike Lockwood59c777a2010-08-02 10:37:41 -0400577 int result = mDatabase->getObjectFilePath(parent, path, dummy);
578 if (result != MTP_RESPONSE_OK)
579 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400580 }
581
582 // read only the fields we need
583 mData.getUInt32(); // storage ID
584 MtpObjectFormat format = mData.getUInt16();
585 mData.getUInt16(); // protection status
586 mSendObjectFileSize = mData.getUInt32();
587 mData.getUInt16(); // thumb format
588 mData.getUInt32(); // thumb compressed size
589 mData.getUInt32(); // thumb pix width
590 mData.getUInt32(); // thumb pix height
591 mData.getUInt32(); // image pix width
592 mData.getUInt32(); // image pix height
593 mData.getUInt32(); // image bit depth
594 mData.getUInt32(); // parent
595 uint16_t associationType = mData.getUInt16();
596 uint32_t associationDesc = mData.getUInt32(); // association desc
597 mData.getUInt32(); // sequence number
598 MtpStringBuffer name, created, modified;
599 mData.getString(name); // file name
600 mData.getString(created); // date created
601 mData.getString(modified); // date modified
602 // keywords follow
603
Mike Lockwoodd0782672010-05-14 15:35:17 -0400604 time_t modifiedTime;
Mike Lockwood56118b52010-05-11 17:16:59 -0400605 if (!parseDateTime(modified, modifiedTime))
606 modifiedTime = 0;
Mike Lockwood56118b52010-05-11 17:16:59 -0400607
608 if (path[path.size() - 1] != '/')
609 path += "/";
610 path += (const char *)name;
611
Mike Lockwoodd815f792010-07-12 08:49:01 -0400612 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
613 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodd0782672010-05-14 15:35:17 -0400614 if (handle == kInvalidObjectHandle) {
Mike Lockwood56118b52010-05-11 17:16:59 -0400615 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodd0782672010-05-14 15:35:17 -0400616 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400617
618 if (format == MTP_FORMAT_ASSOCIATION) {
619 mode_t mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400620 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood56118b52010-05-11 17:16:59 -0400621 umask(mask);
622 if (ret && ret != -EEXIST)
623 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooddad69272010-07-02 15:15:07 -0400624 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood56118b52010-05-11 17:16:59 -0400625 } else {
626 mSendObjectFilePath = path;
627 // save the handle for the SendObject call, which should follow
628 mSendObjectHandle = handle;
Mike Lockwoodd815f792010-07-12 08:49:01 -0400629 mSendObjectFormat = format;
Mike Lockwood56118b52010-05-11 17:16:59 -0400630 }
631
632 mResponse.setParameter(1, storageID);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400633 mResponse.setParameter(2, parent);
Mike Lockwood56118b52010-05-11 17:16:59 -0400634 mResponse.setParameter(3, handle);
635
636 return MTP_RESPONSE_OK;
637}
638
639MtpResponseCode MtpServer::doSendObject() {
Mike Lockwoodd815f792010-07-12 08:49:01 -0400640 MtpResponseCode result = MTP_RESPONSE_OK;
641 mode_t mask;
642 int ret;
643
Mike Lockwood56118b52010-05-11 17:16:59 -0400644 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400645 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwoodd815f792010-07-12 08:49:01 -0400646 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
647 goto done;
Mike Lockwood56118b52010-05-11 17:16:59 -0400648 }
649
650 // read the header
Mike Lockwoodd815f792010-07-12 08:49:01 -0400651 ret = mData.readDataHeader(mFD);
Mike Lockwood56118b52010-05-11 17:16:59 -0400652 // FIXME - check for errors here.
653
654 // reset so we don't attempt to send this back
655 mData.reset();
656
657 mtp_file_range mfr;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400658 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
659 if (mfr.fd < 0) {
Mike Lockwoodd815f792010-07-12 08:49:01 -0400660 result = MTP_RESPONSE_GENERAL_ERROR;
661 goto done;
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400662 }
Mike Lockwooddad69272010-07-02 15:15:07 -0400663 fchown(mfr.fd, getuid(), mFileGroup);
664 // set permissions
Mike Lockwoodd815f792010-07-12 08:49:01 -0400665 mask = umask(0);
Mike Lockwooddad69272010-07-02 15:15:07 -0400666 fchmod(mfr.fd, mFilePermission);
667 umask(mask);
668
Mike Lockwood56118b52010-05-11 17:16:59 -0400669 mfr.offset = 0;
670 mfr.length = mSendObjectFileSize;
671
672 // transfer the file
673 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwood42dbfa52010-06-22 15:03:53 -0400674 close(mfr.fd);
Mike Lockwooddad69272010-07-02 15:15:07 -0400675
Mike Lockwood3e6616d2010-06-29 18:11:52 -0400676 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood56118b52010-05-11 17:16:59 -0400677
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400678 if (ret < 0) {
679 unlink(mSendObjectFilePath);
680 if (errno == ECANCELED)
Mike Lockwoodd815f792010-07-12 08:49:01 -0400681 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400682 else
Mike Lockwoodd815f792010-07-12 08:49:01 -0400683 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwooda82d3c52010-06-04 09:49:21 -0400684 }
Mike Lockwoodd815f792010-07-12 08:49:01 -0400685
686done:
687 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
688 result == MTP_RESPONSE_OK);
689 mSendObjectHandle = kInvalidObjectHandle;
690 mSendObjectFormat = 0;
691 return result;
Mike Lockwood56118b52010-05-11 17:16:59 -0400692}
693
Mike Lockwoodccb6e962010-09-13 17:15:58 -0400694static void deleteRecursive(const char* path) {
695 char pathbuf[PATH_MAX];
696 int pathLength = strlen(path);
697 if (pathLength >= sizeof(pathbuf) - 1) {
698 LOGE("path too long: %s\n", path);
699 }
700 strcpy(pathbuf, path);
701 if (pathbuf[pathLength - 1] != '/') {
702 pathbuf[pathLength++] = '/';
703 }
704 char* fileSpot = pathbuf + pathLength;
705 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
706
707 DIR* dir = opendir(path);
708 if (!dir) {
709 LOGE("opendir %s failed: %s", path, strerror(errno));
710 return;
711 }
712
713 struct dirent* entry;
714 while ((entry = readdir(dir))) {
715 const char* name = entry->d_name;
716
717 // ignore "." and ".."
718 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
719 continue;
720 }
721
722 int nameLength = strlen(name);
723 if (nameLength > pathRemaining) {
724 LOGE("path %s/%s too long\n", path, name);
725 continue;
726 }
727 strcpy(fileSpot, name);
728
729 int type = entry->d_type;
730 if (entry->d_type == DT_DIR) {
731 deleteRecursive(pathbuf);
732 rmdir(pathbuf);
733 } else {
734 unlink(pathbuf);
735 }
736 }
737}
738
739static void deletePath(const char* path) {
740 struct stat statbuf;
741 if (stat(path, &statbuf) == 0) {
742 if (S_ISDIR(statbuf.st_mode)) {
743 deleteRecursive(path);
744 rmdir(path);
745 } else {
746 unlink(path);
747 }
748 } else {
749 LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
750 }
751}
752
Mike Lockwood56118b52010-05-11 17:16:59 -0400753MtpResponseCode MtpServer::doDeleteObject() {
754 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodccb6e962010-09-13 17:15:58 -0400755 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood56118b52010-05-11 17:16:59 -0400756 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
757 // FIXME - implement deleting objects by format
Mike Lockwood56118b52010-05-11 17:16:59 -0400758
759 MtpString filePath;
760 int64_t fileLength;
Mike Lockwood59c777a2010-08-02 10:37:41 -0400761 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
762 if (result == MTP_RESPONSE_OK) {
763 LOGV("deleting %s", (const char *)filePath);
Mike Lockwoodccb6e962010-09-13 17:15:58 -0400764 deletePath((const char *)filePath);
Mike Lockwood59c777a2010-08-02 10:37:41 -0400765 return mDatabase->deleteFile(handle);
766 } else {
767 return result;
768 }
Mike Lockwood56118b52010-05-11 17:16:59 -0400769}
770
771MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood767c5e42010-06-30 17:00:35 -0400772 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood56118b52010-05-11 17:16:59 -0400773 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400774 LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
775 MtpDebug::getFormatCodeName(format));
776 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood767c5e42010-06-30 17:00:35 -0400777 if (!property)
778 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood767c5e42010-06-30 17:00:35 -0400779 property->write(mData);
Mike Lockwood828d19d2010-08-10 15:20:35 -0400780 delete property;
781 return MTP_RESPONSE_OK;
782}
783
784MtpResponseCode MtpServer::doGetDevicePropDesc() {
785 MtpDeviceProperty propCode = mRequest.getParameter(1);
786 LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
787 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
788 if (!property)
789 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
790 property->write(mData);
791 delete property;
Mike Lockwood767c5e42010-06-30 17:00:35 -0400792 return MTP_RESPONSE_OK;
Mike Lockwood56118b52010-05-11 17:16:59 -0400793}
Mike Lockwood8d3257a2010-05-14 10:10:36 -0400794
795} // namespace android