summaryrefslogtreecommitdiffstats
path: root/media/tests/mtp
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2010-07-26 20:44:42 -0400
committerMike Lockwood <lockwood@android.com>2010-07-26 20:47:35 -0400
commit5fd1ff0aa370958dfdbabb6026c1d383d17df97f (patch)
tree8458539ce6e0d8a1aa078e93568a5b0d0c034ed8 /media/tests/mtp
parentad0643a330db13c8f11b1a71fbb7262570114f4d (diff)
downloadframeworks_base-5fd1ff0aa370958dfdbabb6026c1d383d17df97f.zip
frameworks_base-5fd1ff0aa370958dfdbabb6026c1d383d17df97f.tar.gz
frameworks_base-5fd1ff0aa370958dfdbabb6026c1d383d17df97f.tar.bz2
Simple command line test tool for MTP host.
Change-Id: Ifd13e1ca5d49a5477a9850d94d443a50bbc32ff1 Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'media/tests/mtp')
-rw-r--r--media/tests/mtp/Android.mk57
-rw-r--r--media/tests/mtp/MtpFile.cpp187
-rw-r--r--media/tests/mtp/MtpFile.h57
-rw-r--r--media/tests/mtp/mtp.cpp370
4 files changed, 671 insertions, 0 deletions
diff --git a/media/tests/mtp/Android.mk b/media/tests/mtp/Android.mk
new file mode 100644
index 0000000..8cdfb69
--- /dev/null
+++ b/media/tests/mtp/Android.mk
@@ -0,0 +1,57 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ mtp.cpp \
+ MtpFile.cpp \
+
+LOCAL_C_INCLUDES += \
+ frameworks/base/media/mtp \
+
+LOCAL_CFLAGS := -DMTP_HOST
+
+LOCAL_MODULE := mtp
+
+LOCAL_STATIC_LIBRARIES := libmtp libusbhost libutils libcutils
+
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(HOST_OS),linux)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ mtp.cpp \
+ MtpFile.cpp \
+ ../../../libs/utils/RefBase.cpp \
+ ../../../libs/utils/SharedBuffer.cpp \
+ ../../../libs/utils/Threads.cpp \
+ ../../../libs/utils/VectorImpl.cpp \
+
+LOCAL_C_INCLUDES += \
+ frameworks/base/media/mtp \
+
+LOCAL_CFLAGS := -DMTP_HOST -g -O0
+
+have_readline := $(wildcard /usr/include/readline/readline.h)
+have_history := $(wildcard /usr/lib/libhistory*)
+ifneq ($(strip $(have_readline)),)
+LOCAL_CFLAGS += -DHAVE_READLINE=1
+endif
+
+LOCAL_LDLIBS += -lpthread
+ifneq ($(strip $(have_readline)),)
+LOCAL_LDLIBS += -lreadline -lncurses
+endif
+ifneq ($(strip $(have_history)),)
+LOCAL_LDLIBS += -lhistory
+endif
+
+LOCAL_MODULE := mtp
+
+LOCAL_STATIC_LIBRARIES := libmtp libusbhost libcutils
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif
diff --git a/media/tests/mtp/MtpFile.cpp b/media/tests/mtp/MtpFile.cpp
new file mode 100644
index 0000000..00d328e
--- /dev/null
+++ b/media/tests/mtp/MtpFile.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MtpClient.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpObjectInfo.h"
+#include "MtpStorage.h"
+#include "MtpUtils.h"
+
+#include "MtpFile.h"
+
+namespace android {
+
+MtpClient* MtpFile::sClient = NULL;
+
+MtpFile::MtpFile(MtpDevice* device)
+ : mDevice(device),
+ mStorage(0),
+ mHandle(0)
+{
+}
+
+MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage)
+ : mDevice(device),
+ mStorage(storage),
+ mHandle(0)
+{
+}
+
+MtpFile::MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle)
+ : mDevice(device),
+ mStorage(storage),
+ mHandle(handle)
+{
+}
+
+MtpFile::MtpFile(MtpFile* file)
+ : mDevice(file->mDevice),
+ mStorage(file->mStorage),
+ mHandle(file->mHandle)
+{
+}
+
+MtpFile::~MtpFile() {
+}
+
+void MtpFile::print() {
+ if (mHandle) {
+
+ } else if (mStorage) {
+ printf("%x\n", mStorage);
+ } else {
+ int id = mDevice->getID();
+ MtpDeviceInfo* info = mDevice->getDeviceInfo();
+ if (info)
+ printf("%d\t%s %s %s\n", id, info->mManufacturer, info->mModel, info->mSerial);
+ else
+ printf("%d\t(no device info available)\n", id);
+ delete info;
+ }
+}
+
+MtpObjectInfo* MtpFile::getObjectInfo() {
+ return mDevice->getObjectInfo(mHandle);
+}
+
+void MtpFile::list() {
+ if (mStorage) {
+ MtpObjectHandleList* handles = mDevice->getObjectHandles(mStorage, 0,
+ (mHandle ? mHandle : -1));
+ if (handles) {
+ for (int i = 0; i < handles->size(); i++) {
+ MtpObjectHandle handle = (*handles)[i];
+ MtpObjectInfo* info = mDevice->getObjectInfo(handle);
+ if (info) {
+ char modified[100];
+ struct tm tm;
+
+ gmtime_r(&info->mDateModified, &tm);
+ strftime(modified, sizeof(modified), "%a %b %e %H:%M:%S GMT %Y", &tm);
+ printf("%s Handle: %d Format: %04X Size: %d Modified: %s\n",
+ info->mName, handle, info->mFormat, info->mCompressedSize, modified);
+ delete info;
+ }
+ }
+ delete handles;
+ }
+ } else {
+ // list storage units for device
+ MtpStorageIDList* storageList = mDevice->getStorageIDs();
+ for (int i = 0; i < storageList->size(); i++) {
+ MtpStorageID storageID = (*storageList)[i];
+ printf("%x\n", storageID);
+ }
+ }
+}
+
+void MtpFile::init(MtpClient* client) {
+ sClient = client;
+}
+
+MtpFile* MtpFile::parsePath(MtpFile* base, char* path) {
+ MtpDevice* device = NULL;
+ MtpStorageID storage = 0;
+ MtpObjectHandle handle = 0;
+
+ if (path[0] != '/' && base) {
+ device = base->mDevice;
+ storage = base->mStorage;
+ handle = base->mHandle;
+ }
+
+ // parse an absolute path
+ if (path[0] == '/')
+ path++;
+ char* tok = strtok(path, "/");
+ while (tok) {
+ if (storage) {
+ // find child of current handle
+ MtpObjectHandleList* handles = device->getObjectHandles(storage, 0,
+ (handle ? handle : -1));
+ MtpObjectHandle childHandle = 0;
+
+ if (handles) {
+ for (int i = 0; i < handles->size() && !childHandle; i++) {
+ MtpObjectHandle handle = (*handles)[i];
+ MtpObjectInfo* info = device->getObjectInfo(handle);
+ if (info && !strcmp(tok, info->mName))
+ childHandle = handle;
+ delete info;
+ }
+ delete handles;
+ }
+ if (childHandle)
+ handle = childHandle;
+ else
+ return NULL;
+ } else if (device) {
+ unsigned int id;
+ // find storage for the device
+ if (sscanf(tok, "%x", &id) == 1) {
+ MtpStorageIDList* storageList = device->getStorageIDs();
+ bool found = false;
+ for (int i = 0; i < storageList->size(); i++) {
+ if ((*storageList)[i] == id) {
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ storage = id;
+ else
+ return NULL;
+ }
+ } else {
+ // find device
+ unsigned int id;
+ if (sscanf(tok, "%d", &id) == 1)
+ device = sClient->getDevice(id);
+ if (!device)
+ return NULL;
+ }
+
+ tok = strtok(NULL, "/");
+ }
+
+ if (device)
+ return new MtpFile(device, storage, handle);
+ else
+ return NULL;
+}
+
+}
diff --git a/media/tests/mtp/MtpFile.h b/media/tests/mtp/MtpFile.h
new file mode 100644
index 0000000..ab8762b
--- /dev/null
+++ b/media/tests/mtp/MtpFile.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_FILE_H
+#define _MTP_FILE_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpClient;
+class MtpDevice;
+class MtpObjectInfo;
+
+// File-like abstraction for the interactive shell.
+// This can be used to represent an MTP device, storage unit or object
+// (either file or association).
+class MtpFile {
+private:
+ MtpDevice* mDevice;
+ MtpStorageID mStorage;
+ MtpObjectHandle mHandle;
+ static MtpClient* sClient;
+
+public:
+ MtpFile(MtpDevice* device);
+ MtpFile(MtpDevice* device, MtpStorageID storage);
+ MtpFile(MtpDevice* device, MtpStorageID storage, MtpObjectHandle handle);
+ MtpFile(MtpFile* file);
+ virtual ~MtpFile();
+
+ MtpObjectInfo* getObjectInfo();
+ void print();
+ void list();
+
+ inline MtpDevice* getDevice() const { return mDevice; }
+
+ static void init(MtpClient* client);
+ static MtpFile* parsePath(MtpFile* base, char* path);
+};
+
+}
+
+#endif // _MTP_DIRECTORY_H
diff --git a/media/tests/mtp/mtp.cpp b/media/tests/mtp/mtp.cpp
new file mode 100644
index 0000000..2c5e23b
--- /dev/null
+++ b/media/tests/mtp/mtp.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#if HAVE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include "MtpClient.h"
+#include "MtpDevice.h"
+#include "MtpObjectInfo.h"
+
+#include "MtpFile.h"
+
+#define PROMPT "mtp> "
+
+using namespace android;
+
+static MtpClient* sClient = NULL;
+
+// current working directory information for interactive shell
+static MtpFile* sCurrentDirectory = NULL;
+
+static MtpFile* parse_path(char* path) {
+ return MtpFile::parsePath(sCurrentDirectory, path);
+}
+
+class MyClient : public MtpClient {
+private:
+ virtual void deviceAdded(MtpDevice *device) {
+ }
+
+ virtual void deviceRemoved(MtpDevice *device) {
+ }
+
+public:
+};
+
+static void init() {
+ sClient = new MyClient;
+ sClient->start();
+ MtpFile::init(sClient);
+}
+
+static int set_cwd(int argc, char* argv[]) {
+ if (argc != 1) {
+ fprintf(stderr, "cd should have one argument\n");
+ return -1;
+ }
+ if (!strcmp(argv[0], "/")) {
+ delete sCurrentDirectory;
+ sCurrentDirectory = NULL;
+ }
+ else {
+ MtpFile* file = parse_path(argv[0]);
+ if (file) {
+ delete sCurrentDirectory;
+ sCurrentDirectory = file;
+ } else {
+ fprintf(stderr, "could not find %s\n", argv[0]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void list_devices() {
+ // TODO - need to make sure the list will not change while iterating
+ MtpDeviceList& devices = sClient->getDeviceList();
+ for (int i = 0; i < devices.size(); i++) {
+ MtpDevice* device = devices[i];
+ MtpFile* file = new MtpFile(device);
+ file->print();
+ delete file;
+ }
+}
+
+static int list(int argc, char* argv[]) {
+ if (argc == 0) {
+ // list cwd
+ if (sCurrentDirectory) {
+ sCurrentDirectory->list();
+ } else {
+ list_devices();
+ }
+ }
+
+ for (int i = 0; i < argc; i++) {
+ char* path = argv[i];
+ if (!strcmp(path, "/")) {
+ list_devices();
+ } else {
+ MtpFile* file = parse_path(path);
+ if (!file) {
+ fprintf(stderr, "could not find %s\n", path);
+ return -1;
+ }
+ file->list();
+ }
+ }
+
+ return 0;
+}
+
+static int get_file(int argc, char* argv[]) {
+ int ret = -1;
+ int srcFD = -1;
+ int destFD = -1;
+ MtpFile* srcFile = NULL;
+ MtpObjectInfo* info = NULL;
+ char* dest;
+
+ if (argc < 1) {
+ fprintf(stderr, "not enough arguments\n");
+ return -1;
+ } else if (argc > 2) {
+ fprintf(stderr, "too many arguments\n");
+ return -1;
+ }
+
+ // find source object
+ char* src = argv[0];
+ srcFile = parse_path(src);
+ if (!srcFile) {
+ fprintf(stderr, "could not find %s\n", src);
+ return -1;
+ }
+ info = srcFile->getObjectInfo();
+ if (!info) {
+ fprintf(stderr, "could not find object info for %s\n", src);
+ goto fail;
+ }
+ if (info->mFormat == MTP_FORMAT_ASSOCIATION) {
+ fprintf(stderr, "copying directories not implemented yet\n");
+ goto fail;
+ }
+
+ dest = (argc > 1 ? argv[1] : info->mName);
+ destFD = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (destFD < 0) {
+ fprintf(stderr, "could not create %s\n", dest);
+ goto fail;
+ }
+ srcFD = srcFile->getDevice()->readObject(info->mHandle, info->mCompressedSize);
+ if (srcFD < 0)
+ goto fail;
+
+ char buffer[65536];
+ while (1) {
+ int count = read(srcFD, buffer, sizeof(buffer));
+ if (count <= 0)
+ break;
+ write(destFD, buffer, count);
+ }
+ // FIXME - error checking and reporting
+ ret = 0;
+
+fail:
+ delete srcFile;
+ delete info;
+ if (srcFD >= 0)
+ close(srcFD);
+ if (destFD >= 0)
+ close(destFD);
+ return ret;
+}
+
+static int put_file(int argc, char* argv[]) {
+ int ret = -1;
+ int srcFD = -1;
+ MtpFile* destFile = NULL;
+ MtpObjectInfo* srcInfo = NULL;
+ MtpObjectInfo* destInfo = NULL;
+ MtpObjectHandle handle;
+ struct stat statbuf;
+ const char* lastSlash;
+
+ if (argc < 1) {
+ fprintf(stderr, "not enough arguments\n");
+ return -1;
+ } else if (argc > 2) {
+ fprintf(stderr, "too many arguments\n");
+ return -1;
+ }
+ const char* src = argv[0];
+ srcFD = open(src, O_RDONLY);
+ if (srcFD < 0) {
+ fprintf(stderr, "could not open %s\n", src);
+ goto fail;
+ }
+ if (argc == 2) {
+ char* dest = argv[1];
+ destFile = parse_path(dest);
+ if (!destFile) {
+ fprintf(stderr, "could not find %s\n", dest);
+ goto fail;
+ }
+ } else {
+ if (!sCurrentDirectory) {
+ fprintf(stderr, "current working directory not set\n");
+ goto fail;
+ }
+ destFile = new MtpFile(sCurrentDirectory);
+ }
+
+ destInfo = destFile->getObjectInfo();
+ if (!destInfo) {
+ fprintf(stderr, "could not find object info destination directory\n");
+ goto fail;
+ }
+ if (destInfo->mFormat != MTP_FORMAT_ASSOCIATION) {
+ fprintf(stderr, "destination not a directory\n");
+ goto fail;
+ }
+
+ if (fstat(srcFD, &statbuf))
+ goto fail;
+
+ srcInfo = new MtpObjectInfo(0);
+ srcInfo->mStorageID = destInfo->mStorageID;
+ srcInfo->mFormat = MTP_FORMAT_EXIF_JPEG; // FIXME
+ srcInfo->mCompressedSize = statbuf.st_size;
+ srcInfo->mParent = destInfo->mHandle;
+ lastSlash = strrchr(src, '/');
+ srcInfo->mName = strdup(lastSlash ? lastSlash + 1 : src);
+ srcInfo->mDateModified = statbuf.st_mtime;
+ handle = destFile->getDevice()->sendObjectInfo(srcInfo);
+ if (handle <= 0) {
+ printf("sendObjectInfo returned %04X\n", handle);
+ goto fail;
+ }
+ if (destFile->getDevice()->sendObject(srcInfo, srcFD))
+ ret = 0;
+
+fail:
+ delete destFile;
+ delete srcInfo;
+ delete destInfo;
+ if (srcFD >= 0)
+ close(srcFD);
+ printf("returning %d\n", ret);
+ return ret;
+}
+
+typedef int (* command_func)(int argc, char* argv[]);
+
+struct command_table_entry {
+ const char* name;
+ command_func func;
+};
+
+const command_table_entry command_list[] = {
+ { "cd", set_cwd },
+ { "ls", list },
+ { "get", get_file },
+ { "put", put_file },
+ { NULL, NULL },
+};
+
+
+static int do_command(int argc, char* argv[]) {
+ const command_table_entry* command = command_list;
+ const char* name = *argv++;
+ argc--;
+
+ while (command->name) {
+ if (!strcmp(command->name, name))
+ return command->func(argc, argv);
+ else
+ command++;
+ }
+ fprintf(stderr, "unknown command %s\n", name);
+ return -1;
+}
+
+static int shell() {
+ int argc;
+ int result = 0;
+#define MAX_ARGS 100
+ char* argv[MAX_ARGS];
+
+#if HAVE_READLINE
+ using_history();
+#endif
+
+ while (1) {
+#if HAVE_READLINE
+ char* line = readline(PROMPT);
+ if (!line) {
+ printf("\n");
+ exit(0);
+ }
+#else
+ char buffer[1000];
+ printf("%s", PROMPT);
+ char* line = NULL;
+ size_t length = 0;
+
+ buffer[0] = 0;
+ fgets(buffer, sizeof(buffer), stdin);
+ int count = strlen(buffer);
+ if (count > 0 && buffer[0] == EOF) {
+ printf("\n");
+ exit(0);
+ }
+ if (count > 0 && line[count - 1] == '\n')
+ line[count - 1] == 0;
+#endif
+ char* tok = strtok(line, " \t\n\r");
+ if (!tok)
+ continue;
+ if (!strcmp(tok, "quit") || !strcmp(tok, "exit")) {
+ exit(0);
+ }
+#if HAVE_READLINE
+ add_history(line);
+#endif
+ argc = 0;
+ while (tok) {
+ if (argc + 1 == MAX_ARGS) {
+ fprintf(stderr, "too many arguments\n");
+ result = -1;
+ goto bottom_of_loop;
+ }
+
+ argv[argc++] = strdup(tok);
+ tok = strtok(NULL, " \t\n\r");
+ }
+
+ result = do_command(argc, argv);
+
+bottom_of_loop:
+ for (int i = 0; i < argc; i++)
+ free(argv[i]);
+ free(line);
+ }
+
+ return result;
+}
+
+int main(int argc, char* argv[]) {
+ init();
+
+ if (argc == 1)
+ return shell();
+ else
+ return do_command(argc - 1, argv + 1);
+}