Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fd1ff0a authored by Mike Lockwood's avatar Mike Lockwood
Browse files

Simple command line test tool for MTP host.



Change-Id: Ifd13e1ca5d49a5477a9850d94d443a50bbc32ff1
Signed-off-by: default avatarMike Lockwood <lockwood@android.com>
parent ad0643a3
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
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
+187 −0
Original line number Diff line number Diff line
/*
 * 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;
}

}
+57 −0
Original line number Diff line number Diff line
/*
 * 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
+370 −0
Original line number Diff line number Diff line
/*
 * 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);
}