MtpServer.cpp revision fceef46513db3507b413f604cea89e3c7f352663
1/* 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> 24 25#include "MtpDebug.h" 26#include "MtpServer.h" 27#include "MtpStorage.h" 28#include "MtpStringBuffer.h" 29#include "MtpDatabase.h" 30 31#include "f_mtp.h" 32 33namespace android { 34 35static const MtpOperationCode kSupportedOperationCodes[] = { 36 MTP_OPERATION_GET_DEVICE_INFO, 37 MTP_OPERATION_OPEN_SESSION, 38 MTP_OPERATION_CLOSE_SESSION, 39 MTP_OPERATION_GET_STORAGE_IDS, 40 MTP_OPERATION_GET_STORAGE_INFO, 41 MTP_OPERATION_GET_NUM_OBJECTS, 42 MTP_OPERATION_GET_OBJECT_HANDLES, 43 MTP_OPERATION_GET_OBJECT_INFO, 44 MTP_OPERATION_GET_OBJECT, 45// MTP_OPERATION_GET_THUMB, 46 MTP_OPERATION_DELETE_OBJECT, 47 MTP_OPERATION_SEND_OBJECT_INFO, 48 MTP_OPERATION_SEND_OBJECT, 49// MTP_OPERATION_INITIATE_CAPTURE, 50// MTP_OPERATION_FORMAT_STORE, 51// MTP_OPERATION_RESET_DEVICE, 52// MTP_OPERATION_SELF_TEST, 53// MTP_OPERATION_SET_OBJECT_PROTECTION, 54// MTP_OPERATION_POWER_DOWN, 55 MTP_OPERATION_GET_DEVICE_PROP_DESC, 56 MTP_OPERATION_GET_DEVICE_PROP_VALUE, 57 MTP_OPERATION_SET_DEVICE_PROP_VALUE, 58 MTP_OPERATION_RESET_DEVICE_PROP_VALUE, 59// MTP_OPERATION_TERMINATE_OPEN_CAPTURE, 60// MTP_OPERATION_MOVE_OBJECT, 61// MTP_OPERATION_COPY_OBJECT, 62// MTP_OPERATION_GET_PARTIAL_OBJECT, 63// MTP_OPERATION_INITIATE_OPEN_CAPTURE, 64 MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, 65// MTP_OPERATION_GET_OBJECT_PROP_DESC, 66 MTP_OPERATION_GET_OBJECT_PROP_VALUE, 67 MTP_OPERATION_SET_OBJECT_PROP_VALUE, 68// MTP_OPERATION_GET_OBJECT_REFERENCES, 69// MTP_OPERATION_SET_OBJECT_REFERENCES, 70// MTP_OPERATION_SKIP, 71}; 72 73static const MtpObjectProperty kSupportedObjectProperties[] = { 74 MTP_PROPERTY_STORAGE_ID, 75 MTP_PROPERTY_OBJECT_FORMAT, 76 MTP_PROPERTY_OBJECT_SIZE, 77 MTP_PROPERTY_OBJECT_FILE_NAME, 78 MTP_PROPERTY_PARENT_OBJECT, 79}; 80 81static const MtpObjectFormat kSupportedPlaybackFormats[] = { 82 // MTP_FORMAT_UNDEFINED, 83 MTP_FORMAT_ASSOCIATION, 84 // MTP_FORMAT_TEXT, 85 // MTP_FORMAT_HTML, 86 MTP_FORMAT_MP3, 87 //MTP_FORMAT_AVI, 88 MTP_FORMAT_MPEG, 89 // MTP_FORMAT_ASF, 90 MTP_FORMAT_EXIF_JPEG, 91 MTP_FORMAT_TIFF_EP, 92 // MTP_FORMAT_BMP, 93 MTP_FORMAT_GIF, 94 MTP_FORMAT_JFIF, 95 MTP_FORMAT_PNG, 96 MTP_FORMAT_TIFF, 97 MTP_FORMAT_WMA, 98 MTP_FORMAT_OGG, 99 MTP_FORMAT_AAC, 100 // MTP_FORMAT_FLAC, 101 // MTP_FORMAT_WMV, 102 MTP_FORMAT_MP4_CONTAINER, 103 MTP_FORMAT_MP2, 104 MTP_FORMAT_3GP_CONTAINER, 105 // MTP_FORMAT_ABSTRACT_AUDIO_ALBUM, 106 // MTP_FORMAT_ABSTRACT_AV_PLAYLIST, 107 // MTP_FORMAT_WPL_PLAYLIST, 108 // MTP_FORMAT_M3U_PLAYLIST, 109 // MTP_FORMAT_MPL_PLAYLIST, 110 // MTP_FORMAT_PLS_PLAYLIST, 111}; 112 113MtpServer::MtpServer(int fd, const char* databasePath) 114 : mFD(fd), 115 mDatabasePath(databasePath), 116 mDatabase(NULL), 117 mSessionID(0), 118 mSessionOpen(false), 119 mSendObjectHandle(kInvalidObjectHandle), 120 mSendObjectFileSize(0) 121{ 122 mDatabase = new MtpDatabase(); 123 mDatabase->open(databasePath, true); 124} 125 126MtpServer::~MtpServer() { 127} 128 129void MtpServer::addStorage(const char* filePath) { 130 int index = mStorages.size() + 1; 131 index |= index << 16; // set high and low part to our index 132 MtpStorage* storage = new MtpStorage(index, filePath, mDatabase); 133 addStorage(storage); 134} 135 136MtpStorage* MtpServer::getStorage(MtpStorageID id) { 137 for (int i = 0; i < mStorages.size(); i++) { 138 MtpStorage* storage = mStorages[i]; 139 if (storage->getStorageID() == id) 140 return storage; 141 } 142 return NULL; 143} 144 145void MtpServer::scanStorage() { 146 for (int i = 0; i < mStorages.size(); i++) { 147 MtpStorage* storage = mStorages[i]; 148 storage->scanFiles(); 149 } 150} 151 152void MtpServer::run() { 153 int fd = mFD; 154 155 printf("MtpServer::run fd: %d\n", fd); 156 157 while (1) { 158 int ret = mRequest.read(fd); 159 if (ret < 0) { 160 fprintf(stderr, "request read returned %d, errno: %d\n", ret, errno); 161 break; 162 } 163 MtpOperationCode operation = mRequest.getOperationCode(); 164 MtpTransactionID transaction = mRequest.getTransactionID(); 165 166 printf("operation: %s\n", MtpDebug::getOperationCodeName(operation)); 167 mRequest.dump(); 168 169 // FIXME need to generalize this 170 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO); 171 if (dataIn) { 172 int ret = mData.read(fd); 173 if (ret < 0) { 174 fprintf(stderr, "data read returned %d, errno: %d\n", ret, errno); 175 break; 176 } 177 printf("received data:\n"); 178 mData.dump(); 179 } else { 180 mData.reset(); 181 } 182 183 handleRequest(); 184 185 if (!dataIn && mData.hasData()) { 186 mData.setOperationCode(operation); 187 mData.setTransactionID(transaction); 188 printf("sending data:\n"); 189 mData.dump(); 190 ret = mData.write(fd); 191 if (ret < 0) { 192 fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno); 193 break; 194 } 195 } 196 197 mResponse.setTransactionID(transaction); 198 ret = mResponse.write(fd); 199 if (ret < 0) { 200 fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno); 201 break; 202 } 203 } 204} 205 206void MtpServer::handleRequest() { 207 MtpOperationCode operation = mRequest.getOperationCode(); 208 MtpResponseCode response; 209 210 mResponse.reset(); 211 212 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 213 // FIXME - need to delete mSendObjectHandle from the database 214 fprintf(stderr, "expected SendObject after SendObjectInfo\n"); 215 mSendObjectHandle = kInvalidObjectHandle; 216 } 217 218 switch (operation) { 219 case MTP_OPERATION_GET_DEVICE_INFO: 220 response = doGetDeviceInfo(); 221 break; 222 case MTP_OPERATION_OPEN_SESSION: 223 response = doOpenSession(); 224 break; 225 case MTP_OPERATION_CLOSE_SESSION: 226 response = doCloseSession(); 227 break; 228 case MTP_OPERATION_GET_STORAGE_IDS: 229 response = doGetStorageIDs(); 230 break; 231 case MTP_OPERATION_GET_STORAGE_INFO: 232 response = doGetStorageInfo(); 233 break; 234 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 235 response = doGetObjectPropsSupported(); 236 break; 237 case MTP_OPERATION_GET_OBJECT_HANDLES: 238 response = doGetObjectHandles(); 239 break; 240 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 241 response = doGetObjectPropValue(); 242 break; 243 case MTP_OPERATION_GET_OBJECT_INFO: 244 response = doGetObjectInfo(); 245 break; 246 case MTP_OPERATION_GET_OBJECT: 247 response = doGetObject(); 248 break; 249 case MTP_OPERATION_SEND_OBJECT_INFO: 250 response = doSendObjectInfo(); 251 break; 252 case MTP_OPERATION_SEND_OBJECT: 253 response = doSendObject(); 254 break; 255 case MTP_OPERATION_DELETE_OBJECT: 256 response = doDeleteObject(); 257 break; 258 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 259 default: 260 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 261 break; 262 } 263 264 mResponse.setResponseCode(response); 265} 266 267MtpResponseCode MtpServer::doGetDeviceInfo() { 268 MtpStringBuffer string; 269 270 // fill in device info 271 mData.putUInt16(MTP_STANDARD_VERSION); 272 mData.putUInt32(6); // MTP Vendor Extension ID 273 mData.putUInt16(MTP_STANDARD_VERSION); 274 string.set("microsoft.com: 1.0;"); 275 mData.putString(string); // MTP Extensions 276 mData.putUInt16(0); //Functional Mode 277 mData.putAUInt16(kSupportedOperationCodes, 278 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 279 mData.putEmptyArray(); // Events Supported 280 mData.putEmptyArray(); // Device Properties Supported 281 mData.putEmptyArray(); // Capture Formats 282 mData.putAUInt16(kSupportedPlaybackFormats, 283 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats 284 // FIXME 285 string.set("Google, Inc."); 286 mData.putString(string); // Manufacturer 287 string.set("Just an Ordinary MTP Device"); 288 mData.putString(string); // Model 289 string.set("1.0"); 290 mData.putString(string); // Device Version 291 string.set("123456789012345678AA"); 292 mData.putString(string); // Serial Number 293 294 return MTP_RESPONSE_OK; 295} 296 297MtpResponseCode MtpServer::doOpenSession() { 298 if (mSessionOpen) { 299 mResponse.setParameter(1, mSessionID); 300 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 301 } 302 mSessionID = mRequest.getParameter(1); 303 mSessionOpen = true; 304 return MTP_RESPONSE_OK; 305} 306 307MtpResponseCode MtpServer::doCloseSession() { 308 if (!mSessionOpen) 309 return MTP_RESPONSE_SESSION_NOT_OPEN; 310 mSessionID = 0; 311 mSessionOpen = false; 312 return MTP_RESPONSE_OK; 313} 314 315MtpResponseCode MtpServer::doGetStorageIDs() { 316 if (!mSessionOpen) 317 return MTP_RESPONSE_SESSION_NOT_OPEN; 318 319 int count = mStorages.size(); 320 mData.putUInt32(count); 321 for (int i = 0; i < count; i++) 322 mData.putUInt32(mStorages[i]->getStorageID()); 323 324 return MTP_RESPONSE_OK; 325} 326 327MtpResponseCode MtpServer::doGetStorageInfo() { 328 MtpStringBuffer string; 329 330 if (!mSessionOpen) 331 return MTP_RESPONSE_SESSION_NOT_OPEN; 332 MtpStorageID id = mRequest.getParameter(1); 333 MtpStorage* storage = getStorage(id); 334 if (!storage) 335 return MTP_RESPONSE_INVALID_STORAGE_ID; 336 337 mData.putUInt16(storage->getType()); 338 mData.putUInt16(storage->getFileSystemType()); 339 mData.putUInt16(storage->getAccessCapability()); 340 mData.putUInt64(storage->getMaxCapacity()); 341 mData.putUInt64(storage->getFreeSpace()); 342 mData.putUInt32(1024*1024*1024); // Free Space in Objects 343 string.set(storage->getDescription()); 344 mData.putString(string); 345 mData.putEmptyString(); // Volume Identifier 346 347 return MTP_RESPONSE_OK; 348} 349 350MtpResponseCode MtpServer::doGetObjectPropsSupported() { 351 if (!mSessionOpen) 352 return MTP_RESPONSE_SESSION_NOT_OPEN; 353 MtpObjectFormat format = mRequest.getParameter(1); 354 mData.putAUInt16(kSupportedObjectProperties, 355 sizeof(kSupportedObjectProperties) / sizeof(uint16_t)); 356 return MTP_RESPONSE_OK; 357} 358 359MtpResponseCode MtpServer::doGetObjectHandles() { 360 if (!mSessionOpen) 361 return MTP_RESPONSE_SESSION_NOT_OPEN; 362 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 363 MtpObjectFormat format = mRequest.getParameter(2); // 0x00000000 for all formats 364 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 365 // 0x00000000 for all objects? 366 367 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 368 mData.putAUInt32(handles); 369 delete handles; 370 return MTP_RESPONSE_OK; 371} 372 373MtpResponseCode MtpServer::doGetObjectPropValue() { 374 MtpObjectHandle handle = mRequest.getParameter(1); 375 MtpObjectProperty property = mRequest.getParameter(2); 376 377 return mDatabase->getObjectProperty(handle, property, mData); 378} 379 380MtpResponseCode MtpServer::doGetObjectInfo() { 381 MtpObjectHandle handle = mRequest.getParameter(1); 382 return mDatabase->getObjectInfo(handle, mData); 383} 384 385MtpResponseCode MtpServer::doGetObject() { 386 MtpObjectHandle handle = mRequest.getParameter(1); 387 MtpString filePath; 388 int64_t fileLength; 389 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength)) 390 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 391 392 mtp_file_range mfr; 393 mfr.path = filePath; 394 mfr.path_length = strlen(mfr.path); 395 mfr.offset = 0; 396 mfr.length = fileLength; 397 398 // send data header 399 mData.setOperationCode(mRequest.getOperationCode()); 400 mData.setTransactionID(mRequest.getTransactionID()); 401 mData.writeDataHeader(mFD, fileLength); 402 403 // then transfer the file 404 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 405 // FIXME - check for errors here 406 printf("MTP_SEND_FILE returned %d\n", ret); 407 return MTP_RESPONSE_OK; 408} 409 410MtpResponseCode MtpServer::doSendObjectInfo() { 411 MtpString path; 412 MtpStorageID storageID = mRequest.getParameter(1); 413 MtpStorage* storage = getStorage(storageID); 414 MtpObjectHandle parent = mRequest.getParameter(2); 415 if (!storage) 416 return MTP_RESPONSE_INVALID_STORAGE_ID; 417 418 // special case the root 419 if (parent == MTP_PARENT_ROOT) 420 path = storage->getPath(); 421 else { 422 int64_t dummy; 423 if (!mDatabase->getObjectFilePath(parent, path, dummy)) 424 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 425 } 426 427 // read only the fields we need 428 mData.getUInt32(); // storage ID 429 MtpObjectFormat format = mData.getUInt16(); 430 mData.getUInt16(); // protection status 431 mSendObjectFileSize = mData.getUInt32(); 432 mData.getUInt16(); // thumb format 433 mData.getUInt32(); // thumb compressed size 434 mData.getUInt32(); // thumb pix width 435 mData.getUInt32(); // thumb pix height 436 mData.getUInt32(); // image pix width 437 mData.getUInt32(); // image pix height 438 mData.getUInt32(); // image bit depth 439 mData.getUInt32(); // parent 440 uint16_t associationType = mData.getUInt16(); 441 uint32_t associationDesc = mData.getUInt32(); // association desc 442 mData.getUInt32(); // sequence number 443 MtpStringBuffer name, created, modified; 444 mData.getString(name); // file name 445 mData.getString(created); // date created 446 mData.getString(modified); // date modified 447 // keywords follow 448 449 time_t modifiedTime; 450 if (!parseDateTime(modified, modifiedTime)) 451 modifiedTime = 0; 452printf("SendObjectInfo format: %04X size: %d name: %s, created: %s, modified: %s\n", 453format, mSendObjectFileSize, (const char*)name, (const char*)created, (const char*)modified); 454 455 if (path[path.size() - 1] != '/') 456 path += "/"; 457 path += (const char *)name; 458 459 mDatabase->beginTransaction(); 460 MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID, 461 mSendObjectFileSize, modifiedTime); 462 if (handle == kInvalidObjectHandle) { 463 mDatabase->rollbackTransaction(); 464 return MTP_RESPONSE_GENERAL_ERROR; 465 } 466 uint32_t table = MtpDatabase::getTableForFile(format); 467 if (table == kObjectHandleTableAudio) 468 handle = mDatabase->addAudioFile(handle); 469 mDatabase->commitTransaction(); 470 471 if (format == MTP_FORMAT_ASSOCIATION) { 472 mode_t mask = umask(0); 473 int ret = mkdir((const char *)path, S_IRWXU | S_IRWXG | S_IRWXO); 474 umask(mask); 475 if (ret && ret != -EEXIST) 476 return MTP_RESPONSE_GENERAL_ERROR; 477 } else { 478 mSendObjectFilePath = path; 479 // save the handle for the SendObject call, which should follow 480 mSendObjectHandle = handle; 481 } 482 483 mResponse.setParameter(1, storageID); 484 mResponse.setParameter(2, parent); 485 mResponse.setParameter(3, handle); 486 487 return MTP_RESPONSE_OK; 488} 489 490MtpResponseCode MtpServer::doSendObject() { 491 if (mSendObjectHandle == kInvalidObjectHandle) { 492 fprintf(stderr, "Expected SendObjectInfo before SendObject\n"); 493 return MTP_RESPONSE_NO_VALID_OBJECT_INFO; 494 } 495 496 // read the header 497 int ret = mData.readDataHeader(mFD); 498 // FIXME - check for errors here. 499 500 // reset so we don't attempt to send this back 501 mData.reset(); 502 503 mtp_file_range mfr; 504 mfr.path = (const char*)mSendObjectFilePath; 505 mfr.path_length = strlen(mfr.path); 506 mfr.offset = 0; 507 mfr.length = mSendObjectFileSize; 508 509 // transfer the file 510 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 511 // FIXME - check for errors here. 512 // we need to return a reasonable response and delete 513 // mSendObjectHandle from the database if this fails. 514 printf("MTP_RECEIVE_FILE returned %d\n", ret); 515 516 mSendObjectHandle = kInvalidObjectHandle; 517 518 return MTP_RESPONSE_OK; 519} 520 521MtpResponseCode MtpServer::doDeleteObject() { 522 MtpObjectHandle handle = mRequest.getParameter(1); 523 MtpObjectFormat format = mRequest.getParameter(1); 524 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 525 // FIXME - implement deleting objects by format 526 // FIXME - handle non-empty directories 527 528 MtpString filePath; 529 int64_t fileLength; 530 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength)) 531 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 532 533printf("deleting %s\n", (const char *)filePath); 534 // one of these should work 535 rmdir((const char *)filePath); 536 unlink((const char *)filePath); 537 538 mDatabase->deleteFile(handle); 539 540 return MTP_RESPONSE_OK; 541} 542 543MtpResponseCode MtpServer::doGetObjectPropDesc() { 544 MtpObjectProperty property = mRequest.getParameter(1); 545 MtpObjectFormat format = mRequest.getParameter(2); 546 547 return -1; 548} 549 550} // namespace android 551