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