Loading media/mtp/MtpDatabase.h +3 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,9 @@ public: virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property) = 0; virtual MtpResponseCode moveObject(MtpObjectHandle handle, MtpObjectHandle newParent, MtpString& newPath) = 0; virtual void sessionStarted() = 0; virtual void sessionEnded() = 0; Loading media/mtp/MtpServer.cpp +139 −61 Original line number Diff line number Diff line Loading @@ -67,8 +67,8 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_SET_DEVICE_PROP_VALUE, MTP_OPERATION_RESET_DEVICE_PROP_VALUE, // MTP_OPERATION_TERMINATE_OPEN_CAPTURE, // MTP_OPERATION_MOVE_OBJECT, // MTP_OPERATION_COPY_OBJECT, MTP_OPERATION_MOVE_OBJECT, MTP_OPERATION_COPY_OBJECT, MTP_OPERATION_GET_PARTIAL_OBJECT, // MTP_OPERATION_INITIATE_OPEN_CAPTURE, MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, Loading Loading @@ -437,6 +437,12 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_DELETE_OBJECT: response = doDeleteObject(); break; case MTP_OPERATION_COPY_OBJECT: response = doCopyObject(); break; case MTP_OPERATION_MOVE_OBJECT: response = doMoveObject(); break; case MTP_OPERATION_GET_OBJECT_PROP_DESC: response = doGetObjectPropDesc(); break; Loading Loading @@ -1020,6 +1026,137 @@ MtpResponseCode MtpServer::doSendObjectInfo() { return MTP_RESPONSE_OK; } MtpResponseCode MtpServer::doMoveObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; if (mRequest.getParameterCount() < 3) return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle objectHandle = mRequest.getParameter(1); MtpStorageID storageID = mRequest.getParameter(2); MtpStorage* storage = getStorage(storageID); MtpObjectHandle parent = mRequest.getParameter(3); if (!storage) return MTP_RESPONSE_INVALID_STORAGE_ID; MtpString path; MtpResponseCode result; MtpString fromPath; int64_t fileLength; MtpObjectFormat format; MtpObjectInfo info(objectHandle); result = mDatabase->getObjectInfo(objectHandle, info); if (result != MTP_RESPONSE_OK) return result; result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format); if (result != MTP_RESPONSE_OK) return result; // special case the root if (parent == 0) { path = storage->getPath(); } else { int64_t parentLength; MtpObjectFormat parentFormat; result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat); if (result != MTP_RESPONSE_OK) return result; if (parentFormat != MTP_FORMAT_ASSOCIATION) return MTP_RESPONSE_INVALID_PARENT_OBJECT; } if (path[path.size() - 1] != '/') path += "/"; path += info.mName; result = mDatabase->moveObject(objectHandle, parent, path); if (result != MTP_RESPONSE_OK) return result; if (info.mStorageID == storageID) { ALOGV("Moving file from %s to %s", (const char*)fromPath, (const char*)path); if (rename(fromPath, path)) { ALOGE("rename() failed from %s to %s", (const char*)fromPath, (const char*)path); result = MTP_RESPONSE_GENERAL_ERROR; } } else { ALOGV("Moving across storages from %s to %s", (const char*)fromPath, (const char*)path); if (copyFile(fromPath, path)) { result = MTP_RESPONSE_GENERAL_ERROR; } else { deletePath(fromPath); } } // If the move failed, undo the database change if (result != MTP_RESPONSE_OK) if (mDatabase->moveObject(objectHandle, info.mParent, fromPath) != MTP_RESPONSE_OK) ALOGE("Couldn't undo failed move"); return result; } MtpResponseCode MtpServer::doCopyObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; MtpResponseCode result = MTP_RESPONSE_OK; if (mRequest.getParameterCount() < 3) return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle objectHandle = mRequest.getParameter(1); MtpStorageID storageID = mRequest.getParameter(2); MtpStorage* storage = getStorage(storageID); MtpObjectHandle parent = mRequest.getParameter(3); if (!storage) return MTP_RESPONSE_INVALID_STORAGE_ID; MtpString path; MtpString fromPath; int64_t fileLength; MtpObjectFormat format; MtpObjectInfo info(objectHandle); result = mDatabase->getObjectInfo(objectHandle, info); if (result != MTP_RESPONSE_OK) return result; result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format); if (result != MTP_RESPONSE_OK) return result; // special case the root if (parent == 0) { path = storage->getPath(); } else { int64_t parentLength; MtpObjectFormat parentFormat; result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat); if (result != MTP_RESPONSE_OK) return result; if (parentFormat != MTP_FORMAT_ASSOCIATION) return MTP_RESPONSE_INVALID_PARENT_OBJECT; } // check space first if ((uint64_t) fileLength > storage->getFreeSpace()) return MTP_RESPONSE_STORAGE_FULL; if (path[path.size() - 1] != '/') path += "/"; path += info.mName; MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, format, parent, storageID, fileLength, info.mDateModified); if (handle == kInvalidObjectHandle) { return MTP_RESPONSE_GENERAL_ERROR; } ALOGV("Copying file from %s to %s", (const char*)fromPath, (const char*)path); if (copyFile(fromPath, path)) { result = MTP_RESPONSE_GENERAL_ERROR; } mDatabase->endSendObject(path, handle, format, result); mResponse.setParameter(1, handle); return result; } MtpResponseCode MtpServer::doSendObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; Loading Loading @@ -1124,65 +1261,6 @@ done: return result; } static void deleteRecursive(const char* path) { char pathbuf[PATH_MAX]; size_t pathLength = strlen(path); if (pathLength >= sizeof(pathbuf) - 1) { ALOGE("path too long: %s\n", path); } strcpy(pathbuf, path); if (pathbuf[pathLength - 1] != '/') { pathbuf[pathLength++] = '/'; } char* fileSpot = pathbuf + pathLength; int pathRemaining = sizeof(pathbuf) - pathLength - 1; DIR* dir = opendir(path); if (!dir) { ALOGE("opendir %s failed: %s", path, strerror(errno)); return; } struct dirent* entry; while ((entry = readdir(dir))) { const char* name = entry->d_name; // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } int nameLength = strlen(name); if (nameLength > pathRemaining) { ALOGE("path %s/%s too long\n", path, name); continue; } strcpy(fileSpot, name); if (entry->d_type == DT_DIR) { deleteRecursive(pathbuf); rmdir(pathbuf); } else { unlink(pathbuf); } } closedir(dir); } static void deletePath(const char* path) { struct stat statbuf; if (stat(path, &statbuf) == 0) { if (S_ISDIR(statbuf.st_mode)) { deleteRecursive(path); rmdir(path); } else { unlink(path); } } else { ALOGE("deletePath stat failed for %s: %s", path, strerror(errno)); } } MtpResponseCode MtpServer::doDeleteObject() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; Loading media/mtp/MtpServer.h +2 −0 Original line number Diff line number Diff line Loading @@ -161,6 +161,8 @@ private: MtpResponseCode doSendObjectInfo(); MtpResponseCode doSendObject(); MtpResponseCode doDeleteObject(); MtpResponseCode doMoveObject(); MtpResponseCode doCopyObject(); MtpResponseCode doGetObjectPropDesc(); MtpResponseCode doGetDevicePropDesc(); MtpResponseCode doSendPartialObject(); Loading media/mtp/MtpUtils.cpp +107 −0 Original line number Diff line number Diff line Loading @@ -16,13 +16,23 @@ #define LOG_TAG "MtpUtils" #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <dirent.h> #include <fcntl.h> #include <sys/sendfile.h> #include <sys/stat.h> #include <sys/types.h> #include <stdio.h> #include <time.h> #include <unistd.h> #include "MtpUtils.h" namespace android { constexpr unsigned long FILE_COPY_SIZE = 262144; /* DateTime strings follow a compatible subset of the definition found in ISO 8601, and take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this Loading Loading @@ -78,4 +88,101 @@ void formatDateTime(time_t seconds, char* buffer, int bufferLength) { tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } int copyFile(const char *fromPath, const char *toPath) { auto start = std::chrono::steady_clock::now(); android::base::unique_fd fromFd(open(fromPath, O_RDONLY)); if (fromFd == -1) { PLOG(ERROR) << "Failed to open copy from " << fromPath; return -1; } android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)); if (toFd == -1) { PLOG(ERROR) << "Failed to open copy to " << toPath; return -1; } off_t offset = 0; struct stat sstat = {}; if (stat(fromPath, &sstat) == -1) return -1; off_t length = sstat.st_size; int ret = 0; while (offset < length) { ssize_t transfer_length = std::min(length - offset, (off_t) FILE_COPY_SIZE); ret = sendfile(toFd, fromFd, &offset, transfer_length); if (ret != transfer_length) { ret = -1; PLOG(ERROR) << "Copying failed!"; break; } } auto end = std::chrono::steady_clock::now(); std::chrono::duration<double> diff = end - start; LOG(INFO) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length << ", Rate: " << ((double) length) / diff.count() << " bytes/s"; return ret == -1 ? -1 : 0; } void deleteRecursive(const char* path) { char pathbuf[PATH_MAX]; size_t pathLength = strlen(path); if (pathLength >= sizeof(pathbuf) - 1) { LOG(ERROR) << "path too long: " << path; } strcpy(pathbuf, path); if (pathbuf[pathLength - 1] != '/') { pathbuf[pathLength++] = '/'; } char* fileSpot = pathbuf + pathLength; int pathRemaining = sizeof(pathbuf) - pathLength - 1; DIR* dir = opendir(path); if (!dir) { PLOG(ERROR) << "opendir " << path << " failed"; return; } struct dirent* entry; while ((entry = readdir(dir))) { const char* name = entry->d_name; // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } int nameLength = strlen(name); if (nameLength > pathRemaining) { LOG(ERROR) << "path " << path << "/" << name << " too long"; continue; } strcpy(fileSpot, name); if (entry->d_type == DT_DIR) { deleteRecursive(pathbuf); rmdir(pathbuf); } else { unlink(pathbuf); } } closedir(dir); } void deletePath(const char* path) { struct stat statbuf; if (stat(path, &statbuf) == 0) { if (S_ISDIR(statbuf.st_mode)) { deleteRecursive(path); rmdir(path); } else { unlink(path); } } else { PLOG(ERROR) << "deletePath stat failed for " << path;; } } } // namespace android media/mtp/MtpUtils.h +4 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,10 @@ namespace android { bool parseDateTime(const char* dateTime, time_t& outSeconds); void formatDateTime(time_t seconds, char* buffer, int bufferLength); int copyFile(const char *fromPath, const char *toPath); void deleteRecursive(const char* path); void deletePath(const char* path); }; // namespace android #endif // _MTP_UTILS_H Loading
media/mtp/MtpDatabase.h +3 −0 Original line number Diff line number Diff line Loading @@ -103,6 +103,9 @@ public: virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property) = 0; virtual MtpResponseCode moveObject(MtpObjectHandle handle, MtpObjectHandle newParent, MtpString& newPath) = 0; virtual void sessionStarted() = 0; virtual void sessionEnded() = 0; Loading
media/mtp/MtpServer.cpp +139 −61 Original line number Diff line number Diff line Loading @@ -67,8 +67,8 @@ static const MtpOperationCode kSupportedOperationCodes[] = { MTP_OPERATION_SET_DEVICE_PROP_VALUE, MTP_OPERATION_RESET_DEVICE_PROP_VALUE, // MTP_OPERATION_TERMINATE_OPEN_CAPTURE, // MTP_OPERATION_MOVE_OBJECT, // MTP_OPERATION_COPY_OBJECT, MTP_OPERATION_MOVE_OBJECT, MTP_OPERATION_COPY_OBJECT, MTP_OPERATION_GET_PARTIAL_OBJECT, // MTP_OPERATION_INITIATE_OPEN_CAPTURE, MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, Loading Loading @@ -437,6 +437,12 @@ bool MtpServer::handleRequest() { case MTP_OPERATION_DELETE_OBJECT: response = doDeleteObject(); break; case MTP_OPERATION_COPY_OBJECT: response = doCopyObject(); break; case MTP_OPERATION_MOVE_OBJECT: response = doMoveObject(); break; case MTP_OPERATION_GET_OBJECT_PROP_DESC: response = doGetObjectPropDesc(); break; Loading Loading @@ -1020,6 +1026,137 @@ MtpResponseCode MtpServer::doSendObjectInfo() { return MTP_RESPONSE_OK; } MtpResponseCode MtpServer::doMoveObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; if (mRequest.getParameterCount() < 3) return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle objectHandle = mRequest.getParameter(1); MtpStorageID storageID = mRequest.getParameter(2); MtpStorage* storage = getStorage(storageID); MtpObjectHandle parent = mRequest.getParameter(3); if (!storage) return MTP_RESPONSE_INVALID_STORAGE_ID; MtpString path; MtpResponseCode result; MtpString fromPath; int64_t fileLength; MtpObjectFormat format; MtpObjectInfo info(objectHandle); result = mDatabase->getObjectInfo(objectHandle, info); if (result != MTP_RESPONSE_OK) return result; result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format); if (result != MTP_RESPONSE_OK) return result; // special case the root if (parent == 0) { path = storage->getPath(); } else { int64_t parentLength; MtpObjectFormat parentFormat; result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat); if (result != MTP_RESPONSE_OK) return result; if (parentFormat != MTP_FORMAT_ASSOCIATION) return MTP_RESPONSE_INVALID_PARENT_OBJECT; } if (path[path.size() - 1] != '/') path += "/"; path += info.mName; result = mDatabase->moveObject(objectHandle, parent, path); if (result != MTP_RESPONSE_OK) return result; if (info.mStorageID == storageID) { ALOGV("Moving file from %s to %s", (const char*)fromPath, (const char*)path); if (rename(fromPath, path)) { ALOGE("rename() failed from %s to %s", (const char*)fromPath, (const char*)path); result = MTP_RESPONSE_GENERAL_ERROR; } } else { ALOGV("Moving across storages from %s to %s", (const char*)fromPath, (const char*)path); if (copyFile(fromPath, path)) { result = MTP_RESPONSE_GENERAL_ERROR; } else { deletePath(fromPath); } } // If the move failed, undo the database change if (result != MTP_RESPONSE_OK) if (mDatabase->moveObject(objectHandle, info.mParent, fromPath) != MTP_RESPONSE_OK) ALOGE("Couldn't undo failed move"); return result; } MtpResponseCode MtpServer::doCopyObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; MtpResponseCode result = MTP_RESPONSE_OK; if (mRequest.getParameterCount() < 3) return MTP_RESPONSE_INVALID_PARAMETER; MtpObjectHandle objectHandle = mRequest.getParameter(1); MtpStorageID storageID = mRequest.getParameter(2); MtpStorage* storage = getStorage(storageID); MtpObjectHandle parent = mRequest.getParameter(3); if (!storage) return MTP_RESPONSE_INVALID_STORAGE_ID; MtpString path; MtpString fromPath; int64_t fileLength; MtpObjectFormat format; MtpObjectInfo info(objectHandle); result = mDatabase->getObjectInfo(objectHandle, info); if (result != MTP_RESPONSE_OK) return result; result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format); if (result != MTP_RESPONSE_OK) return result; // special case the root if (parent == 0) { path = storage->getPath(); } else { int64_t parentLength; MtpObjectFormat parentFormat; result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat); if (result != MTP_RESPONSE_OK) return result; if (parentFormat != MTP_FORMAT_ASSOCIATION) return MTP_RESPONSE_INVALID_PARENT_OBJECT; } // check space first if ((uint64_t) fileLength > storage->getFreeSpace()) return MTP_RESPONSE_STORAGE_FULL; if (path[path.size() - 1] != '/') path += "/"; path += info.mName; MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, format, parent, storageID, fileLength, info.mDateModified); if (handle == kInvalidObjectHandle) { return MTP_RESPONSE_GENERAL_ERROR; } ALOGV("Copying file from %s to %s", (const char*)fromPath, (const char*)path); if (copyFile(fromPath, path)) { result = MTP_RESPONSE_GENERAL_ERROR; } mDatabase->endSendObject(path, handle, format, result); mResponse.setParameter(1, handle); return result; } MtpResponseCode MtpServer::doSendObject() { if (!hasStorage()) return MTP_RESPONSE_GENERAL_ERROR; Loading Loading @@ -1124,65 +1261,6 @@ done: return result; } static void deleteRecursive(const char* path) { char pathbuf[PATH_MAX]; size_t pathLength = strlen(path); if (pathLength >= sizeof(pathbuf) - 1) { ALOGE("path too long: %s\n", path); } strcpy(pathbuf, path); if (pathbuf[pathLength - 1] != '/') { pathbuf[pathLength++] = '/'; } char* fileSpot = pathbuf + pathLength; int pathRemaining = sizeof(pathbuf) - pathLength - 1; DIR* dir = opendir(path); if (!dir) { ALOGE("opendir %s failed: %s", path, strerror(errno)); return; } struct dirent* entry; while ((entry = readdir(dir))) { const char* name = entry->d_name; // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } int nameLength = strlen(name); if (nameLength > pathRemaining) { ALOGE("path %s/%s too long\n", path, name); continue; } strcpy(fileSpot, name); if (entry->d_type == DT_DIR) { deleteRecursive(pathbuf); rmdir(pathbuf); } else { unlink(pathbuf); } } closedir(dir); } static void deletePath(const char* path) { struct stat statbuf; if (stat(path, &statbuf) == 0) { if (S_ISDIR(statbuf.st_mode)) { deleteRecursive(path); rmdir(path); } else { unlink(path); } } else { ALOGE("deletePath stat failed for %s: %s", path, strerror(errno)); } } MtpResponseCode MtpServer::doDeleteObject() { if (!hasStorage()) return MTP_RESPONSE_INVALID_OBJECT_HANDLE; Loading
media/mtp/MtpServer.h +2 −0 Original line number Diff line number Diff line Loading @@ -161,6 +161,8 @@ private: MtpResponseCode doSendObjectInfo(); MtpResponseCode doSendObject(); MtpResponseCode doDeleteObject(); MtpResponseCode doMoveObject(); MtpResponseCode doCopyObject(); MtpResponseCode doGetObjectPropDesc(); MtpResponseCode doGetDevicePropDesc(); MtpResponseCode doSendPartialObject(); Loading
media/mtp/MtpUtils.cpp +107 −0 Original line number Diff line number Diff line Loading @@ -16,13 +16,23 @@ #define LOG_TAG "MtpUtils" #include <android-base/logging.h> #include <android-base/unique_fd.h> #include <dirent.h> #include <fcntl.h> #include <sys/sendfile.h> #include <sys/stat.h> #include <sys/types.h> #include <stdio.h> #include <time.h> #include <unistd.h> #include "MtpUtils.h" namespace android { constexpr unsigned long FILE_COPY_SIZE = 262144; /* DateTime strings follow a compatible subset of the definition found in ISO 8601, and take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this Loading Loading @@ -78,4 +88,101 @@ void formatDateTime(time_t seconds, char* buffer, int bufferLength) { tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } int copyFile(const char *fromPath, const char *toPath) { auto start = std::chrono::steady_clock::now(); android::base::unique_fd fromFd(open(fromPath, O_RDONLY)); if (fromFd == -1) { PLOG(ERROR) << "Failed to open copy from " << fromPath; return -1; } android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)); if (toFd == -1) { PLOG(ERROR) << "Failed to open copy to " << toPath; return -1; } off_t offset = 0; struct stat sstat = {}; if (stat(fromPath, &sstat) == -1) return -1; off_t length = sstat.st_size; int ret = 0; while (offset < length) { ssize_t transfer_length = std::min(length - offset, (off_t) FILE_COPY_SIZE); ret = sendfile(toFd, fromFd, &offset, transfer_length); if (ret != transfer_length) { ret = -1; PLOG(ERROR) << "Copying failed!"; break; } } auto end = std::chrono::steady_clock::now(); std::chrono::duration<double> diff = end - start; LOG(INFO) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length << ", Rate: " << ((double) length) / diff.count() << " bytes/s"; return ret == -1 ? -1 : 0; } void deleteRecursive(const char* path) { char pathbuf[PATH_MAX]; size_t pathLength = strlen(path); if (pathLength >= sizeof(pathbuf) - 1) { LOG(ERROR) << "path too long: " << path; } strcpy(pathbuf, path); if (pathbuf[pathLength - 1] != '/') { pathbuf[pathLength++] = '/'; } char* fileSpot = pathbuf + pathLength; int pathRemaining = sizeof(pathbuf) - pathLength - 1; DIR* dir = opendir(path); if (!dir) { PLOG(ERROR) << "opendir " << path << " failed"; return; } struct dirent* entry; while ((entry = readdir(dir))) { const char* name = entry->d_name; // ignore "." and ".." if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) { continue; } int nameLength = strlen(name); if (nameLength > pathRemaining) { LOG(ERROR) << "path " << path << "/" << name << " too long"; continue; } strcpy(fileSpot, name); if (entry->d_type == DT_DIR) { deleteRecursive(pathbuf); rmdir(pathbuf); } else { unlink(pathbuf); } } closedir(dir); } void deletePath(const char* path) { struct stat statbuf; if (stat(path, &statbuf) == 0) { if (S_ISDIR(statbuf.st_mode)) { deleteRecursive(path); rmdir(path); } else { unlink(path); } } else { PLOG(ERROR) << "deletePath stat failed for " << path;; } } } // namespace android
media/mtp/MtpUtils.h +4 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,10 @@ namespace android { bool parseDateTime(const char* dateTime, time_t& outSeconds); void formatDateTime(time_t seconds, char* buffer, int bufferLength); int copyFile(const char *fromPath, const char *toPath); void deleteRecursive(const char* path); void deletePath(const char* path); }; // namespace android #endif // _MTP_UTILS_H