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