blob: 6332b4ef2423df74b3bfe7f938f3e40126f39dfc [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 Lockwood16864ba2010-05-11 17:16:59 -040029#include "MtpDebug.h"
Mike Lockwood7f53a192010-07-09 10:45:22 -040030#include "MtpDatabase.h"
Mike Lockwood21ef7d02010-06-30 17:00:35 -040031#include "MtpProperty.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040032#include "MtpServer.h"
33#include "MtpStorage.h"
34#include "MtpStringBuffer.h"
Mike Lockwood16864ba2010-05-11 17:16:59 -040035
Mike Lockwood8065e202010-07-15 13:36:52 -040036#include <linux/usb/f_mtp.h>
Mike Lockwood16864ba2010-05-11 17:16:59 -040037
Mike Lockwood7850ef92010-05-14 10:10:36 -040038namespace android {
39
Mike Lockwood16864ba2010-05-11 17:16:59 -040040static const MtpOperationCode kSupportedOperationCodes[] = {
41 MTP_OPERATION_GET_DEVICE_INFO,
42 MTP_OPERATION_OPEN_SESSION,
43 MTP_OPERATION_CLOSE_SESSION,
44 MTP_OPERATION_GET_STORAGE_IDS,
45 MTP_OPERATION_GET_STORAGE_INFO,
46 MTP_OPERATION_GET_NUM_OBJECTS,
47 MTP_OPERATION_GET_OBJECT_HANDLES,
48 MTP_OPERATION_GET_OBJECT_INFO,
49 MTP_OPERATION_GET_OBJECT,
50// MTP_OPERATION_GET_THUMB,
51 MTP_OPERATION_DELETE_OBJECT,
52 MTP_OPERATION_SEND_OBJECT_INFO,
53 MTP_OPERATION_SEND_OBJECT,
54// MTP_OPERATION_INITIATE_CAPTURE,
55// MTP_OPERATION_FORMAT_STORE,
56// MTP_OPERATION_RESET_DEVICE,
57// MTP_OPERATION_SELF_TEST,
58// MTP_OPERATION_SET_OBJECT_PROTECTION,
59// MTP_OPERATION_POWER_DOWN,
Mike Lockwoode3e76c42010-09-02 14:57:30 -040060 MTP_OPERATION_GET_DEVICE_PROP_DESC,
Mike Lockwood8277cec2010-08-10 15:20:35 -040061 MTP_OPERATION_GET_DEVICE_PROP_VALUE,
62 MTP_OPERATION_SET_DEVICE_PROP_VALUE,
63 MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
Mike Lockwood16864ba2010-05-11 17:16:59 -040064// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
65// MTP_OPERATION_MOVE_OBJECT,
66// MTP_OPERATION_COPY_OBJECT,
67// MTP_OPERATION_GET_PARTIAL_OBJECT,
68// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
69 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
Mike Lockwood8277cec2010-08-10 15:20:35 -040070 MTP_OPERATION_GET_OBJECT_PROP_DESC,
71// MTP_OPERATION_GET_OBJECT_PROP_VALUE,
Mike Lockwood343af4e2010-08-02 10:52:20 -040072// MTP_OPERATION_SET_OBJECT_PROP_VALUE,
Mike Lockwood438344f2010-08-03 15:30:09 -040073 MTP_OPERATION_GET_OBJECT_REFERENCES,
74 MTP_OPERATION_SET_OBJECT_REFERENCES,
Mike Lockwood16864ba2010-05-11 17:16:59 -040075// MTP_OPERATION_SKIP,
76};
77
Mike Lockwood873871f2010-07-12 18:54:16 -040078static const MtpEventCode kSupportedEventCodes[] = {
79 MTP_EVENT_OBJECT_ADDED,
80 MTP_EVENT_OBJECT_REMOVED,
81};
82
Mike Lockwood1865a5d2010-07-03 00:44:05 -040083MtpServer::MtpServer(int fd, MtpDatabase* database,
Mike Lockwood8e2a2802010-07-02 15:15:07 -040084 int fileGroup, int filePerm, int directoryPerm)
Mike Lockwood16864ba2010-05-11 17:16:59 -040085 : mFD(fd),
Mike Lockwood1865a5d2010-07-03 00:44:05 -040086 mDatabase(database),
Mike Lockwood8e2a2802010-07-02 15:15:07 -040087 mFileGroup(fileGroup),
88 mFilePermission(filePerm),
89 mDirectoryPermission(directoryPerm),
Mike Lockwood16864ba2010-05-11 17:16:59 -040090 mSessionID(0),
91 mSessionOpen(false),
92 mSendObjectHandle(kInvalidObjectHandle),
Mike Lockwood4714b072010-07-12 08:49:01 -040093 mSendObjectFormat(0),
Mike Lockwood16864ba2010-05-11 17:16:59 -040094 mSendObjectFileSize(0)
95{
Mike Lockwood16864ba2010-05-11 17:16:59 -040096}
97
98MtpServer::~MtpServer() {
99}
100
101void MtpServer::addStorage(const char* filePath) {
102 int index = mStorages.size() + 1;
103 index |= index << 16; // set high and low part to our index
104 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
105 addStorage(storage);
106}
107
108MtpStorage* MtpServer::getStorage(MtpStorageID id) {
109 for (int i = 0; i < mStorages.size(); i++) {
110 MtpStorage* storage = mStorages[i];
111 if (storage->getStorageID() == id)
112 return storage;
113 }
114 return NULL;
115}
116
Mike Lockwood16864ba2010-05-11 17:16:59 -0400117void MtpServer::run() {
118 int fd = mFD;
119
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400120 LOGV("MtpServer::run fd: %d\n", fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400121
122 while (1) {
123 int ret = mRequest.read(fd);
124 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400125 LOGE("request read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400126 if (errno == ECANCELED) {
127 // return to top of loop and wait for next command
128 continue;
129 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400130 break;
131 }
132 MtpOperationCode operation = mRequest.getOperationCode();
133 MtpTransactionID transaction = mRequest.getTransactionID();
134
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400135 LOGV("operation: %s", MtpDebug::getOperationCodeName(operation));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400136 mRequest.dump();
137
138 // FIXME need to generalize this
Mike Lockwood438344f2010-08-03 15:30:09 -0400139 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
Mike Lockwood8277cec2010-08-10 15:20:35 -0400140 || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
141 || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
142 || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400143 if (dataIn) {
144 int ret = mData.read(fd);
145 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400146 LOGE("data read returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400147 if (errno == ECANCELED) {
148 // return to top of loop and wait for next command
149 continue;
150 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400151 break;
152 }
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400153 LOGV("received data:");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400154 mData.dump();
155 } else {
156 mData.reset();
157 }
158
Mike Lockwood916076c2010-06-04 09:49:21 -0400159 if (handleRequest()) {
160 if (!dataIn && mData.hasData()) {
161 mData.setOperationCode(operation);
162 mData.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400163 LOGV("sending data:");
Mike Lockwood916076c2010-06-04 09:49:21 -0400164 ret = mData.write(fd);
165 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400166 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400167 if (errno == ECANCELED) {
168 // return to top of loop and wait for next command
169 continue;
170 }
171 break;
172 }
173 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400174
Mike Lockwood916076c2010-06-04 09:49:21 -0400175 mResponse.setTransactionID(transaction);
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400176 LOGV("sending response %04X", mResponse.getResponseCode());
Mike Lockwood916076c2010-06-04 09:49:21 -0400177 ret = mResponse.write(fd);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400178 if (ret < 0) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400179 LOGE("request write returned %d, errno: %d", ret, errno);
Mike Lockwood916076c2010-06-04 09:49:21 -0400180 if (errno == ECANCELED) {
181 // return to top of loop and wait for next command
182 continue;
183 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400184 break;
185 }
Mike Lockwood916076c2010-06-04 09:49:21 -0400186 } else {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400187 LOGV("skipping response\n");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400188 }
189 }
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400190
191 if (mSessionOpen)
192 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400193}
194
Mike Lockwood873871f2010-07-12 18:54:16 -0400195void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400196 if (mSessionOpen) {
197 LOGD("sendObjectAdded %d\n", handle);
198 mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
199 mEvent.setTransactionID(mRequest.getTransactionID());
200 mEvent.setParameter(1, handle);
201 int ret = mEvent.write(mFD);
202 LOGD("mEvent.write returned %d\n", ret);
203 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400204}
205
206void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
Mike Lockwood73ecd232010-07-19 14:29:58 -0400207 if (mSessionOpen) {
208 LOGD("sendObjectRemoved %d\n", handle);
209 mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
210 mEvent.setTransactionID(mRequest.getTransactionID());
211 mEvent.setParameter(1, handle);
212 int ret = mEvent.write(mFD);
213 LOGD("mEvent.write returned %d\n", ret);
214 }
Mike Lockwood873871f2010-07-12 18:54:16 -0400215}
216
Mike Lockwood916076c2010-06-04 09:49:21 -0400217bool MtpServer::handleRequest() {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400218 MtpOperationCode operation = mRequest.getOperationCode();
219 MtpResponseCode response;
220
221 mResponse.reset();
222
223 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
224 // FIXME - need to delete mSendObjectHandle from the database
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400225 LOGE("expected SendObject after SendObjectInfo");
Mike Lockwood16864ba2010-05-11 17:16:59 -0400226 mSendObjectHandle = kInvalidObjectHandle;
227 }
228
229 switch (operation) {
230 case MTP_OPERATION_GET_DEVICE_INFO:
231 response = doGetDeviceInfo();
232 break;
233 case MTP_OPERATION_OPEN_SESSION:
234 response = doOpenSession();
235 break;
236 case MTP_OPERATION_CLOSE_SESSION:
237 response = doCloseSession();
238 break;
239 case MTP_OPERATION_GET_STORAGE_IDS:
240 response = doGetStorageIDs();
241 break;
242 case MTP_OPERATION_GET_STORAGE_INFO:
243 response = doGetStorageInfo();
244 break;
245 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
246 response = doGetObjectPropsSupported();
247 break;
248 case MTP_OPERATION_GET_OBJECT_HANDLES:
249 response = doGetObjectHandles();
250 break;
Mike Lockwood343af4e2010-08-02 10:52:20 -0400251 case MTP_OPERATION_GET_NUM_OBJECTS:
252 response = doGetNumObjects();
253 break;
Mike Lockwood438344f2010-08-03 15:30:09 -0400254 case MTP_OPERATION_GET_OBJECT_REFERENCES:
255 response = doGetObjectReferences();
256 break;
257 case MTP_OPERATION_SET_OBJECT_REFERENCES:
258 response = doSetObjectReferences();
259 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400260 case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
261 response = doGetObjectPropValue();
262 break;
Mike Lockwood8277cec2010-08-10 15:20:35 -0400263 case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
264 response = doSetObjectPropValue();
265 break;
266 case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
267 response = doGetDevicePropValue();
268 break;
269 case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
270 response = doSetDevicePropValue();
271 break;
272 case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
273 response = doResetDevicePropValue();
274 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400275 case MTP_OPERATION_GET_OBJECT_INFO:
276 response = doGetObjectInfo();
277 break;
278 case MTP_OPERATION_GET_OBJECT:
279 response = doGetObject();
280 break;
281 case MTP_OPERATION_SEND_OBJECT_INFO:
282 response = doSendObjectInfo();
283 break;
284 case MTP_OPERATION_SEND_OBJECT:
285 response = doSendObject();
286 break;
287 case MTP_OPERATION_DELETE_OBJECT:
288 response = doDeleteObject();
289 break;
290 case MTP_OPERATION_GET_OBJECT_PROP_DESC:
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400291 response = doGetObjectPropDesc();
292 break;
Mike Lockwoode3e76c42010-09-02 14:57:30 -0400293 case MTP_OPERATION_GET_DEVICE_PROP_DESC:
294 response = doGetDevicePropDesc();
295 break;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400296 default:
297 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
298 break;
299 }
300
Mike Lockwood916076c2010-06-04 09:49:21 -0400301 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
302 return false;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400303 mResponse.setResponseCode(response);
Mike Lockwood916076c2010-06-04 09:49:21 -0400304 return true;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400305}
306
307MtpResponseCode MtpServer::doGetDeviceInfo() {
308 MtpStringBuffer string;
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700309 char prop_value[PROPERTY_VALUE_MAX];
Mike Lockwood16864ba2010-05-11 17:16:59 -0400310
Mike Lockwood782aef12010-08-10 07:37:50 -0400311 MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
312 MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
313 MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
314
Mike Lockwood16864ba2010-05-11 17:16:59 -0400315 // fill in device info
316 mData.putUInt16(MTP_STANDARD_VERSION);
317 mData.putUInt32(6); // MTP Vendor Extension ID
318 mData.putUInt16(MTP_STANDARD_VERSION);
319 string.set("microsoft.com: 1.0;");
320 mData.putString(string); // MTP Extensions
321 mData.putUInt16(0); //Functional Mode
322 mData.putAUInt16(kSupportedOperationCodes,
323 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
Mike Lockwood873871f2010-07-12 18:54:16 -0400324 mData.putAUInt16(kSupportedEventCodes,
325 sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
Mike Lockwood782aef12010-08-10 07:37:50 -0400326 mData.putAUInt16(deviceProperties); // Device Properties Supported
327 mData.putAUInt16(captureFormats); // Capture Formats
328 mData.putAUInt16(playbackFormats); // Playback Formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400329 // FIXME
330 string.set("Google, Inc.");
331 mData.putString(string); // Manufacturer
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700332
333 property_get("ro.product.model", prop_value, "MTP Device");
334 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400335 mData.putString(string); // Model
336 string.set("1.0");
337 mData.putString(string); // Device Version
Mike Lockwoodc42aa122010-06-14 17:58:08 -0700338
339 property_get("ro.serialno", prop_value, "????????");
340 string.set(prop_value);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400341 mData.putString(string); // Serial Number
342
Mike Lockwood782aef12010-08-10 07:37:50 -0400343 delete playbackFormats;
344 delete captureFormats;
345 delete deviceProperties;
346
Mike Lockwood16864ba2010-05-11 17:16:59 -0400347 return MTP_RESPONSE_OK;
348}
349
350MtpResponseCode MtpServer::doOpenSession() {
351 if (mSessionOpen) {
352 mResponse.setParameter(1, mSessionID);
353 return MTP_RESPONSE_SESSION_ALREADY_OPEN;
354 }
355 mSessionID = mRequest.getParameter(1);
356 mSessionOpen = true;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400357
358 mDatabase->sessionStarted();
359
Mike Lockwood16864ba2010-05-11 17:16:59 -0400360 return MTP_RESPONSE_OK;
361}
362
363MtpResponseCode MtpServer::doCloseSession() {
364 if (!mSessionOpen)
365 return MTP_RESPONSE_SESSION_NOT_OPEN;
366 mSessionID = 0;
367 mSessionOpen = false;
Mike Lockwood6b3a9d12010-08-31 16:25:12 -0400368 mDatabase->sessionEnded();
Mike Lockwood16864ba2010-05-11 17:16:59 -0400369 return MTP_RESPONSE_OK;
370}
371
372MtpResponseCode MtpServer::doGetStorageIDs() {
373 if (!mSessionOpen)
374 return MTP_RESPONSE_SESSION_NOT_OPEN;
375
376 int count = mStorages.size();
377 mData.putUInt32(count);
378 for (int i = 0; i < count; i++)
379 mData.putUInt32(mStorages[i]->getStorageID());
380
381 return MTP_RESPONSE_OK;
382}
383
384MtpResponseCode MtpServer::doGetStorageInfo() {
385 MtpStringBuffer string;
386
387 if (!mSessionOpen)
388 return MTP_RESPONSE_SESSION_NOT_OPEN;
389 MtpStorageID id = mRequest.getParameter(1);
390 MtpStorage* storage = getStorage(id);
391 if (!storage)
392 return MTP_RESPONSE_INVALID_STORAGE_ID;
393
394 mData.putUInt16(storage->getType());
395 mData.putUInt16(storage->getFileSystemType());
396 mData.putUInt16(storage->getAccessCapability());
397 mData.putUInt64(storage->getMaxCapacity());
398 mData.putUInt64(storage->getFreeSpace());
399 mData.putUInt32(1024*1024*1024); // Free Space in Objects
400 string.set(storage->getDescription());
401 mData.putString(string);
402 mData.putEmptyString(); // Volume Identifier
403
404 return MTP_RESPONSE_OK;
405}
406
407MtpResponseCode MtpServer::doGetObjectPropsSupported() {
408 if (!mSessionOpen)
409 return MTP_RESPONSE_SESSION_NOT_OPEN;
410 MtpObjectFormat format = mRequest.getParameter(1);
Mike Lockwood782aef12010-08-10 07:37:50 -0400411 MtpDevicePropertyList* properties = mDatabase->getSupportedObjectProperties(format);
412 mData.putAUInt16(properties);
Mike Lockwoodbf9b2052010-08-10 15:11:32 -0400413 delete properties;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400414 return MTP_RESPONSE_OK;
415}
416
417MtpResponseCode MtpServer::doGetObjectHandles() {
418 if (!mSessionOpen)
419 return MTP_RESPONSE_SESSION_NOT_OPEN;
420 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
Mike Lockwoode13401b2010-05-19 15:12:14 -0400421 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
Mike Lockwood16864ba2010-05-11 17:16:59 -0400422 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
423 // 0x00000000 for all objects?
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400424 if (parent == 0xFFFFFFFF)
425 parent = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400426
427 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
428 mData.putAUInt32(handles);
429 delete handles;
430 return MTP_RESPONSE_OK;
431}
432
Mike Lockwood343af4e2010-08-02 10:52:20 -0400433MtpResponseCode MtpServer::doGetNumObjects() {
434 if (!mSessionOpen)
435 return MTP_RESPONSE_SESSION_NOT_OPEN;
436 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
437 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
438 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
439 // 0x00000000 for all objects?
440 if (parent == 0xFFFFFFFF)
441 parent = 0;
442
443 int count = mDatabase->getNumObjects(storageID, format, parent);
444 if (count >= 0) {
445 mResponse.setParameter(1, count);
446 return MTP_RESPONSE_OK;
447 } else {
448 mResponse.setParameter(1, 0);
449 return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
450 }
451}
452
Mike Lockwood438344f2010-08-03 15:30:09 -0400453MtpResponseCode MtpServer::doGetObjectReferences() {
454 if (!mSessionOpen)
455 return MTP_RESPONSE_SESSION_NOT_OPEN;
456 MtpStorageID handle = mRequest.getParameter(1);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400457
458 // FIXME - check for invalid object handle
Mike Lockwood438344f2010-08-03 15:30:09 -0400459 MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400460 if (handles) {
461 mData.putAUInt32(handles);
462 delete handles;
463 } else {
Mike Lockwood438344f2010-08-03 15:30:09 -0400464 mData.putEmptyArray();
Mike Lockwood438344f2010-08-03 15:30:09 -0400465 }
Mike Lockwood438344f2010-08-03 15:30:09 -0400466 return MTP_RESPONSE_OK;
467}
468
469MtpResponseCode MtpServer::doSetObjectReferences() {
470 if (!mSessionOpen)
471 return MTP_RESPONSE_SESSION_NOT_OPEN;
472 MtpStorageID handle = mRequest.getParameter(1);
473 MtpObjectHandleList* references = mData.getAUInt32();
474 MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
475 delete references;
476 return result;
477}
478
Mike Lockwood16864ba2010-05-11 17:16:59 -0400479MtpResponseCode MtpServer::doGetObjectPropValue() {
480 MtpObjectHandle handle = mRequest.getParameter(1);
481 MtpObjectProperty property = mRequest.getParameter(2);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400482 LOGD("GetObjectPropValue %d %s\n", handle,
483 MtpDebug::getObjectPropCodeName(property));
Mike Lockwood16864ba2010-05-11 17:16:59 -0400484
Mike Lockwood8277cec2010-08-10 15:20:35 -0400485 return mDatabase->getObjectPropertyValue(handle, property, mData);
486}
487
488MtpResponseCode MtpServer::doSetObjectPropValue() {
489 MtpObjectHandle handle = mRequest.getParameter(1);
490 MtpObjectProperty property = mRequest.getParameter(2);
491 LOGD("SetObjectPropValue %d %s\n", handle,
492 MtpDebug::getObjectPropCodeName(property));
493
494 return mDatabase->setObjectPropertyValue(handle, property, mData);
495}
496
497MtpResponseCode MtpServer::doGetDevicePropValue() {
498 MtpDeviceProperty property = mRequest.getParameter(1);
499 LOGD("GetDevicePropValue %s\n",
500 MtpDebug::getDevicePropCodeName(property));
501
502 return mDatabase->getDevicePropertyValue(property, mData);
503}
504
505MtpResponseCode MtpServer::doSetDevicePropValue() {
506 MtpDeviceProperty property = mRequest.getParameter(1);
507 LOGD("SetDevicePropValue %s\n",
508 MtpDebug::getDevicePropCodeName(property));
509
510 return mDatabase->setDevicePropertyValue(property, mData);
511}
512
513MtpResponseCode MtpServer::doResetDevicePropValue() {
514 MtpDeviceProperty property = mRequest.getParameter(1);
515 LOGD("ResetDevicePropValue %s\n",
516 MtpDebug::getDevicePropCodeName(property));
517
518 return mDatabase->resetDeviceProperty(property);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400519}
520
521MtpResponseCode MtpServer::doGetObjectInfo() {
522 MtpObjectHandle handle = mRequest.getParameter(1);
523 return mDatabase->getObjectInfo(handle, mData);
524}
525
526MtpResponseCode MtpServer::doGetObject() {
527 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400528 MtpString pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400529 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400530 int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
531 if (result != MTP_RESPONSE_OK)
532 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400533
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400534 const char* filePath = (const char *)pathBuf;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400535 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400536 mfr.fd = open(filePath, O_RDONLY);
537 if (mfr.fd < 0) {
538 return MTP_RESPONSE_GENERAL_ERROR;
539 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400540 mfr.offset = 0;
541 mfr.length = fileLength;
542
543 // send data header
544 mData.setOperationCode(mRequest.getOperationCode());
545 mData.setTransactionID(mRequest.getTransactionID());
546 mData.writeDataHeader(mFD, fileLength);
547
548 // then transfer the file
549 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400550 close(mfr.fd);
Mike Lockwood916076c2010-06-04 09:49:21 -0400551 if (ret < 0) {
552 if (errno == ECANCELED)
553 return MTP_RESPONSE_TRANSACTION_CANCELLED;
554 else
555 return MTP_RESPONSE_GENERAL_ERROR;
556 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400557 return MTP_RESPONSE_OK;
558}
559
560MtpResponseCode MtpServer::doSendObjectInfo() {
561 MtpString path;
562 MtpStorageID storageID = mRequest.getParameter(1);
563 MtpStorage* storage = getStorage(storageID);
564 MtpObjectHandle parent = mRequest.getParameter(2);
565 if (!storage)
566 return MTP_RESPONSE_INVALID_STORAGE_ID;
567
568 // special case the root
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400569 if (parent == MTP_PARENT_ROOT) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400570 path = storage->getPath();
Mike Lockwood1865a5d2010-07-03 00:44:05 -0400571 parent = 0;
572 } else {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400573 int64_t dummy;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400574 int result = mDatabase->getObjectFilePath(parent, path, dummy);
575 if (result != MTP_RESPONSE_OK)
576 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400577 }
578
579 // read only the fields we need
580 mData.getUInt32(); // storage ID
581 MtpObjectFormat format = mData.getUInt16();
582 mData.getUInt16(); // protection status
583 mSendObjectFileSize = mData.getUInt32();
584 mData.getUInt16(); // thumb format
585 mData.getUInt32(); // thumb compressed size
586 mData.getUInt32(); // thumb pix width
587 mData.getUInt32(); // thumb pix height
588 mData.getUInt32(); // image pix width
589 mData.getUInt32(); // image pix height
590 mData.getUInt32(); // image bit depth
591 mData.getUInt32(); // parent
592 uint16_t associationType = mData.getUInt16();
593 uint32_t associationDesc = mData.getUInt32(); // association desc
594 mData.getUInt32(); // sequence number
595 MtpStringBuffer name, created, modified;
596 mData.getString(name); // file name
597 mData.getString(created); // date created
598 mData.getString(modified); // date modified
599 // keywords follow
600
Mike Lockwoodfceef462010-05-14 15:35:17 -0400601 time_t modifiedTime;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400602 if (!parseDateTime(modified, modifiedTime))
603 modifiedTime = 0;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400604
605 if (path[path.size() - 1] != '/')
606 path += "/";
607 path += (const char *)name;
608
Mike Lockwood4714b072010-07-12 08:49:01 -0400609 MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
610 format, parent, storageID, mSendObjectFileSize, modifiedTime);
Mike Lockwoodfceef462010-05-14 15:35:17 -0400611 if (handle == kInvalidObjectHandle) {
Mike Lockwood16864ba2010-05-11 17:16:59 -0400612 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwoodfceef462010-05-14 15:35:17 -0400613 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400614
615 if (format == MTP_FORMAT_ASSOCIATION) {
616 mode_t mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400617 int ret = mkdir((const char *)path, mDirectoryPermission);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400618 umask(mask);
619 if (ret && ret != -EEXIST)
620 return MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400621 chown((const char *)path, getuid(), mFileGroup);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400622 } else {
623 mSendObjectFilePath = path;
624 // save the handle for the SendObject call, which should follow
625 mSendObjectHandle = handle;
Mike Lockwood4714b072010-07-12 08:49:01 -0400626 mSendObjectFormat = format;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400627 }
628
629 mResponse.setParameter(1, storageID);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400630 mResponse.setParameter(2, parent);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400631 mResponse.setParameter(3, handle);
632
633 return MTP_RESPONSE_OK;
634}
635
636MtpResponseCode MtpServer::doSendObject() {
Mike Lockwood4714b072010-07-12 08:49:01 -0400637 MtpResponseCode result = MTP_RESPONSE_OK;
638 mode_t mask;
639 int ret;
640
Mike Lockwood16864ba2010-05-11 17:16:59 -0400641 if (mSendObjectHandle == kInvalidObjectHandle) {
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400642 LOGE("Expected SendObjectInfo before SendObject");
Mike Lockwood4714b072010-07-12 08:49:01 -0400643 result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
644 goto done;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400645 }
646
647 // read the header
Mike Lockwood4714b072010-07-12 08:49:01 -0400648 ret = mData.readDataHeader(mFD);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400649 // FIXME - check for errors here.
650
651 // reset so we don't attempt to send this back
652 mData.reset();
653
654 mtp_file_range mfr;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400655 mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC);
656 if (mfr.fd < 0) {
Mike Lockwood4714b072010-07-12 08:49:01 -0400657 result = MTP_RESPONSE_GENERAL_ERROR;
658 goto done;
Mike Lockwoodc6588762010-06-22 15:03:53 -0400659 }
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400660 fchown(mfr.fd, getuid(), mFileGroup);
661 // set permissions
Mike Lockwood4714b072010-07-12 08:49:01 -0400662 mask = umask(0);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400663 fchmod(mfr.fd, mFilePermission);
664 umask(mask);
665
Mike Lockwood16864ba2010-05-11 17:16:59 -0400666 mfr.offset = 0;
667 mfr.length = mSendObjectFileSize;
668
669 // transfer the file
670 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
Mike Lockwoodc6588762010-06-22 15:03:53 -0400671 close(mfr.fd);
Mike Lockwood8e2a2802010-07-02 15:15:07 -0400672
Mike Lockwoodb14e5882010-06-29 18:11:52 -0400673 LOGV("MTP_RECEIVE_FILE returned %d", ret);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400674
Mike Lockwood916076c2010-06-04 09:49:21 -0400675 if (ret < 0) {
676 unlink(mSendObjectFilePath);
677 if (errno == ECANCELED)
Mike Lockwood4714b072010-07-12 08:49:01 -0400678 result = MTP_RESPONSE_TRANSACTION_CANCELLED;
Mike Lockwood916076c2010-06-04 09:49:21 -0400679 else
Mike Lockwood4714b072010-07-12 08:49:01 -0400680 result = MTP_RESPONSE_GENERAL_ERROR;
Mike Lockwood916076c2010-06-04 09:49:21 -0400681 }
Mike Lockwood4714b072010-07-12 08:49:01 -0400682
683done:
684 mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
685 result == MTP_RESPONSE_OK);
686 mSendObjectHandle = kInvalidObjectHandle;
687 mSendObjectFormat = 0;
688 return result;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400689}
690
Mike Lockwoodd3211492010-09-13 17:15:58 -0400691static void deleteRecursive(const char* path) {
692 char pathbuf[PATH_MAX];
693 int pathLength = strlen(path);
694 if (pathLength >= sizeof(pathbuf) - 1) {
695 LOGE("path too long: %s\n", path);
696 }
697 strcpy(pathbuf, path);
698 if (pathbuf[pathLength - 1] != '/') {
699 pathbuf[pathLength++] = '/';
700 }
701 char* fileSpot = pathbuf + pathLength;
702 int pathRemaining = sizeof(pathbuf) - pathLength - 1;
703
704 DIR* dir = opendir(path);
705 if (!dir) {
706 LOGE("opendir %s failed: %s", path, strerror(errno));
707 return;
708 }
709
710 struct dirent* entry;
711 while ((entry = readdir(dir))) {
712 const char* name = entry->d_name;
713
714 // ignore "." and ".."
715 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
716 continue;
717 }
718
719 int nameLength = strlen(name);
720 if (nameLength > pathRemaining) {
721 LOGE("path %s/%s too long\n", path, name);
722 continue;
723 }
724 strcpy(fileSpot, name);
725
726 int type = entry->d_type;
727 if (entry->d_type == DT_DIR) {
728 deleteRecursive(pathbuf);
729 rmdir(pathbuf);
730 } else {
731 unlink(pathbuf);
732 }
733 }
734}
735
736static void deletePath(const char* path) {
737 struct stat statbuf;
738 if (stat(path, &statbuf) == 0) {
739 if (S_ISDIR(statbuf.st_mode)) {
740 deleteRecursive(path);
741 rmdir(path);
742 } else {
743 unlink(path);
744 }
745 } else {
746 LOGE("deletePath stat failed for %s: %s", path, strerror(errno));
747 }
748}
749
Mike Lockwood16864ba2010-05-11 17:16:59 -0400750MtpResponseCode MtpServer::doDeleteObject() {
751 MtpObjectHandle handle = mRequest.getParameter(1);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400752 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400753 // FIXME - support deleting all objects if handle is 0xFFFFFFFF
754 // FIXME - implement deleting objects by format
Mike Lockwood16864ba2010-05-11 17:16:59 -0400755
756 MtpString filePath;
757 int64_t fileLength;
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400758 int result = mDatabase->getObjectFilePath(handle, filePath, fileLength);
759 if (result == MTP_RESPONSE_OK) {
760 LOGV("deleting %s", (const char *)filePath);
Mike Lockwoodd3211492010-09-13 17:15:58 -0400761 deletePath((const char *)filePath);
Mike Lockwood9c04c4c2010-08-02 10:37:41 -0400762 return mDatabase->deleteFile(handle);
763 } else {
764 return result;
765 }
Mike Lockwood16864ba2010-05-11 17:16:59 -0400766}
767
768MtpResponseCode MtpServer::doGetObjectPropDesc() {
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400769 MtpObjectProperty propCode = mRequest.getParameter(1);
Mike Lockwood16864ba2010-05-11 17:16:59 -0400770 MtpObjectFormat format = mRequest.getParameter(2);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400771 LOGD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
772 MtpDebug::getFormatCodeName(format));
773 MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400774 if (!property)
775 return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400776 property->write(mData);
Mike Lockwood8277cec2010-08-10 15:20:35 -0400777 delete property;
778 return MTP_RESPONSE_OK;
779}
780
781MtpResponseCode MtpServer::doGetDevicePropDesc() {
782 MtpDeviceProperty propCode = mRequest.getParameter(1);
783 LOGD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
784 MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
785 if (!property)
786 return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
787 property->write(mData);
788 delete property;
Mike Lockwood21ef7d02010-06-30 17:00:35 -0400789 return MTP_RESPONSE_OK;
Mike Lockwood16864ba2010-05-11 17:16:59 -0400790}
Mike Lockwood7850ef92010-05-14 10:10:36 -0400791
792} // namespace android