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