MtpServer.cpp revision 916076c6d84dac9b104fbdf94af5dcd7bce669fd
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 if (errno == ECANCELED) { 162 // return to top of loop and wait for next command 163 continue; 164 } 165 break; 166 } 167 MtpOperationCode operation = mRequest.getOperationCode(); 168 MtpTransactionID transaction = mRequest.getTransactionID(); 169 170 printf("operation: %s\n", MtpDebug::getOperationCodeName(operation)); 171 mRequest.dump(); 172 173 // FIXME need to generalize this 174 bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO); 175 if (dataIn) { 176 int ret = mData.read(fd); 177 if (ret < 0) { 178 fprintf(stderr, "data read returned %d, errno: %d\n", ret, errno); 179 if (errno == ECANCELED) { 180 // return to top of loop and wait for next command 181 continue; 182 } 183 break; 184 } 185 printf("received data:\n"); 186 mData.dump(); 187 } else { 188 mData.reset(); 189 } 190 191 if (handleRequest()) { 192 if (!dataIn && mData.hasData()) { 193 mData.setOperationCode(operation); 194 mData.setTransactionID(transaction); 195 printf("sending data:\n"); 196 mData.dump(); 197 ret = mData.write(fd); 198 if (ret < 0) { 199 fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno); 200 if (errno == ECANCELED) { 201 // return to top of loop and wait for next command 202 continue; 203 } 204 break; 205 } 206 } 207 208 mResponse.setTransactionID(transaction); 209 printf("sending response %04X\n", mResponse.getResponseCode()); 210 ret = mResponse.write(fd); 211 if (ret < 0) { 212 fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno); 213 if (errno == ECANCELED) { 214 // return to top of loop and wait for next command 215 continue; 216 } 217 break; 218 } 219 } else { 220 printf("skipping response\n"); 221 } 222 } 223} 224 225bool MtpServer::handleRequest() { 226 MtpOperationCode operation = mRequest.getOperationCode(); 227 MtpResponseCode response; 228 229 mResponse.reset(); 230 231 if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) { 232 // FIXME - need to delete mSendObjectHandle from the database 233 fprintf(stderr, "expected SendObject after SendObjectInfo\n"); 234 mSendObjectHandle = kInvalidObjectHandle; 235 } 236 237 switch (operation) { 238 case MTP_OPERATION_GET_DEVICE_INFO: 239 response = doGetDeviceInfo(); 240 break; 241 case MTP_OPERATION_OPEN_SESSION: 242 response = doOpenSession(); 243 break; 244 case MTP_OPERATION_CLOSE_SESSION: 245 response = doCloseSession(); 246 break; 247 case MTP_OPERATION_GET_STORAGE_IDS: 248 response = doGetStorageIDs(); 249 break; 250 case MTP_OPERATION_GET_STORAGE_INFO: 251 response = doGetStorageInfo(); 252 break; 253 case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED: 254 response = doGetObjectPropsSupported(); 255 break; 256 case MTP_OPERATION_GET_OBJECT_HANDLES: 257 response = doGetObjectHandles(); 258 break; 259 case MTP_OPERATION_GET_OBJECT_PROP_VALUE: 260 response = doGetObjectPropValue(); 261 break; 262 case MTP_OPERATION_GET_OBJECT_INFO: 263 response = doGetObjectInfo(); 264 break; 265 case MTP_OPERATION_GET_OBJECT: 266 response = doGetObject(); 267 break; 268 case MTP_OPERATION_SEND_OBJECT_INFO: 269 response = doSendObjectInfo(); 270 break; 271 case MTP_OPERATION_SEND_OBJECT: 272 response = doSendObject(); 273 break; 274 case MTP_OPERATION_DELETE_OBJECT: 275 response = doDeleteObject(); 276 break; 277 case MTP_OPERATION_GET_OBJECT_PROP_DESC: 278 default: 279 response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; 280 break; 281 } 282 283 if (response == MTP_RESPONSE_TRANSACTION_CANCELLED) 284 return false; 285 mResponse.setResponseCode(response); 286 return true; 287} 288 289MtpResponseCode MtpServer::doGetDeviceInfo() { 290 MtpStringBuffer string; 291 292 // fill in device info 293 mData.putUInt16(MTP_STANDARD_VERSION); 294 mData.putUInt32(6); // MTP Vendor Extension ID 295 mData.putUInt16(MTP_STANDARD_VERSION); 296 string.set("microsoft.com: 1.0;"); 297 mData.putString(string); // MTP Extensions 298 mData.putUInt16(0); //Functional Mode 299 mData.putAUInt16(kSupportedOperationCodes, 300 sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported 301 mData.putEmptyArray(); // Events Supported 302 mData.putEmptyArray(); // Device Properties Supported 303 mData.putEmptyArray(); // Capture Formats 304 mData.putAUInt16(kSupportedPlaybackFormats, 305 sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats 306 // FIXME 307 string.set("Google, Inc."); 308 mData.putString(string); // Manufacturer 309 string.set("Just an Ordinary MTP Device"); 310 mData.putString(string); // Model 311 string.set("1.0"); 312 mData.putString(string); // Device Version 313 string.set("123456789012345678AA"); 314 mData.putString(string); // Serial Number 315 316 return MTP_RESPONSE_OK; 317} 318 319MtpResponseCode MtpServer::doOpenSession() { 320 if (mSessionOpen) { 321 mResponse.setParameter(1, mSessionID); 322 return MTP_RESPONSE_SESSION_ALREADY_OPEN; 323 } 324 mSessionID = mRequest.getParameter(1); 325 mSessionOpen = true; 326 return MTP_RESPONSE_OK; 327} 328 329MtpResponseCode MtpServer::doCloseSession() { 330 if (!mSessionOpen) 331 return MTP_RESPONSE_SESSION_NOT_OPEN; 332 mSessionID = 0; 333 mSessionOpen = false; 334 return MTP_RESPONSE_OK; 335} 336 337MtpResponseCode MtpServer::doGetStorageIDs() { 338 if (!mSessionOpen) 339 return MTP_RESPONSE_SESSION_NOT_OPEN; 340 341 int count = mStorages.size(); 342 mData.putUInt32(count); 343 for (int i = 0; i < count; i++) 344 mData.putUInt32(mStorages[i]->getStorageID()); 345 346 return MTP_RESPONSE_OK; 347} 348 349MtpResponseCode MtpServer::doGetStorageInfo() { 350 MtpStringBuffer string; 351 352 if (!mSessionOpen) 353 return MTP_RESPONSE_SESSION_NOT_OPEN; 354 MtpStorageID id = mRequest.getParameter(1); 355 MtpStorage* storage = getStorage(id); 356 if (!storage) 357 return MTP_RESPONSE_INVALID_STORAGE_ID; 358 359 mData.putUInt16(storage->getType()); 360 mData.putUInt16(storage->getFileSystemType()); 361 mData.putUInt16(storage->getAccessCapability()); 362 mData.putUInt64(storage->getMaxCapacity()); 363 mData.putUInt64(storage->getFreeSpace()); 364 mData.putUInt32(1024*1024*1024); // Free Space in Objects 365 string.set(storage->getDescription()); 366 mData.putString(string); 367 mData.putEmptyString(); // Volume Identifier 368 369 return MTP_RESPONSE_OK; 370} 371 372MtpResponseCode MtpServer::doGetObjectPropsSupported() { 373 if (!mSessionOpen) 374 return MTP_RESPONSE_SESSION_NOT_OPEN; 375 MtpObjectFormat format = mRequest.getParameter(1); 376 mData.putAUInt16(kSupportedObjectProperties, 377 sizeof(kSupportedObjectProperties) / sizeof(uint16_t)); 378 return MTP_RESPONSE_OK; 379} 380 381MtpResponseCode MtpServer::doGetObjectHandles() { 382 if (!mSessionOpen) 383 return MTP_RESPONSE_SESSION_NOT_OPEN; 384 MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage 385 MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats 386 MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent 387 // 0x00000000 for all objects? 388 389 MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent); 390 mData.putAUInt32(handles); 391 delete handles; 392 return MTP_RESPONSE_OK; 393} 394 395MtpResponseCode MtpServer::doGetObjectPropValue() { 396 MtpObjectHandle handle = mRequest.getParameter(1); 397 MtpObjectProperty property = mRequest.getParameter(2); 398 399 return mDatabase->getObjectProperty(handle, property, mData); 400} 401 402MtpResponseCode MtpServer::doGetObjectInfo() { 403 MtpObjectHandle handle = mRequest.getParameter(1); 404 return mDatabase->getObjectInfo(handle, mData); 405} 406 407MtpResponseCode MtpServer::doGetObject() { 408 MtpObjectHandle handle = mRequest.getParameter(1); 409 MtpString filePath; 410 int64_t fileLength; 411 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength)) 412 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 413 414 mtp_file_range mfr; 415 mfr.path = filePath; 416 mfr.path_length = strlen(mfr.path); 417 mfr.offset = 0; 418 mfr.length = fileLength; 419 420 // send data header 421 mData.setOperationCode(mRequest.getOperationCode()); 422 mData.setTransactionID(mRequest.getTransactionID()); 423 mData.writeDataHeader(mFD, fileLength); 424 425 // then transfer the file 426 int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr); 427 if (ret < 0) { 428 if (errno == ECANCELED) 429 return MTP_RESPONSE_TRANSACTION_CANCELLED; 430 else 431 return MTP_RESPONSE_GENERAL_ERROR; 432 } 433 return MTP_RESPONSE_OK; 434} 435 436MtpResponseCode MtpServer::doSendObjectInfo() { 437 MtpString path; 438 MtpStorageID storageID = mRequest.getParameter(1); 439 MtpStorage* storage = getStorage(storageID); 440 MtpObjectHandle parent = mRequest.getParameter(2); 441 if (!storage) 442 return MTP_RESPONSE_INVALID_STORAGE_ID; 443 444 // special case the root 445 if (parent == MTP_PARENT_ROOT) 446 path = storage->getPath(); 447 else { 448 int64_t dummy; 449 if (!mDatabase->getObjectFilePath(parent, path, dummy)) 450 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 451 } 452 453 // read only the fields we need 454 mData.getUInt32(); // storage ID 455 MtpObjectFormat format = mData.getUInt16(); 456 mData.getUInt16(); // protection status 457 mSendObjectFileSize = mData.getUInt32(); 458 mData.getUInt16(); // thumb format 459 mData.getUInt32(); // thumb compressed size 460 mData.getUInt32(); // thumb pix width 461 mData.getUInt32(); // thumb pix height 462 mData.getUInt32(); // image pix width 463 mData.getUInt32(); // image pix height 464 mData.getUInt32(); // image bit depth 465 mData.getUInt32(); // parent 466 uint16_t associationType = mData.getUInt16(); 467 uint32_t associationDesc = mData.getUInt32(); // association desc 468 mData.getUInt32(); // sequence number 469 MtpStringBuffer name, created, modified; 470 mData.getString(name); // file name 471 mData.getString(created); // date created 472 mData.getString(modified); // date modified 473 // keywords follow 474 475 time_t modifiedTime; 476 if (!parseDateTime(modified, modifiedTime)) 477 modifiedTime = 0; 478printf("SendObjectInfo format: %04X size: %d name: %s, created: %s, modified: %s\n", 479format, mSendObjectFileSize, (const char*)name, (const char*)created, (const char*)modified); 480 481 if (path[path.size() - 1] != '/') 482 path += "/"; 483 path += (const char *)name; 484 485 mDatabase->beginTransaction(); 486 MtpObjectHandle handle = mDatabase->addFile((const char*)path, format, parent, storageID, 487 mSendObjectFileSize, modifiedTime); 488 if (handle == kInvalidObjectHandle) { 489 mDatabase->rollbackTransaction(); 490 return MTP_RESPONSE_GENERAL_ERROR; 491 } 492 uint32_t table = MtpDatabase::getTableForFile(format); 493 if (table == kObjectHandleTableAudio) 494 handle = mDatabase->addAudioFile(handle); 495 mDatabase->commitTransaction(); 496 497 if (format == MTP_FORMAT_ASSOCIATION) { 498 mode_t mask = umask(0); 499 int ret = mkdir((const char *)path, S_IRWXU | S_IRWXG | S_IRWXO); 500 umask(mask); 501 if (ret && ret != -EEXIST) 502 return MTP_RESPONSE_GENERAL_ERROR; 503 } else { 504 mSendObjectFilePath = path; 505 // save the handle for the SendObject call, which should follow 506 mSendObjectHandle = handle; 507 } 508 509 mResponse.setParameter(1, storageID); 510 mResponse.setParameter(2, parent); 511 mResponse.setParameter(3, handle); 512 513 return MTP_RESPONSE_OK; 514} 515 516MtpResponseCode MtpServer::doSendObject() { 517 if (mSendObjectHandle == kInvalidObjectHandle) { 518 fprintf(stderr, "Expected SendObjectInfo before SendObject\n"); 519 return MTP_RESPONSE_NO_VALID_OBJECT_INFO; 520 } 521 522 // read the header 523 int ret = mData.readDataHeader(mFD); 524 // FIXME - check for errors here. 525 526 // reset so we don't attempt to send this back 527 mData.reset(); 528 529 mtp_file_range mfr; 530 mfr.path = (const char*)mSendObjectFilePath; 531 mfr.path_length = strlen(mfr.path); 532 mfr.offset = 0; 533 mfr.length = mSendObjectFileSize; 534 535 // transfer the file 536 ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr); 537 // FIXME - we need to delete mSendObjectHandle from the database if this fails. 538 printf("MTP_RECEIVE_FILE returned %d\n", ret); 539 mSendObjectHandle = kInvalidObjectHandle; 540 541 if (ret < 0) { 542 unlink(mSendObjectFilePath); 543 if (errno == ECANCELED) 544 return MTP_RESPONSE_TRANSACTION_CANCELLED; 545 else 546 return MTP_RESPONSE_GENERAL_ERROR; 547 } 548 return MTP_RESPONSE_OK; 549} 550 551MtpResponseCode MtpServer::doDeleteObject() { 552 MtpObjectHandle handle = mRequest.getParameter(1); 553 MtpObjectFormat format = mRequest.getParameter(1); 554 // FIXME - support deleting all objects if handle is 0xFFFFFFFF 555 // FIXME - implement deleting objects by format 556 // FIXME - handle non-empty directories 557 558 MtpString filePath; 559 int64_t fileLength; 560 if (!mDatabase->getObjectFilePath(handle, filePath, fileLength)) 561 return MTP_RESPONSE_INVALID_OBJECT_HANDLE; 562 563printf("deleting %s\n", (const char *)filePath); 564 // one of these should work 565 rmdir((const char *)filePath); 566 unlink((const char *)filePath); 567 568 mDatabase->deleteFile(handle); 569 570 return MTP_RESPONSE_OK; 571} 572 573MtpResponseCode MtpServer::doGetObjectPropDesc() { 574 MtpObjectProperty property = mRequest.getParameter(1); 575 MtpObjectFormat format = mRequest.getParameter(2); 576 577 return -1; 578} 579 580} // namespace android 581