diff options
Diffstat (limited to 'media')
39 files changed, 402 insertions, 1248 deletions
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/MtpClient.java index 98da1f6..dcb1983 100644 --- a/media/java/android/media/MtpClient.java +++ b/media/java/android/media/MtpClient.java @@ -16,7 +16,6 @@ package android.media; -import android.os.ParcelFileDescriptor; import android.util.Log; /** @@ -69,9 +68,10 @@ public class MtpClient { return native_get_storage_id(deviceID, objectID); } - // create a file descriptor for reading the contents of an object over MTP - public ParcelFileDescriptor openFile(int deviceID, long objectID) { - return native_open_file(deviceID, objectID); + // Reads a file from device to host to the specified destination. + // Returns true if the transfer succeeds. + public boolean importFile(int deviceID, long objectID, String destPath) { + return native_import_file(deviceID, objectID, destPath); } public interface Listener { @@ -104,5 +104,5 @@ public class MtpClient { private native boolean native_delete_object(int deviceID, long objectID); private native long native_get_parent(int deviceID, long objectID); private native long native_get_storage_id(int deviceID, long objectID); - private native ParcelFileDescriptor native_open_file(int deviceID, long objectID); + private native boolean native_import_file(int deviceID, long objectID, String destPath); } diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/MtpCursor.java index 9b5ab95..daa3f4d 100644 --- a/media/java/android/media/MtpCursor.java +++ b/media/java/android/media/MtpCursor.java @@ -40,6 +40,7 @@ public final class MtpCursor extends AbstractWindowedCursor { public static final int OBJECT_ID = 6; public static final int STORAGE_CHILDREN = 7; public static final int OBJECT_CHILDREN = 8; + public static final int OBJECT_IMPORT = 9; /** The names of the columns in the projection */ private String[] mColumns; diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp index d23185b..144dfc8 100644 --- a/media/jni/android_media_MtpClient.cpp +++ b/media/jni/android_media_MtpClient.cpp @@ -26,6 +26,7 @@ #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" +#include "private/android_filesystem_config.h" #include "MtpClient.h" #include "MtpDevice.h" @@ -39,19 +40,6 @@ static jmethodID method_deviceAdded; static jmethodID method_deviceRemoved; static jfieldID field_context; -static struct file_descriptor_offsets_t -{ - jclass mClass; - jmethodID mConstructor; - jfieldID mDescriptor; -} gFileDescriptorOffsets; - -static struct parcel_file_descriptor_offsets_t -{ - jclass mClass; - jmethodID mConstructor; -} gParcelFileDescriptorOffsets; - #ifdef HAVE_ANDROID_OS static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { @@ -201,34 +189,19 @@ android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz, return -1; } -static jobject -android_media_MtpClient_open_file(JNIEnv *env, jobject thiz, - jint device_id, jlong object_id) +static jboolean +android_media_MtpClient_import_file(JNIEnv *env, jobject thiz, + jint device_id, jlong object_id, jstring dest_path) { #ifdef HAVE_ANDROID_OS MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); MtpDevice* device = client->getDevice(device_id); - if (!device) - return NULL; - - MtpObjectInfo* info = device->getObjectInfo(object_id); - if (!info) - return NULL; - int object_size = info->mCompressedSize; - delete info; - int fd = device->readObject(object_id, object_size); - if (fd < 0) - return NULL; - - jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, - gFileDescriptorOffsets.mConstructor); - if (fileDescriptor != NULL) { - env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd); - } else { - return NULL; + if (device) { + const char *destPathStr = env->GetStringUTFChars(dest_path, NULL); + bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664); + env->ReleaseStringUTFChars(dest_path, destPathStr); + return result; } - return env->NewObject(gParcelFileDescriptorOffsets.mClass, - gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); #endif return NULL; } @@ -243,8 +216,8 @@ static JNINativeMethod gMethods[] = { {"native_delete_object", "(IJ)Z", (void *)android_media_MtpClient_delete_object}, {"native_get_parent", "(IJ)J", (void *)android_media_MtpClient_get_parent}, {"native_get_storage_id", "(IJ)J", (void *)android_media_MtpClient_get_storage_id}, - {"native_open_file", "(IJ)Landroid/os/ParcelFileDescriptor;", - (void *)android_media_MtpClient_open_file}, + {"native_import_file", "(IJLjava/lang/String;)Z", + (void *)android_media_MtpClient_import_file}, }; static const char* const kClassPathName = "android/media/MtpClient"; @@ -276,21 +249,6 @@ int register_android_media_MtpClient(JNIEnv *env) return -1; } - clazz = env->FindClass("java/io/FileDescriptor"); - LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); - gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); - gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); - LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, - "Unable to find descriptor field in java.io.FileDescriptor"); - - clazz = env->FindClass("android/os/ParcelFileDescriptor"); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); - gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); - LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, - "Unable to find constructor for android.os.ParcelFileDescriptor"); - return AndroidRuntime::registerNativeMethods(env, "android/media/MtpClient", gMethods, NELEM(gMethods)); } diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h index 42037af..1b0fd38 100644 --- a/media/jni/soundpool/SoundPool.h +++ b/media/jni/soundpool/SoundPool.h @@ -22,7 +22,6 @@ #include <utils/Vector.h> #include <utils/KeyedVector.h> #include <media/AudioTrack.h> -#include <cutils/atomic.h> namespace android { diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 88b8c86..aadeba5 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -27,7 +27,6 @@ #include <media/AudioEffect.h> #include <utils/Log.h> -#include <cutils/atomic.h> #include <binder/IPCThreadState.h> @@ -207,18 +206,22 @@ status_t AudioEffect::setEnabled(bool enabled) return INVALID_OPERATION; } - if (enabled) { - LOGV("enable %p", this); - if (android_atomic_or(1, &mEnabled) == 0) { - return mIEffect->enable(); + status_t status = NO_ERROR; + + AutoMutex lock(mLock); + if (enabled != mEnabled) { + if (enabled) { + LOGV("enable %p", this); + status = mIEffect->enable(); + } else { + LOGV("disable %p", this); + status = mIEffect->disable(); } - } else { - LOGV("disable %p", this); - if (android_atomic_and(~1, &mEnabled) == 1) { - return mIEffect->disable(); + if (status == NO_ERROR) { + mEnabled = enabled; } } - return NO_ERROR; + return status; } status_t AudioEffect::command(uint32_t cmdCode, @@ -232,26 +235,26 @@ status_t AudioEffect::command(uint32_t cmdCode, return INVALID_OPERATION; } - if ((cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) && - (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL)) { - return BAD_VALUE; + if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) { + if (mEnabled == (cmdCode == EFFECT_CMD_ENABLE)) { + return NO_ERROR; + } + if (replySize == NULL || *replySize != sizeof(status_t) || replyData == NULL) { + return BAD_VALUE; + } + mLock.lock(); } status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData); - if (status != NO_ERROR) { - return status; - } if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) { - status = *(status_t *)replyData; - if (status != NO_ERROR) { - return status; + if (status == NO_ERROR) { + status = *(status_t *)replyData; } - if (cmdCode == EFFECT_CMD_ENABLE) { - android_atomic_or(1, &mEnabled); - } else { - android_atomic_and(~1, &mEnabled); + if (status == NO_ERROR) { + mEnabled = (cmdCode == EFFECT_CMD_ENABLE); } + mLock.unlock(); } return status; @@ -370,11 +373,7 @@ void AudioEffect::enableStatusChanged(bool enabled) { LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); if (mStatus == ALREADY_EXISTS) { - if (enabled) { - android_atomic_or(1, &mEnabled); - } else { - android_atomic_and(~1, &mEnabled); - } + mEnabled = enabled; if (mCbf) { mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled); } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index a6c515c..1d6ffa0 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -35,7 +35,6 @@ #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <utils/Timers.h> -#include <cutils/atomic.h> #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) @@ -282,7 +281,9 @@ status_t AudioRecord::start() t->mLock.lock(); } - if (android_atomic_or(1, &mActive) == 0) { + AutoMutex lock(mLock); + if (mActive == 0) { + mActive = 1; ret = mAudioRecord->start(); if (ret == DEAD_OBJECT) { LOGV("start() dead IAudioRecord: creating a new one"); @@ -302,8 +303,7 @@ status_t AudioRecord::start() setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT); } } else { - LOGV("start() failed"); - android_atomic_and(~1, &mActive); + mActive = 0; } } @@ -322,9 +322,11 @@ status_t AudioRecord::stop() if (t != 0) { t->mLock.lock(); - } + } - if (android_atomic_and(~1, &mActive) == 1) { + AutoMutex lock(mLock); + if (mActive == 1) { + mActive = 0; mCblk->cv.signal(); mAudioRecord->stop(); // the record head position will reset to 0, so if a marker is set, we need diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 587c8ff..c1bed59 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -35,7 +35,6 @@ #include <binder/Parcel.h> #include <binder/IPCThreadState.h> #include <utils/Timers.h> -#include <cutils/atomic.h> #define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) #define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) @@ -312,7 +311,9 @@ void AudioTrack::start() t->mLock.lock(); } - if (android_atomic_or(1, &mActive) == 0) { + AutoMutex lock(mLock); + if (mActive == 0) { + mActive = 1; mNewPosition = mCblk->server + mUpdatePeriod; mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; mCblk->waitTimeMs = 0; @@ -344,7 +345,7 @@ void AudioTrack::start() } if (status != NO_ERROR) { LOGV("start() failed"); - android_atomic_and(~1, &mActive); + mActive = 0; if (t != 0) { t->requestExit(); } else { @@ -367,7 +368,9 @@ void AudioTrack::stop() t->mLock.lock(); } - if (android_atomic_and(~1, &mActive) == 1) { + AutoMutex lock(mLock); + if (mActive == 1) { + mActive = 0; mCblk->cv.signal(); mAudioTrack->stop(); // Cancel loops (If we are in the middle of a loop, playback @@ -407,7 +410,6 @@ void AudioTrack::flush() mMarkerReached = false; mUpdatePeriod = 0; - if (!mActive) { mAudioTrack->flush(); // Release AudioTrack callback thread in case it was waiting for new buffers @@ -419,7 +421,9 @@ void AudioTrack::flush() void AudioTrack::pause() { LOGV("pause"); - if (android_atomic_and(~1, &mActive) == 1) { + AutoMutex lock(mLock); + if (mActive == 1) { + mActive = 0; mAudioTrack->pause(); } } diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index dadd1db..ec3b5a2 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -864,7 +864,7 @@ status_t StagefrightRecorder::startAMRRecording() { return UNKNOWN_ERROR; } - mWriter = new AMRWriter(dup(mOutputFd)); + mWriter = new AMRWriter(mOutputFd); mWriter->addSource(audioEncoder); if (mMaxFileDurationUs != 0) { @@ -912,7 +912,7 @@ status_t StagefrightRecorder::startRTPRecording() { } } - mWriter = new ARTPWriter(dup(mOutputFd)); + mWriter = new ARTPWriter(mOutputFd); mWriter->addSource(source); mWriter->setListener(mListener); @@ -922,7 +922,7 @@ status_t StagefrightRecorder::startRTPRecording() { status_t StagefrightRecorder::startMPEG2TSRecording() { CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS); - sp<MediaWriter> writer = new MPEG2TSWriter(dup(mOutputFd)); + sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd); if (mAudioSource != AUDIO_SOURCE_LIST_END) { if (mAudioEncoder != AUDIO_ENCODER_AAC) { @@ -1204,7 +1204,7 @@ status_t StagefrightRecorder::setupMPEG4Recording( mediaWriter->clear(); *totalBitRate = 0; status_t err = OK; - sp<MediaWriter> writer = new MPEG4Writer(dup(outputFd)); + sp<MediaWriter> writer = new MPEG4Writer(outputFd); // Add audio source first if it exists if (!mCaptureTimeLapse && (mAudioSource != AUDIO_SOURCE_LIST_END)) { diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index ecbd96c..0db3d1d 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -24,22 +24,28 @@ #include <media/mediarecorder.h> #include <sys/prctl.h> #include <sys/resource.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> namespace android { AMRWriter::AMRWriter(const char *filename) - : mFile(fopen(filename, "wb")), - mFd(mFile == NULL? -1: fileno(mFile)), - mInitCheck(mFile != NULL ? OK : NO_INIT), + : mFd(-1), + mInitCheck(NO_INIT), mStarted(false), mPaused(false), mResumed(false) { + + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC); + if (mFd >= 0) { + mInitCheck = OK; + } } AMRWriter::AMRWriter(int fd) - : mFile(fdopen(fd, "wb")), - mFd(mFile == NULL? -1: fileno(mFile)), - mInitCheck(mFile != NULL ? OK : NO_INIT), + : mFd(dup(fd)), + mInitCheck(mFd < 0? NO_INIT: OK), mStarted(false), mPaused(false), mResumed(false) { @@ -50,9 +56,9 @@ AMRWriter::~AMRWriter() { stop(); } - if (mFile != NULL) { - fclose(mFile); - mFile = NULL; + if (mFd != -1) { + close(mFd); + mFd = -1; } } @@ -92,7 +98,7 @@ status_t AMRWriter::addSource(const sp<MediaSource> &source) { mSource = source; const char *kHeader = isWide ? "#!AMR-WB\n" : "#!AMR\n"; - size_t n = strlen(kHeader); + ssize_t n = strlen(kHeader); if (write(mFd, kHeader, n) != n) { return ERROR_IO; } @@ -266,9 +272,8 @@ status_t AMRWriter::threadFunc() { notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_COMPLETION_STATUS, UNKNOWN_ERROR); } - fflush(mFile); - fclose(mFile); - mFile = NULL; + close(mFd); + mFd = -1; mReachedEOS = true; if (err == ERROR_END_OF_STREAM) { return OK; diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 8fe1d4d..4ad1eb4 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -49,7 +49,6 @@ LOCAL_SRC_FILES:= \ WVMExtractor.cpp \ XINGSeeker.cpp \ avc_utils.cpp \ - string.cpp LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp index c9f68e9..98d5b50 100644 --- a/media/libstagefright/FileSource.cpp +++ b/media/libstagefright/FileSource.cpp @@ -18,12 +18,14 @@ #include <media/stagefright/MediaDebug.h> #include <sys/types.h> #include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> namespace android { FileSource::FileSource(const char *filename) - : mFile(fopen(filename, "rb")), - mFd(mFile == NULL ? -1 : fileno(mFile)), + : mFd(-1), mOffset(0), mLength(-1), mDecryptHandle(NULL), @@ -31,11 +33,12 @@ FileSource::FileSource(const char *filename) mDrmBufOffset(0), mDrmBufSize(0), mDrmBuf(NULL){ + + mFd = open(filename, O_LARGEFILE | O_RDONLY); } FileSource::FileSource(int fd, int64_t offset, int64_t length) - : mFile(fdopen(fd, "rb")), - mFd(fd), + : mFd(fd), mOffset(offset), mLength(length), mDecryptHandle(NULL), @@ -48,9 +51,9 @@ FileSource::FileSource(int fd, int64_t offset, int64_t length) } FileSource::~FileSource() { - if (mFile != NULL) { - fclose(mFile); - mFile = NULL; + if (mFd >= 0) { + close(mFd); + mFd = -1; } if (mDrmBuf != NULL) { @@ -60,11 +63,11 @@ FileSource::~FileSource() { } status_t FileSource::initCheck() const { - return mFile != NULL ? OK : NO_INIT; + return mFd >= 0 ? OK : NO_INIT; } ssize_t FileSource::readAt(off64_t offset, void *data, size_t size) { - if (mFile == NULL) { + if (mFd < 0) { return NO_INIT; } @@ -95,7 +98,7 @@ ssize_t FileSource::readAt(off64_t offset, void *data, size_t size) { } status_t FileSource::getSize(off64_t *size) { - if (mFile == NULL) { + if (mFd < 0) { return NO_INIT; } diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index ccc6a34..e7f00aa 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -36,7 +36,7 @@ namespace android { // static -const char *HTTPStream::kStatusKey = ":status:"; +const char *HTTPStream::kStatusKey = ":status:"; // MUST be lowercase. HTTPStream::HTTPStream() : mState(READY), @@ -220,7 +220,7 @@ status_t HTTPStream::receive_header(int *http_status) { return err; } - mHeaders.add(string(kStatusKey), string(line)); + mHeaders.add(AString(kStatusKey), AString(line)); char *spacePos = strchr(line, ' '); if (spacePos == NULL) { @@ -264,7 +264,10 @@ status_t HTTPStream::receive_header(int *http_status) { char *colonPos = strchr(line, ':'); if (colonPos == NULL) { - mHeaders.add(string(line), string()); + AString key = line; + key.tolower(); + + mHeaders.add(key, AString()); } else { char *end_of_key = colonPos; while (end_of_key > line && isspace(end_of_key[-1])) { @@ -278,7 +281,10 @@ status_t HTTPStream::receive_header(int *http_status) { *end_of_key = '\0'; - mHeaders.add(string(line), string(start_of_value)); + AString key = line; + key.tolower(); + + mHeaders.add(key, AString(start_of_value)); } } @@ -314,8 +320,11 @@ ssize_t HTTPStream::receive(void *data, size_t size) { return (ssize_t)total; } -bool HTTPStream::find_header_value(const string &key, string *value) const { - ssize_t index = mHeaders.indexOfKey(key); +bool HTTPStream::find_header_value(const AString &key, AString *value) const { + AString key_lower = key; + key_lower.tolower(); + + ssize_t index = mHeaders.indexOfKey(key_lower); if (index < 0) { value->clear(); return false; diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp index 81a2b0d..4d8165e 100644 --- a/media/libstagefright/MPEG2TSWriter.cpp +++ b/media/libstagefright/MPEG2TSWriter.cpp @@ -410,7 +410,7 @@ void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) { //////////////////////////////////////////////////////////////////////////////// MPEG2TSWriter::MPEG2TSWriter(int fd) - : mFile(fdopen(fd, "wb")), + : mFile(fdopen(dup(fd), "wb")), mStarted(false), mNumSourcesDone(0), mNumTSPacketsWritten(0), diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 19e3eae..6760707 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -33,6 +33,10 @@ #include <media/stagefright/MediaSource.h> #include <media/stagefright/Utils.h> #include <media/mediarecorder.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #include "include/ESDS.h" @@ -214,8 +218,8 @@ private: }; MPEG4Writer::MPEG4Writer(const char *filename) - : mFile(fopen(filename, "wb")), - mFd(mFile == NULL? -1: fileno(mFile)), + : mFd(-1), + mInitCheck(NO_INIT), mUse4ByteNalLength(true), mUse32BitOffset(true), mIsFileSizeLimitExplicitlyRequested(false), @@ -225,12 +229,16 @@ MPEG4Writer::MPEG4Writer(const char *filename) mMdatOffset(0), mEstimatedMoovBoxSize(0), mInterleaveDurationUs(1000000) { - CHECK(mFile != NULL); + + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC); + if (mFd >= 0) { + mInitCheck = OK; + } } MPEG4Writer::MPEG4Writer(int fd) - : mFile(fdopen(fd, "wb")), - mFd(mFile == NULL? -1: fileno(mFile)), + : mFd(dup(fd)), + mInitCheck(mFd < 0? NO_INIT: OK), mUse4ByteNalLength(true), mUse32BitOffset(true), mIsFileSizeLimitExplicitlyRequested(false), @@ -240,7 +248,6 @@ MPEG4Writer::MPEG4Writer(int fd) mMdatOffset(0), mEstimatedMoovBoxSize(0), mInterleaveDurationUs(1000000) { - CHECK(mFile != NULL); } MPEG4Writer::~MPEG4Writer() { @@ -370,7 +377,7 @@ int64_t MPEG4Writer::estimateMoovBoxSize(int32_t bitRate) { } status_t MPEG4Writer::start(MetaData *param) { - if (mFile == NULL) { + if (mInitCheck != OK) { return UNKNOWN_ERROR; } @@ -493,7 +500,7 @@ bool MPEG4Writer::use32BitFileOffset() const { } status_t MPEG4Writer::pause() { - if (mFile == NULL) { + if (mInitCheck != OK) { return OK; } mPaused = true; @@ -577,7 +584,7 @@ void MPEG4Writer::writeCompositionMatrix(int degrees) { status_t MPEG4Writer::stop() { - if (mFile == NULL) { + if (mInitCheck != OK) { return OK; } @@ -600,9 +607,9 @@ status_t MPEG4Writer::stop() { // Do not write out movie header on error. if (err != OK) { - fflush(mFile); - fclose(mFile); - mFile = NULL; + close(mFd); + mFd = -1; + mInitCheck = NO_INIT; mStarted = false; return err; } @@ -665,7 +672,7 @@ status_t MPEG4Writer::stop() { // Moov box lseek64(mFd, mFreeBoxOffset, SEEK_SET); mOffset = mFreeBoxOffset; - write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile); + write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset); // Free box lseek64(mFd, mOffset, SEEK_SET); @@ -682,9 +689,9 @@ status_t MPEG4Writer::stop() { CHECK(mBoxes.empty()); - fflush(mFile); - fclose(mFile); - mFile = NULL; + close(mFd); + mFd = -1; + mInitCheck = NO_INIT; mStarted = false; return err; } @@ -763,20 +770,21 @@ off64_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) { } size_t MPEG4Writer::write( - const void *ptr, size_t size, size_t nmemb, FILE *stream) { + const void *ptr, size_t size, size_t nmemb) { const size_t bytes = size * nmemb; - int fd = fileno(stream); if (mWriteMoovBoxToMemory) { + // This happens only when we write the moov box at the end of + // recording, not for each output video/audio frame we receive. off64_t moovBoxSize = 8 + mMoovBoxBufferOffset + bytes; if (moovBoxSize > mEstimatedMoovBoxSize) { for (List<off64_t>::iterator it = mBoxes.begin(); it != mBoxes.end(); ++it) { (*it) += mOffset; } - lseek64(fd, mOffset, SEEK_SET); - ::write(fd, mMoovBoxBuffer, mMoovBoxBufferOffset); - ::write(fd, ptr, size * nmemb); + lseek64(mFd, mOffset, SEEK_SET); + ::write(mFd, mMoovBoxBuffer, mMoovBoxBufferOffset); + ::write(mFd, ptr, size * nmemb); mOffset += (bytes + mMoovBoxBufferOffset); free(mMoovBoxBuffer); mMoovBoxBuffer = NULL; @@ -788,7 +796,7 @@ size_t MPEG4Writer::write( mMoovBoxBufferOffset += bytes; } } else { - ::write(fd, ptr, size * nmemb); + ::write(mFd, ptr, size * nmemb); mOffset += bytes; } return bytes; @@ -822,36 +830,36 @@ void MPEG4Writer::endBox() { } void MPEG4Writer::writeInt8(int8_t x) { - write(&x, 1, 1, mFile); + write(&x, 1, 1); } void MPEG4Writer::writeInt16(int16_t x) { x = htons(x); - write(&x, 1, 2, mFile); + write(&x, 1, 2); } void MPEG4Writer::writeInt32(int32_t x) { x = htonl(x); - write(&x, 1, 4, mFile); + write(&x, 1, 4); } void MPEG4Writer::writeInt64(int64_t x) { x = hton64(x); - write(&x, 1, 8, mFile); + write(&x, 1, 8); } void MPEG4Writer::writeCString(const char *s) { size_t n = strlen(s); - write(s, 1, n + 1, mFile); + write(s, 1, n + 1); } void MPEG4Writer::writeFourcc(const char *s) { CHECK_EQ(strlen(s), 4); - write(s, 1, 4, mFile); + write(s, 1, 4); } void MPEG4Writer::write(const void *data, size_t size) { - write(data, 1, size, mFile); + write(data, 1, size); } bool MPEG4Writer::isFileStreamable() const { diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index 7f765ca..829ab20 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -465,7 +465,7 @@ status_t NuCachedSource2::seekInternal_l(off64_t offset) { return OK; } - LOGI("new range: offset= %ld", offset); + LOGI("new range: offset= %lld", offset); mCacheOffset = offset; diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp index 15c9ac6..269b233 100644 --- a/media/libstagefright/NuHTTPDataSource.cpp +++ b/media/libstagefright/NuHTTPDataSource.cpp @@ -178,7 +178,7 @@ status_t NuHTTPDataSource::connect( } if (IsRedirectStatusCode(httpStatus)) { - string value; + AString value; CHECK(mHTTP.find_header_value("Location", &value)); mState = DISCONNECTED; @@ -198,9 +198,8 @@ status_t NuHTTPDataSource::connect( mHasChunkedTransferEncoding = false; { - string value; - if (mHTTP.find_header_value("Transfer-Encoding", &value) - || mHTTP.find_header_value("Transfer-encoding", &value)) { + AString value; + if (mHTTP.find_header_value("Transfer-Encoding", &value)) { // We don't currently support any transfer encodings but // chunked. @@ -222,9 +221,9 @@ status_t NuHTTPDataSource::connect( applyTimeoutResponse(); if (offset == 0) { - string value; + AString value; unsigned long x; - if (mHTTP.find_header_value(string("Content-Length"), &value) + if (mHTTP.find_header_value(AString("Content-Length"), &value) && ParseSingleUnsignedLong(value.c_str(), &x)) { mContentLength = (off64_t)x; mContentLengthValid = true; @@ -239,9 +238,9 @@ status_t NuHTTPDataSource::connect( return ERROR_UNSUPPORTED; } - string value; + AString value; unsigned long x; - if (mHTTP.find_header_value(string("Content-Range"), &value)) { + if (mHTTP.find_header_value(AString("Content-Range"), &value)) { const char *slashPos = strchr(value.c_str(), '/'); if (slashPos != NULL && ParseSingleUnsignedLong(slashPos + 1, &x)) { @@ -439,7 +438,7 @@ void NuHTTPDataSource::MakeFullHeaders( } void NuHTTPDataSource::applyTimeoutResponse() { - string timeout; + AString timeout; if (mHTTP.find_header_value("X-SocketTimeout", &timeout)) { const char *s = timeout.c_str(); char *end; diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp index 23b7681..783f2d0 100644 --- a/media/libstagefright/ShoutcastSource.cpp +++ b/media/libstagefright/ShoutcastSource.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include "include/stagefright_string.h" #include "include/HTTPStream.h" #include <stdlib.h> @@ -34,7 +33,7 @@ ShoutcastSource::ShoutcastSource(HTTPStream *http) mBytesUntilMetaData(0), mGroup(NULL), mStarted(false) { - string metaint; + AString metaint; if (mHttp->find_header_value("icy-metaint", &metaint)) { char *end; const char *start = metaint.c_str(); diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 0f6af28..86e0e73 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -127,10 +127,11 @@ status_t StagefrightMediaScanner::processFile( || !strcasecmp(extension, ".rtttl") || !strcasecmp(extension, ".rtx") || !strcasecmp(extension, ".ota")) { - return HandleMIDI(path, &client); - } - - if (mRetriever->setDataSource(path) == OK + status_t status = HandleMIDI(path, &client); + if (status != OK) { + return status; + } + } else if (mRetriever->setDataSource(path) == OK && mRetriever->setMode( METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) { const char *value; diff --git a/media/libstagefright/include/HTTPStream.h b/media/libstagefright/include/HTTPStream.h index 793798f..545cd0c 100644 --- a/media/libstagefright/include/HTTPStream.h +++ b/media/libstagefright/include/HTTPStream.h @@ -18,10 +18,9 @@ #define HTTP_STREAM_H_ -#include "stagefright_string.h" - #include <sys/types.h> +#include <media/stagefright/foundation/AString.h> #include <media/stagefright/MediaErrors.h> #include <utils/KeyedVector.h> #include <utils/threads.h> @@ -50,7 +49,7 @@ public: static const char *kStatusKey; bool find_header_value( - const string &key, string *value) const; + const AString &key, AString *value) const; // Pass a negative value to disable the timeout. void setReceiveTimeout(int seconds); @@ -70,7 +69,7 @@ private: Mutex mLock; int mSocket; - KeyedVector<string, string> mHeaders; + KeyedVector<AString, AString> mHeaders; HTTPStream(const HTTPStream &); HTTPStream &operator=(const HTTPStream &); diff --git a/media/libstagefright/include/stagefright_string.h b/media/libstagefright/include/stagefright_string.h deleted file mode 100644 index 5dc7116..0000000 --- a/media/libstagefright/include/stagefright_string.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2009 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 STRING_H_ - -#define STRING_H_ - -#include <utils/String8.h> - -namespace android { - -class string { -public: - typedef size_t size_type; - static size_type npos; - - string(); - string(const char *s); - string(const char *s, size_t length); - string(const string &from, size_type start, size_type length = npos); - - const char *c_str() const; - size_type size() const; - - void clear(); - void erase(size_type from, size_type length); - - size_type find(char c) const; - - bool operator<(const string &other) const; - bool operator==(const string &other) const; - - string &operator+=(char c); - -private: - String8 mString; -}; - -} // namespace android - -#endif // STRING_H_ diff --git a/media/libstagefright/rtsp/ARTPWriter.cpp b/media/libstagefright/rtsp/ARTPWriter.cpp index 155fd96..5a033e1 100644 --- a/media/libstagefright/rtsp/ARTPWriter.cpp +++ b/media/libstagefright/rtsp/ARTPWriter.cpp @@ -46,7 +46,7 @@ static int UniformRand(int limit) { ARTPWriter::ARTPWriter(int fd) : mFlags(0), - mFd(fd), + mFd(dup(fd)), mLooper(new ALooper), mReflector(new AHandlerReflector<ARTPWriter>(this)) { CHECK_GE(fd, 0); diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp deleted file mode 100644 index 8b2c36c..0000000 --- a/media/libstagefright/string.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2009 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 "include/stagefright_string.h" - -#include <media/stagefright/MediaDebug.h> - -namespace android { - -// static -string::size_type string::npos = (string::size_type)-1; - -string::string() { -} - -string::string(const char *s, size_t length) - : mString(s, length) { -} - -string::string(const string &from, size_type start, size_type length) { - CHECK(start <= from.size()); - if (length == npos) { - length = from.size() - start; - } else { - CHECK(start + length <= from.size()); - } - - mString.setTo(from.c_str() + start, length); -} - -string::string(const char *s) - : mString(s) { -} - -const char *string::c_str() const { - return mString.string(); -} - -string::size_type string::size() const { - return mString.length(); -} - -void string::clear() { - mString = String8(); -} - -string::size_type string::find(char c) const { - char s[2]; - s[0] = c; - s[1] = '\0'; - - ssize_t index = mString.find(s); - - return index < 0 ? npos : (size_type)index; -} - -bool string::operator<(const string &other) const { - return mString < other.mString; -} - -bool string::operator==(const string &other) const { - return mString == other.mString; -} - -string &string::operator+=(char c) { - mString.append(&c, 1); - - return *this; -} - -void string::erase(size_t from, size_t length) { - String8 s(mString.string(), from); - s.append(mString.string() + from + length); - - mString = s; -} - -} // namespace android - diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp index 20dd94d..e1d1a92 100644 --- a/media/mtp/MtpDataPacket.cpp +++ b/media/mtp/MtpDataPacket.cpp @@ -413,20 +413,32 @@ int MtpDataPacket::read(struct usb_endpoint *ep) { } int MtpDataPacket::readData(struct usb_endpoint *ep, void* buffer, int length) { - int packetSize = usb_endpoint_max_packet(ep); int read = 0; while (read < length) { - int ret = transfer(ep, (char *)buffer + read, packetSize); + int ret = transfer(ep, (char *)buffer + read, length - read); if (ret < 0) { -printf("MtpDataPacket::readData returning %d\n", ret); return ret; } read += ret; } -printf("MtpDataPacket::readData returning %d\n", read); return read; } +// Queue a read request. Call readDataWait to wait for result +int MtpDataPacket::readDataAsync(struct usb_endpoint *ep, void* buffer, int length) { + if (usb_endpoint_queue(ep, buffer, length)) { + LOGE("usb_endpoint_queue failed, errno: %d", errno); + return -1; + } + return 0; +} + +// Wait for result of readDataAsync +int MtpDataPacket::readDataWait(struct usb_endpoint *ep) { + int ep_num; + return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num); +} + int MtpDataPacket::readDataHeader(struct usb_endpoint *ep) { int length = transfer(ep, mBuffer, usb_endpoint_max_packet(ep)); if (length >= 0) @@ -454,15 +466,7 @@ int MtpDataPacket::write(struct usb_endpoint *ep) { } int MtpDataPacket::write(struct usb_endpoint *ep, void* buffer, uint32_t length) { - int ret = 0; - int packetSize = usb_endpoint_max_packet(ep); - while (length > 0) { - int write = (length > packetSize ? packetSize : length); - int ret = transfer(ep, buffer, write); - if (ret < 0) - break; - length -= ret; - } + int ret = transfer(ep, buffer, length); return (ret < 0 ? ret : 0); } diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h index fab6a07..3ae6226 100644 --- a/media/mtp/MtpDataPacket.h +++ b/media/mtp/MtpDataPacket.h @@ -102,6 +102,8 @@ public: #ifdef MTP_HOST int read(struct usb_endpoint *ep); int readData(struct usb_endpoint *ep, void* buffer, int length); + int readDataAsync(struct usb_endpoint *ep, void* buffer, int length); + int readDataWait(struct usb_endpoint *ep); int readDataHeader(struct usb_endpoint *ep); int writeDataHeader(struct usb_endpoint *ep, uint32_t length); @@ -110,6 +112,7 @@ public: #endif inline bool hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; } + inline uint32_t getContainerLength() const { return MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); } void* getData(int& outLength) const; }; diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp index fca0142..416ebfe 100644 --- a/media/mtp/MtpDevice.cpp +++ b/media/mtp/MtpDevice.cpp @@ -348,97 +348,96 @@ MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) { return NULL; } -class ReadObjectThread : public Thread { -private: - MtpDevice* mDevice; - MtpObjectHandle mHandle; - int mObjectSize; - void* mInitialData; - int mInitialDataLength; - int mFD; - -public: - ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize) - : mDevice(device), - mHandle(handle), - mObjectSize(objectSize), - mInitialData(NULL), - mInitialDataLength(0) - { +// reads the object's data and writes it to the specified file path +bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) { + LOGD("readObject: %s", destPath); + int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC); + if (fd < 0) { + LOGE("open failed for %s", destPath); + return false; } - virtual ~ReadObjectThread() { - if (mFD >= 0) - close(mFD); - free(mInitialData); - } + fchown(fd, getuid(), group); + // set permissions + int mask = umask(0); + fchmod(fd, perm); + umask(mask); - // returns file descriptor - int init() { - mDevice->mRequest.reset(); - mDevice->mRequest.setParameter(1, mHandle); - if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT) - && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) { - - // mData will contain header and possibly the beginning of the object data - mInitialData = mDevice->mData.getData(mInitialDataLength); - - // create a pipe for the client to read from - int pipefd[2]; - if (pipe(pipefd) < 0) { - LOGE("pipe failed (%s)", strerror(errno)); - return -1; - } - - mFD = pipefd[1]; - return pipefd[0]; - } else { - return -1; - } - } + Mutex::Autolock autoLock(mMutex); + bool result = false; - virtual bool threadLoop() { - int remaining = mObjectSize; - if (mInitialData) { - write(mFD, mInitialData, mInitialDataLength); - remaining -= mInitialDataLength; - free(mInitialData); - mInitialData = NULL; + mRequest.reset(); + mRequest.setParameter(1, handle); + if (sendRequest(MTP_OPERATION_GET_OBJECT) + && mData.readDataHeader(mEndpointIn)) { + uint32_t length = mData.getContainerLength(); + if (length < MTP_CONTAINER_HEADER_SIZE) + goto fail; + length -= MTP_CONTAINER_HEADER_SIZE; + uint32_t remaining = length; + + int initialDataLength = 0; + void* initialData = mData.getData(initialDataLength); + if (initialData) { + if (initialDataLength > 0) { + if (write(fd, initialData, initialDataLength) != initialDataLength) + goto fail; + remaining -= initialDataLength; + } + free(initialData); } - char buffer[16384]; - while (remaining > 0) { - int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining); - int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize); - int written; - if (count >= 0) { - int written = write(mFD, buffer, count); - // FIXME check error - remaining -= count; + // USB reads greater than 16K don't work + char buffer1[16384], buffer2[16384]; + char* readBuffer = buffer1; + char* writeBuffer = NULL; + int writeLength = 0; + + while (remaining > 0 || writeBuffer) { + if (remaining > 0) { + // queue up a read request + int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining); + if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) { + LOGE("readDataAsync failed"); + goto fail; + } } else { - break; + readBuffer = NULL; } - } - MtpResponseCode ret = mDevice->readResponse(); - mDevice->mMutex.unlock(); - return false; - } -}; + if (writeBuffer) { + // write previous buffer + if (write(fd, writeBuffer, writeLength) != writeLength) { + LOGE("write failed"); + // wait for pending read before failing + if (readBuffer) + mData.readDataWait(mEndpointIn); + goto fail; + } + writeBuffer = NULL; + } - // returns the file descriptor for a pipe to read the object's data -int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) { - mMutex.lock(); + // wait for read to complete + if (readBuffer) { + int read = mData.readDataWait(mEndpointIn); + if (read < 0) + goto fail; - ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize); - int fd = thread->init(); - if (fd < 0) { - delete thread; - mMutex.unlock(); - } else { - thread->run("ReadObjectThread"); + writeBuffer = readBuffer; + writeLength = read; + remaining -= read; + readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1); + } + } + + MtpResponseCode response = readResponse(); + if (response == MTP_RESPONSE_OK) + result = true; } - return fd; + +fail: + ::close(fd); + return result; } bool MtpDevice::sendRequest(MtpOperationCode operation) { diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h index 57f492f..21c85d5 100644 --- a/media/mtp/MtpDevice.h +++ b/media/mtp/MtpDevice.h @@ -75,7 +75,8 @@ public: MtpDeviceInfo* getDeviceInfo(); MtpStorageIDList* getStorageIDs(); MtpStorageInfo* getStorageInfo(MtpStorageID storageID); - MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); + MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, + MtpObjectHandle parent); MtpObjectInfo* getObjectInfo(MtpObjectHandle handle); void* getThumbnail(MtpObjectHandle handle, int& outLength); MtpObjectHandle sendObjectInfo(MtpObjectInfo* info); @@ -86,12 +87,10 @@ public: MtpProperty* getDevicePropDesc(MtpDeviceProperty code); - // returns the file descriptor for a pipe to read the object's data - int readObject(MtpObjectHandle handle, int objectSize); + bool readObject(MtpObjectHandle handle, const char* destPath, int group, + int perm); private: - friend class ReadObjectThread; - bool sendRequest(MtpOperationCode operation); bool sendData(); bool readData(); diff --git a/media/tests/CameraBrowser/Android.mk b/media/tests/CameraBrowser/Android.mk index 1d81129..295b3e6 100644 --- a/media/tests/CameraBrowser/Android.mk +++ b/media/tests/CameraBrowser/Android.mk @@ -1,7 +1,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests +LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) diff --git a/media/tests/CameraBrowser/AndroidManifest.xml b/media/tests/CameraBrowser/AndroidManifest.xml index eae0b01..db4a336 100644 --- a/media/tests/CameraBrowser/AndroidManifest.xml +++ b/media/tests/CameraBrowser/AndroidManifest.xml @@ -1,8 +1,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.camerabrowser"> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <application android:label="@string/app_label"> <activity android:name="CameraBrowser" android:label="Camera Browser"> <intent-filter> diff --git a/media/tests/CameraBrowser/res/layout/object_info.xml b/media/tests/CameraBrowser/res/layout/object_info.xml index ac210b9..a0499f2 100644 --- a/media/tests/CameraBrowser/res/layout/object_info.xml +++ b/media/tests/CameraBrowser/res/layout/object_info.xml @@ -153,5 +153,17 @@ <TableRow> <ImageView android:id="@+id/thumbnail" /> </TableRow> + <TableRow> + <Button android:id="@+id/import_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/import_label"> + </Button> + <Button android:id="@+id/delete_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/delete_label"> + </Button> + </TableRow> </TableLayout> diff --git a/media/tests/CameraBrowser/res/menu/object_menu.xml b/media/tests/CameraBrowser/res/menu/object_menu.xml deleted file mode 100644 index a0865f0..0000000 --- a/media/tests/CameraBrowser/res/menu/object_menu.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> - -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:id="@+id/save" - android:title="@string/save_item" /> - <item android:id="@+id/delete" - android:title="@string/delete_item" /> -</menu> diff --git a/media/tests/CameraBrowser/res/values/strings.xml b/media/tests/CameraBrowser/res/values/strings.xml index cd477f1..7955773 100644 --- a/media/tests/CameraBrowser/res/values/strings.xml +++ b/media/tests/CameraBrowser/res/values/strings.xml @@ -32,9 +32,9 @@ <string name="modified_label">Modified: </string> <string name="keywords_label">Keywords: </string> - <!-- menu items --> - <string name="save_item">Save</string> - <string name="delete_item">Delete</string> + <!-- button labels --> + <string name="import_label">Import</string> + <string name="delete_label">Delete</string> <!-- toasts --> <string name="object_saved_message">Object saved</string> diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java new file mode 100644 index 0000000..fb004c4 --- /dev/null +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/DeviceDisconnectedReceiver.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +package com.android.camerabrowser; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.Usb; +import android.net.Uri; + +public class DeviceDisconnectedReceiver extends BroadcastReceiver { + + private final Activity mActivity; + private final int mDeviceID; + + public DeviceDisconnectedReceiver(Activity activity, int deviceID) { + mActivity = activity; + mDeviceID = deviceID; + + IntentFilter filter = new IntentFilter(Usb.ACTION_USB_CAMERA_DETACHED); + filter.addDataScheme("content"); + activity.registerReceiver(this, filter); + } + + @Override + public void onReceive(Context context, Intent intent) { + // close our activity if the device it is displaying is disconnected + Uri uri = intent.getData(); + int id = Integer.parseInt(uri.getPathSegments().get(1)); + if (id == mDeviceID) { + mActivity.finish(); + } + } +}
\ No newline at end of file diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java index 6d34fd4..2060657 100644 --- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectBrowser.java @@ -46,6 +46,7 @@ public class ObjectBrowser extends ListActivity { private int mDeviceID; private long mStorageID; private long mObjectID; + private DeviceDisconnectedReceiver mDisconnectedReceiver; private static final String[] OBJECT_COLUMNS = new String[] { Mtp.Object._ID, Mtp.Object.NAME, Mtp.Object.FORMAT, Mtp.Object.THUMB }; @@ -58,15 +59,17 @@ public class ObjectBrowser extends ListActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + mDeviceID = getIntent().getIntExtra("device", 0); + mStorageID = getIntent().getLongExtra("storage", 0); + mObjectID = getIntent().getLongExtra("object", 0); + mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceID); } @Override protected void onResume() { super.onResume(); - mDeviceID = getIntent().getIntExtra("device", 0); - mStorageID = getIntent().getLongExtra("storage", 0); - mObjectID = getIntent().getLongExtra("object", 0); if (mDeviceID != 0 && mStorageID != 0) { Cursor c; Uri uri; @@ -87,6 +90,12 @@ public class ObjectBrowser extends ListActivity { } @Override + protected void onDestroy() { + unregisterReceiver(mDisconnectedReceiver); + super.onDestroy(); + } + + @Override protected void onListItemClick(ListView l, View v, int position, long id) { long rowID = mAdapter.getItemId(position); Cursor c = getContentResolver().query( diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java index 9f2f98e..4e63128 100644 --- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java @@ -16,6 +16,7 @@ package com.android.camerabrowser; import android.app.Activity; +import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; @@ -23,36 +24,31 @@ import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; -import android.os.FileUtils; -import android.os.ParcelFileDescriptor; -import android.os.Process; import android.provider.Mtp; import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; +import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.Calendar; import java.util.Date; /** * A view to display the properties of an object. */ -public class ObjectViewer extends Activity { +public class ObjectViewer extends Activity implements View.OnClickListener { private static final String TAG = "ObjectViewer"; private int mDeviceID; private long mStorageID; private long mObjectID; + private String mFileName; + private Button mImportButton; + private Button mDeleteButton; + private DeviceDisconnectedReceiver mDisconnectedReceiver; private static final String[] OBJECT_COLUMNS = new String[] { Mtp.Object._ID, @@ -77,15 +73,21 @@ public class ObjectViewer extends Activity { super.onCreate(savedInstanceState); setContentView(R.layout.object_info); - } - @Override - protected void onResume() { - super.onResume(); + mImportButton = (Button)findViewById(R.id.import_button); + mImportButton.setOnClickListener(this); + mDeleteButton = (Button)findViewById(R.id.delete_button); + mDeleteButton.setOnClickListener(this); mDeviceID = getIntent().getIntExtra("device", 0); mStorageID = getIntent().getLongExtra("storage", 0); mObjectID = getIntent().getLongExtra("object", 0); + mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceID); + } + + @Override + protected void onResume() { + super.onResume(); if (mDeviceID != 0 && mObjectID != 0) { Cursor c = getContentResolver().query( @@ -93,7 +95,8 @@ public class ObjectViewer extends Activity { OBJECT_COLUMNS, null, null, null); c.moveToFirst(); TextView view = (TextView)findViewById(R.id.name); - view.setText(c.getString(1)); + mFileName = c.getString(1); + view.setText(mFileName); view = (TextView)findViewById(R.id.size); view.setText(Long.toString(c.getLong(2))); view = (TextView)findViewById(R.id.thumb_width); @@ -132,113 +135,33 @@ public class ObjectViewer extends Activity { } @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.object_menu, menu); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - MenuItem item = menu.findItem(R.id.save); - item.setEnabled(true); - item = menu.findItem(R.id.delete); - item.setEnabled(true); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.save: - save(); - return true; - case R.id.delete: - delete(); - return true; - } - return false; - } - - private static String getTimestamp() { - Calendar c = Calendar.getInstance(); - c.setTimeInMillis(System.currentTimeMillis()); - return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c); + protected void onDestroy() { + unregisterReceiver(mDisconnectedReceiver); + super.onDestroy(); } - private void save() { - boolean success = false; - Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID); - File destFile = null; - ParcelFileDescriptor pfd = null; - FileInputStream fis = null; - FileOutputStream fos = null; - - try { - pfd = getContentResolver().openFileDescriptor(uri, "r"); - Log.d(TAG, "save got pfd " + pfd); - if (pfd != null) { - fis = new FileInputStream(pfd.getFileDescriptor()); - Log.d(TAG, "save got fis " + fis); - File destDir = Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DCIM); - destDir.mkdirs(); - destFile = new File(destDir, "CameraBrowser-" + getTimestamp() + ".jpeg"); - - - Log.d(TAG, "save got destFile " + destFile); - - if (destFile.exists()) { - destFile.delete(); - } - fos = new FileOutputStream(destFile); - - byte[] buffer = new byte[65536]; - int bytesRead; - while ((bytesRead = fis.read(buffer)) >= 0) { - fos.write(buffer, 0, bytesRead); - } + private void importObject() { + // copy file to /mnt/sdcard/imported/<filename> + File dest = Environment.getExternalStorageDirectory(); + dest = new File(dest, "imported"); + dest.mkdirs(); + dest = new File(dest, mFileName); - // temporary workaround until we straighten out permissions in /data/media - FileUtils.setPermissions(destDir.getPath(), 0775, Process.myUid(), Process.SDCARD_RW_GID); - FileUtils.setPermissions(destFile.getPath(), 0664, Process.myUid(), Process.SDCARD_RW_GID); + Uri requestUri = Mtp.Object.getContentUriForImport(mDeviceID, mObjectID, + dest.getAbsolutePath()); + Uri resultUri = getContentResolver().insert(requestUri, new ContentValues()); + Log.d(TAG, "save returned " + resultUri); - success = true; - } - } catch (Exception e) { - Log.e(TAG, "Exception in ObjectView.save", e); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (Exception e) { - } - } - if (fos != null) { - try { - fos.close(); - } catch (Exception e) { - } - } - if (pfd != null) { - try { - pfd.close(); - } catch (Exception e) { - } - } - } - - if (success) { - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - intent.setData(Uri.fromFile(destFile)); - sendBroadcast(intent); + if (resultUri != null) { Toast.makeText(this, R.string.object_saved_message, Toast.LENGTH_SHORT).show(); + Intent intent = new Intent(Intent.ACTION_VIEW, resultUri); + startActivity(intent); } else { Toast.makeText(this, R.string.save_failed_message, Toast.LENGTH_SHORT).show(); } } - private void delete() { + private void deleteObject() { Uri uri = Mtp.Object.getContentUri(mDeviceID, mObjectID); Log.d(TAG, "deleting " + uri); @@ -251,4 +174,12 @@ public class ObjectViewer extends Activity { Toast.makeText(this, R.string.delete_failed_message, Toast.LENGTH_SHORT).show(); } } + + public void onClick(View v) { + if (v == mImportButton) { + importObject(); + } else if (v == mDeleteButton) { + deleteObject(); + } + } } diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java index 63e036e..4da88d6 100644 --- a/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/StorageBrowser.java @@ -37,6 +37,7 @@ public class StorageBrowser extends ListActivity { private ListAdapter mAdapter; private int mDeviceID; + private DeviceDisconnectedReceiver mDisconnectedReceiver; private static final String[] STORAGE_COLUMNS = new String[] { Mtp.Storage._ID, Mtp.Storage.DESCRIPTION }; @@ -44,13 +45,14 @@ public class StorageBrowser extends ListActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mDeviceID = getIntent().getIntExtra("device", 0); + mDisconnectedReceiver = new DeviceDisconnectedReceiver(this, mDeviceID); } @Override protected void onResume() { super.onResume(); - mDeviceID = getIntent().getIntExtra("device", 0); if (mDeviceID != 0) { Cursor c = getContentResolver().query(Mtp.Storage.getContentUri(mDeviceID), STORAGE_COLUMNS, null, null, null); @@ -67,6 +69,12 @@ public class StorageBrowser extends ListActivity { } @Override + protected void onDestroy() { + unregisterReceiver(mDisconnectedReceiver); + super.onDestroy(); + } + + @Override protected void onListItemClick(ListView l, View v, int position, long id) { Intent intent = new Intent(this, ObjectBrowser.class); intent.putExtra("device", mDeviceID); diff --git a/media/tests/mtp/Android.mk b/media/tests/mtp/Android.mk deleted file mode 100644 index a9074ed..0000000 --- a/media/tests/mtp/Android.mk +++ /dev/null @@ -1,61 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -ifneq ($(TARGET_SIMULATOR),true) - -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) - -endif - -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 deleted file mode 100644 index 00d328e..0000000 --- a/media/tests/mtp/MtpFile.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 deleted file mode 100644 index ab8762b..0000000 --- a/media/tests/mtp/MtpFile.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3202cae..0000000 --- a/media/tests/mtp/mtp.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - * 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] == (char)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); -} |