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