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

Commit 133dfe7e authored by Mike Lockwood's avatar Mike Lockwood Committed by Android (Google) Code Review
Browse files

Merge "Simple command line test tool for MTP host."

parents 1979628e 5fd1ff0a
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);
}