Add caching mechanism to speed up midi file reading speed.

This cache mechanism reduces the number of DataSource reads and
binder round trips to the IDataSource. For midi files smaller
than 1.5M, the pre-read data mechanism will be enabled.
Its purpose is to reduce the time spent on filetype sniffing.

Issue:164977553

Change-Id: I1db7c739a69f4324cc339b8521b5a6998e8a8df0
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index da272e3..f682b6e 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -21,6 +21,7 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <algorithm>
 
 #include <media/MidiIoWrapper.h>
 #include <media/MediaExtractorPluginApi.h>
@@ -33,6 +34,8 @@
 }
 
 namespace android {
+int MidiIoWrapper::sCacheBufferSize = 0;
+Mutex MidiIoWrapper::mCacheLock;
 
 MidiIoWrapper::MidiIoWrapper(const char *path) {
     ALOGV("MidiIoWrapper(%s)", path);
@@ -40,6 +43,8 @@
     mBase = 0;
     mLength = lseek(mFd, 0, SEEK_END);
     mDataSource = nullptr;
+    mCacheBuffer = NULL;
+    mCacheBufRangeLength = 0;
 }
 
 MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) {
@@ -48,6 +53,8 @@
     mBase = offset;
     mLength = size;
     mDataSource = nullptr;
+    mCacheBuffer = NULL;
+    mCacheBufRangeLength = 0;
 }
 
 class MidiIoWrapper::DataSourceUnwrapper {
@@ -97,6 +104,8 @@
     } else {
         mLength = 0;
     }
+    mCacheBuffer = NULL;
+    mCacheBufRangeLength = 0;
 }
 
 MidiIoWrapper::~MidiIoWrapper() {
@@ -105,11 +114,80 @@
         close(mFd);
     }
     delete mDataSource;
+
+    if (NULL != mCacheBuffer) {
+        delete [] mCacheBuffer;
+        mCacheBuffer = NULL;
+        {
+            Mutex::Autolock _l(mCacheLock);
+            sCacheBufferSize -= mLength;
+        }
+    }
 }
 
 int MidiIoWrapper::readAt(void *buffer, int offset, int size) {
     ALOGV("readAt(%p, %d, %d)", buffer, offset, size);
 
+    if (offset < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (offset + size > mLength) {
+        size = mLength - offset;
+    }
+
+    if (mCacheBuffer == NULL) {
+        Mutex::Autolock _l(mCacheLock);
+        if (sCacheBufferSize + mLength <= kTotalCacheSize) {
+            mCacheBuffer = new (std::nothrow) unsigned char[mLength];
+            if (NULL != mCacheBuffer) {
+                sCacheBufferSize += mLength;
+                ALOGV("sCacheBufferSize : %d", sCacheBufferSize);
+            } else {
+                ALOGE("failed to allocate memory for mCacheBuffer");
+            }
+        } else {
+            ALOGV("not allocate memory for mCacheBuffer");
+        }
+    }
+
+    if (mCacheBuffer != NULL) {
+        if (mCacheBufRangeLength > 0 && mCacheBufRangeLength >= (offset + size)) {
+            /* Use buffered data */
+            memcpy(buffer, (void*)(mCacheBuffer + offset), size);
+            return size;
+        } else {
+            /* Buffer new data */
+            int64_t beyondCacheBufRangeLength = (offset + size) - mCacheBufRangeLength;
+            int64_t numRequiredBytesToCache =
+                  std::max((int64_t)kSingleCacheSize, beyondCacheBufRangeLength);
+            int64_t availableReadLength = mLength - mCacheBufRangeLength;
+            int64_t readSize = std::min(availableReadLength, numRequiredBytesToCache);
+            int actualNumBytesRead =
+                unbufferedReadAt(mCacheBuffer + mCacheBufRangeLength,
+                        mCacheBufRangeLength, readSize);
+            if(actualNumBytesRead > 0) {
+                mCacheBufRangeLength += actualNumBytesRead;
+                if (offset >= mCacheBufRangeLength) {
+                    return 0;
+                } else if (offset + size >= mCacheBufRangeLength) {
+                    memcpy(buffer, (void*)(mCacheBuffer + offset), mCacheBufRangeLength - offset);
+                    return mCacheBufRangeLength - offset;
+                } else {
+                    memcpy(buffer, (void*)(mCacheBuffer + offset), size);
+                    return size;
+                }
+            } else {
+                return actualNumBytesRead;
+            }
+        }
+    } else {
+        return unbufferedReadAt(buffer, offset, size);
+    }
+}
+
+int MidiIoWrapper::unbufferedReadAt(void *buffer, int offset, int size) {
+    ALOGV("unbufferedReadAt(%p, %d, %d)", buffer, offset, size);
     if (mDataSource != NULL) {
         return mDataSource->readAt(offset, buffer, size);
     }
diff --git a/media/libmedia/include/media/MidiIoWrapper.h b/media/libmedia/include/media/MidiIoWrapper.h
index 0cdd4ad..5fa745c 100644
--- a/media/libmedia/include/media/MidiIoWrapper.h
+++ b/media/libmedia/include/media/MidiIoWrapper.h
@@ -18,6 +18,7 @@
 #define MIDI_IO_WRAPPER_H_
 
 #include <libsonivox/eas_types.h>
+#include <utils/Mutex.h>
 
 namespace android {
 
@@ -32,17 +33,27 @@
     ~MidiIoWrapper();
 
     int readAt(void *buffer, int offset, int size);
+    int unbufferedReadAt(void *buffer, int offset, int size);
     int size();
 
     EAS_FILE_LOCATOR getLocator();
 
 private:
+    enum {
+        kTotalCacheSize      = 1024 * 1024 + 512 * 1024,
+        kSingleCacheSize     = 65536,
+    };
+
     int mFd;
     off64_t mBase;
     int64_t  mLength;
     class DataSourceUnwrapper;
     DataSourceUnwrapper *mDataSource;
     EAS_FILE mEasFile;
+    unsigned char *mCacheBuffer;
+    int64_t mCacheBufRangeLength;
+    static int sCacheBufferSize;
+    static Mutex mCacheLock;
 };