diff options
| author | 2010-07-26 20:44:42 -0400 | |
|---|---|---|
| committer | 2010-07-26 20:47:35 -0400 | |
| commit | 5fd1ff0aa370958dfdbabb6026c1d383d17df97f (patch) | |
| tree | 8458539ce6e0d8a1aa078e93568a5b0d0c034ed8 | |
| parent | ad0643a330db13c8f11b1a71fbb7262570114f4d (diff) | |
Simple command line test tool for MTP host.
Change-Id: Ifd13e1ca5d49a5477a9850d94d443a50bbc32ff1
Signed-off-by: Mike Lockwood <lockwood@android.com>
| -rw-r--r-- | media/tests/mtp/Android.mk | 57 | ||||
| -rw-r--r-- | media/tests/mtp/MtpFile.cpp | 187 | ||||
| -rw-r--r-- | media/tests/mtp/MtpFile.h | 57 | ||||
| -rw-r--r-- | media/tests/mtp/mtp.cpp | 370 | 
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 000000000000..8cdfb69bf563 --- /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 000000000000..00d328efd5d1 --- /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 000000000000..ab8762b3adba --- /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 000000000000..2c5e23bf660c --- /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); +}  |