media: handle overrides and measure max codec instance.

Bug: 19620911
Change-Id: I68d5919284700f37ccc6c6b9f96cd87ccdd40e6a
diff --git a/include/media/IMediaCodecList.h b/include/media/IMediaCodecList.h
index e93ea8b..12b52d7 100644
--- a/include/media/IMediaCodecList.h
+++ b/include/media/IMediaCodecList.h
@@ -21,6 +21,8 @@
 #include <binder/IInterface.h>
 #include <binder/Parcel.h>
 
+#include <media/stagefright/foundation/AMessage.h>
+
 namespace android {
 
 struct MediaCodecInfo;
@@ -33,6 +35,8 @@
     virtual size_t countCodecs() const = 0;
     virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const = 0;
 
+    virtual const sp<AMessage> getGlobalSettings() const = 0;
+
     virtual ssize_t findCodecByType(
             const char *type, bool encoder, size_t startIndex = 0) const = 0;
 
diff --git a/include/media/MediaCodecInfo.h b/include/media/MediaCodecInfo.h
index cd56adb..895a13a 100644
--- a/include/media/MediaCodecInfo.h
+++ b/include/media/MediaCodecInfo.h
@@ -35,6 +35,8 @@
 struct Parcel;
 struct CodecCapabilities;
 
+typedef KeyedVector<AString, AString> CodecSettings;
+
 struct MediaCodecInfo : public RefBase {
     struct ProfileLevel {
         uint32_t mProfile;
@@ -104,6 +106,7 @@
     MediaCodecInfo(AString name, bool encoder, const char *mime);
     void addQuirk(const char *name);
     status_t addMime(const char *mime);
+    status_t updateMime(const char *mime);
     status_t initializeCapabilities(const CodecCapabilities &caps);
     void addDetail(const AString &key, const AString &value);
     void addFeature(const AString &key, int32_t value);
@@ -114,6 +117,7 @@
     DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
 
     friend class MediaCodecList;
+    friend class MediaCodecListOverridesTest;
 };
 
 }  // namespace android
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index 53f3095..9d1d675 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -48,9 +48,14 @@
         return mCodecInfos.itemAt(index);
     }
 
+    virtual const sp<AMessage> getGlobalSettings() const;
+
     // to be used by MediaPlayerService alone
     static sp<IMediaCodecList> getLocalInstance();
 
+    // only to be used in getLocalInstance
+    void updateDetailsForMultipleCodecs(const KeyedVector<AString, CodecSettings>& updates);
+
 private:
     class BinderDeathObserver : public IBinder::DeathRecipient {
         void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -75,11 +80,14 @@
 
     status_t mInitCheck;
     Section mCurrentSection;
+    bool mUpdate;
     Vector<Section> mPastSections;
     int32_t mDepth;
     AString mHrefBase;
 
-    KeyedVector<AString, AString> mSettings;
+    sp<AMessage> mGlobalSettings;
+    KeyedVector<AString, CodecSettings> mOverrides;
+
     Vector<sp<MediaCodecInfo> > mCodecInfos;
     sp<MediaCodecInfo> mCurrentInfo;
     sp<IOMX> mOMX;
@@ -89,7 +97,7 @@
 
     status_t initCheck() const;
     void parseXMLFile(const char *path);
-    void parseTopLevelXMLFile(const char *path);
+    void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
 
     static void StartElementHandlerWrapper(
             void *me, const char *name, const char **attrs);
@@ -104,6 +112,8 @@
     status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
     void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
 
+    void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+
     status_t addQuirk(const char **attrs);
     status_t addTypeFromAttributes(const char **attrs);
     status_t addLimit(const char **attrs);
diff --git a/media/libmedia/IMediaCodecList.cpp b/media/libmedia/IMediaCodecList.cpp
index 80020db..e2df104 100644
--- a/media/libmedia/IMediaCodecList.cpp
+++ b/media/libmedia/IMediaCodecList.cpp
@@ -30,6 +30,7 @@
     CREATE = IBinder::FIRST_CALL_TRANSACTION,
     COUNT_CODECS,
     GET_CODEC_INFO,
+    GET_GLOBAL_SETTINGS,
     FIND_CODEC_BY_TYPE,
     FIND_CODEC_BY_NAME,
 };
@@ -64,6 +65,19 @@
         }
     }
 
+    virtual const sp<AMessage> getGlobalSettings() const
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaCodecList::getInterfaceDescriptor());
+        remote()->transact(GET_GLOBAL_SETTINGS, data, &reply);
+        status_t err = reply.readInt32();
+        if (err == OK) {
+            return AMessage::FromParcel(reply);
+        } else {
+            return NULL;
+        }
+    }
+
     virtual ssize_t findCodecByType(
             const char *type, bool encoder, size_t startIndex = 0) const
     {
@@ -125,6 +139,20 @@
         }
         break;
 
+        case GET_GLOBAL_SETTINGS:
+        {
+            CHECK_INTERFACE(IMediaCodecList, data, reply);
+            const sp<AMessage> info = getGlobalSettings();
+            if (info != NULL) {
+                reply->writeInt32(OK);
+                info->writeToParcel(reply);
+            } else {
+                reply->writeInt32(-ERANGE);
+            }
+            return NO_ERROR;
+        }
+        break;
+
         case FIND_CODEC_BY_TYPE:
         {
             CHECK_INTERFACE(IMediaCodecList, data, reply);
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 7b4c4e2..8d3fa7b 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -206,6 +206,17 @@
     return OK;
 }
 
+status_t MediaCodecInfo::updateMime(const char *mime) {
+    ssize_t ix = getCapabilityIndex(mime);
+    if (ix < 0) {
+        ALOGE("updateMime mime not found %s", mime);
+        return -EINVAL;
+    }
+
+    mCurrentCaps = mCaps.valueAt(ix);
+    return OK;
+}
+
 void MediaCodecInfo::removeMime(const char *mime) {
     ssize_t ix = getCapabilityIndex(mime);
     if (ix >= 0) {
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index a2cbdaf..b0eeb7f 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -34,6 +34,7 @@
         MediaClock.cpp                    \
         MediaCodec.cpp                    \
         MediaCodecList.cpp                \
+        MediaCodecListOverrides.cpp       \
         MediaCodecSource.cpp              \
         MediaDefs.cpp                     \
         MediaExtractor.cpp                \
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 3e757c7..26798ae 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -18,6 +18,8 @@
 #define LOG_TAG "MediaCodecList"
 #include <utils/Log.h>
 
+#include "MediaCodecListOverrides.h"
+
 #include <binder/IServiceManager.h>
 
 #include <media/IMediaCodecList.h>
@@ -31,6 +33,7 @@
 #include <media/stagefright/OMXClient.h>
 #include <media/stagefright/OMXCodec.h>
 
+#include <sys/stat.h>
 #include <utils/threads.h>
 
 #include <libexpat/expat.h>
@@ -41,6 +44,8 @@
 
 static MediaCodecList *gCodecList = NULL;
 
+static const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
+
 static bool parseBoolean(const char *s) {
     if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
         return true;
@@ -55,16 +60,42 @@
 
 // static
 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
-    Mutex::Autolock autoLock(sInitMutex);
+    bool profilingNeeded = false;
+    KeyedVector<AString, CodecSettings> updates;
+    Vector<sp<MediaCodecInfo>> infos;
 
-    if (gCodecList == NULL) {
-        gCodecList = new MediaCodecList;
-        if (gCodecList->initCheck() == OK) {
-            sCodecList = gCodecList;
+    {
+        Mutex::Autolock autoLock(sInitMutex);
+
+        if (gCodecList == NULL) {
+            gCodecList = new MediaCodecList;
+            if (gCodecList->initCheck() == OK) {
+                sCodecList = gCodecList;
+
+                struct stat s;
+                if (stat(kProfilingResults, &s) == -1) {
+                    // profiling results doesn't existed
+                    profilingNeeded = true;
+                    for (size_t i = 0; i < gCodecList->countCodecs(); ++i) {
+                        infos.push_back(gCodecList->getCodecInfo(i));
+                    }
+                }
+            }
         }
     }
 
-    return sCodecList;
+    if (profilingNeeded) {
+        profileCodecs(infos, &updates);
+    }
+
+    {
+        Mutex::Autolock autoLock(sInitMutex);
+        if (updates.size() > 0) {
+            gCodecList->updateDetailsForMultipleCodecs(updates);
+        }
+
+        return sCodecList;
+    }
 }
 
 static Mutex sRemoteInitMutex;
@@ -103,11 +134,27 @@
 }
 
 MediaCodecList::MediaCodecList()
-    : mInitCheck(NO_INIT) {
+    : mInitCheck(NO_INIT),
+      mUpdate(false),
+      mGlobalSettings(new AMessage()) {
     parseTopLevelXMLFile("/etc/media_codecs.xml");
+    parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
 }
 
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
+void MediaCodecList::updateDetailsForMultipleCodecs(
+        const KeyedVector<AString, CodecSettings>& updates) {
+    if (updates.size() == 0) {
+        return;
+    }
+
+    exportResultsToXML(kProfilingResults, updates);
+
+    for (size_t i = 0; i < updates.size(); ++i) {
+        applyCodecSettings(updates.keyAt(i), updates.valueAt(i), &mCodecInfos);
+    }
+}
+
+void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
     // get href_base
     char *href_base_end = strrchr(codecs_xml, '/');
     if (href_base_end != NULL) {
@@ -128,21 +175,16 @@
     mOMX.clear();
 
     if (mInitCheck != OK) {
+        if (ignore_errors) {
+            mInitCheck = OK;
+            return;
+        }
         mCodecInfos.clear();
         return;
     }
 
-    // TODO: parse/create overrides.xml, update mCodecInfos and mSettings with overrides.
-
     for (size_t i = mCodecInfos.size(); i-- > 0;) {
         const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-        for (size_t i = 0; i < info.mCaps.size(); ++i) {
-            const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(i);
-            for (size_t j = 0; j < mSettings.size(); ++j) {
-                caps->getDetails()->setString(mSettings.keyAt(j).c_str(), mSettings[j].c_str());
-            }
-        }
-
         if (info.mCaps.size() == 0) {
             // No types supported by this component???
             ALOGW("Component %s does not support any type of media?",
@@ -186,6 +228,16 @@
                     }
                     ALOGV("    levels=[%s]", nice.c_str());
                 }
+                {
+                    AString quirks;
+                    for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
+                        if (ix > 0) {
+                            quirks.append(", ");
+                        }
+                        quirks.append(info.mQuirks[ix]);
+                    }
+                    ALOGV("    quirks=[%s]", quirks.c_str());
+                }
             }
 #endif
         }
@@ -533,20 +585,45 @@
         return -EINVAL;
     }
 
-    bool isUpdate = (update != NULL) && parseBoolean(update);
-    bool isExisted = (mSettings.indexOfKey(name) >= 0);
-    if (isUpdate != isExisted) {
+    mUpdate = (update != NULL) && parseBoolean(update);
+    if (mUpdate != mGlobalSettings->contains(name)) {
         return -EINVAL;
     }
 
-    mSettings.add(name, value);
+    mGlobalSettings->setString(name, value);
     return OK;
 }
 
+void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
+    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+        if (AString(name) == mCodecInfos[i]->getCodecName()) {
+            if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
+                ALOGW("Overrides with an unexpected mime %s", type);
+                // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
+                // overrides we don't want.
+                mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+            } else {
+                mCurrentInfo = mCodecInfos.editItemAt(i);
+                mCurrentInfo->updateMime(type);  // to set the current cap
+            }
+            return;
+        }
+    }
+    mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+    // The next step involves trying to load the codec, which may
+    // fail.  Only list the codec if this succeeds.
+    // However, keep mCurrentInfo object around until parsing
+    // of full codec info is completed.
+    if (initializeCapabilities(type) == OK) {
+        mCodecInfos.push_back(mCurrentInfo);
+    }
+}
+
 status_t MediaCodecList::addMediaCodecFromAttributes(
         bool encoder, const char **attrs) {
     const char *name = NULL;
     const char *type = NULL;
+    const char *update = NULL;
 
     size_t i = 0;
     while (attrs[i] != NULL) {
@@ -562,6 +639,12 @@
             }
             type = attrs[i + 1];
             ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
         } else {
             return -EINVAL;
         }
@@ -573,14 +656,39 @@
         return -EINVAL;
     }
 
-    mCurrentInfo = new MediaCodecInfo(name, encoder, type);
-    // The next step involves trying to load the codec, which may
-    // fail.  Only list the codec if this succeeds.
-    // However, keep mCurrentInfo object around until parsing
-    // of full codec info is completed.
-    if (initializeCapabilities(type) == OK) {
-        mCodecInfos.push_back(mCurrentInfo);
+    mUpdate = (update != NULL) && parseBoolean(update);
+    ssize_t index = -1;
+    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+        if (AString(name) == mCodecInfos[i]->getCodecName()) {
+            index = i;
+        }
     }
+    if (mUpdate != (index >= 0)) {
+        return -EINVAL;
+    }
+
+    if (index >= 0) {
+        // existing codec
+        mCurrentInfo = mCodecInfos.editItemAt(index);
+        if (type != NULL) {
+            // existing type
+            if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
+                return -EINVAL;
+            }
+            mCurrentInfo->updateMime(type);
+        }
+    } else {
+        // new codec
+        mCurrentInfo = new MediaCodecInfo(name, encoder, type);
+        // The next step involves trying to load the codec, which may
+        // fail.  Only list the codec if this succeeds.
+        // However, keep mCurrentInfo object around until parsing
+        // of full codec info is completed.
+        if (initializeCapabilities(type) == OK) {
+            mCodecInfos.push_back(mCurrentInfo);
+        }
+    }
+
     return OK;
 }
 
@@ -634,6 +742,7 @@
 
 status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
     const char *name = NULL;
+    const char *update = NULL;
 
     size_t i = 0;
     while (attrs[i] != NULL) {
@@ -643,6 +752,12 @@
             }
             name = attrs[i + 1];
             ++i;
+        } else if (!strcmp(attrs[i], "update")) {
+            if (attrs[i + 1] == NULL) {
+                return -EINVAL;
+            }
+            update = attrs[i + 1];
+            ++i;
         } else {
             return -EINVAL;
         }
@@ -654,14 +769,25 @@
         return -EINVAL;
     }
 
-    status_t ret = mCurrentInfo->addMime(name);
+    bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
+    if (mUpdate != isExistingType) {
+        return -EINVAL;
+    }
+
+    status_t ret;
+    if (mUpdate) {
+        ret = mCurrentInfo->updateMime(name);
+    } else {
+        ret = mCurrentInfo->addMime(name);
+    }
+
     if (ret != OK) {
         return ret;
     }
 
     // The next step involves trying to load the codec, which may
     // fail.  Handle this gracefully (by not reporting such mime).
-    if (initializeCapabilities(name) != OK) {
+    if (!mUpdate && initializeCapabilities(name) != OK) {
         mCurrentInfo->removeMime(name);
     }
     return OK;
@@ -933,4 +1059,8 @@
     return mCodecInfos.size();
 }
 
+const sp<AMessage> MediaCodecList::getGlobalSettings() const {
+    return mGlobalSettings;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
new file mode 100644
index 0000000..3c54f34
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2015 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecListOverrides"
+#include <utils/Log.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <gui/Surface.h>
+#include <media/ICrypto.h>
+#include <media/IMediaCodecList.h>
+#include <media/MediaCodecInfo.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodec.h>
+
+namespace android {
+
+// a limit to avoid allocating unreasonable number of codec instances in the measurement.
+// this should be in sync with the MAX_SUPPORTED_INSTANCES defined in MediaCodecInfo.java.
+static const int kMaxInstances = 32;
+
+// TODO: move MediaCodecInfo to C++. Until then, some temp methods to parse out info.
+static bool getMeasureSize(sp<MediaCodecInfo::Capabilities> caps, int32_t *width, int32_t *height) {
+    AString sizeRange;
+    if (!caps->getDetails()->findString("size-range", &sizeRange)) {
+        return false;
+    }
+    AString minSize;
+    AString maxSize;
+    if (!splitString(sizeRange, "-", &minSize, &maxSize)) {
+        return false;
+    }
+    AString sWidth;
+    AString sHeight;
+    if (!splitString(minSize, "x", &sWidth, &sHeight)) {
+        if (!splitString(minSize, "*", &sWidth, &sHeight)) {
+            return false;
+        }
+    }
+
+    *width = strtol(sWidth.c_str(), NULL, 10);
+    *height = strtol(sHeight.c_str(), NULL, 10);
+    return (*width > 0) && (*height > 0);
+}
+
+static void getMeasureBitrate(sp<MediaCodecInfo::Capabilities> caps, int32_t *bitrate) {
+    // Until have native MediaCodecInfo, we cannot get bitrates based on profile/levels.
+    // We use 200000 as default value for our measurement.
+    *bitrate = 200000;
+    AString bitrateRange;
+    if (!caps->getDetails()->findString("bitrate-range", &bitrateRange)) {
+        return;
+    }
+    AString minBitrate;
+    AString maxBitrate;
+    if (!splitString(bitrateRange, "-", &minBitrate, &maxBitrate)) {
+        return;
+    }
+
+    *bitrate = strtol(minBitrate.c_str(), NULL, 10);
+}
+
+static sp<AMessage> getMeasureFormat(
+        bool isEncoder, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+    sp<AMessage> format = new AMessage();
+    format->setString("mime", mime);
+
+    if (isEncoder) {
+        int32_t bitrate = 0;
+        getMeasureBitrate(caps, &bitrate);
+        format->setInt32("bitrate", bitrate);
+    }
+
+    if (mime.startsWith("video/")) {
+        int32_t width = 0;
+        int32_t height = 0;
+        if (!getMeasureSize(caps, &width, &height)) {
+            return NULL;
+        }
+        format->setInt32("width", width);
+        format->setInt32("height", height);
+
+        Vector<uint32_t> colorFormats;
+        caps->getSupportedColorFormats(&colorFormats);
+        if (colorFormats.size() == 0) {
+            return NULL;
+        }
+        format->setInt32("color-format", colorFormats[0]);
+
+        format->setFloat("frame-rate", 10.0);
+        format->setInt32("i-frame-interval", 10);
+    } else {
+        // TODO: profile hw audio
+        return NULL;
+    }
+
+    return format;
+}
+
+static size_t doProfileCodecs(
+        bool isEncoder, AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) {
+    sp<AMessage> format = getMeasureFormat(isEncoder, mime, caps);
+    if (format == NULL) {
+        return 0;
+    }
+    if (isEncoder) {
+        format->setInt32("encoder", 1);
+    }
+    ALOGV("doProfileCodecs %s %s %s %s",
+            name.c_str(), mime.c_str(), isEncoder ? "encoder" : "decoder",
+            format->debugString().c_str());
+
+    status_t err = OK;
+    Vector<sp<MediaCodec>> codecs;
+    while (err == OK && codecs.size() < kMaxInstances) {
+        sp<ALooper> looper = new ALooper;
+        looper->setName("MediaCodec_looper");
+        ALOGV("doProfileCodecs for codec #%u", codecs.size());
+        ALOGV("doProfileCodecs start looper");
+        looper->start(
+                false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO);
+        ALOGV("doProfileCodecs CreateByComponentName");
+        sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err);
+        if (err != OK) {
+            ALOGV("Failed to create codec: %s", name.c_str());
+            break;
+        }
+        const sp<Surface> nativeWindow;
+        const sp<ICrypto> crypto;
+        uint32_t flags = 0;
+        ALOGV("doProfileCodecs configure");
+        err = codec->configure(format, nativeWindow, crypto, flags);
+        if (err != OK) {
+            ALOGV("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str());
+            codec->release();
+            break;
+        }
+        ALOGV("doProfileCodecs start");
+        err = codec->start();
+        if (err != OK) {
+            ALOGV("Failed to start codec: %s with mime: %s", name.c_str(), mime.c_str());
+            codec->release();
+            break;
+        }
+        codecs.push_back(codec);
+    }
+
+    for (size_t i = 0; i < codecs.size(); ++i) {
+        ALOGV("doProfileCodecs release %s", name.c_str());
+        err = codecs[i]->release();
+        if (err != OK) {
+            ALOGE("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str());
+        }
+    }
+
+    return codecs.size();
+}
+
+static void printLongString(const char *buf, size_t size) {
+    AString print;
+    const char *start = buf;
+    size_t len;
+    size_t totalLen = size;
+    while (totalLen > 0) {
+        len = (totalLen > 500) ? 500 : totalLen;
+        print.setTo(start, len);
+        ALOGV("%s", print.c_str());
+        totalLen -= len;
+        start += len;
+    }
+}
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2) {
+    ssize_t pos = s.find(delimiter.c_str());
+    if (pos < 0) {
+        return false;
+    }
+    *s1 = AString(s, 0, pos);
+    *s2 = AString(s, pos + 1, s.size() - pos - 1);
+    return true;
+}
+
+bool splitString(
+        const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3) {
+    AString temp;
+    if (!splitString(s, delimiter, s1, &temp)) {
+        return false;
+    }
+    if (!splitString(temp, delimiter, s2, s3)) {
+        return false;
+    }
+    return true;
+}
+
+void profileCodecs(
+        const Vector<sp<MediaCodecInfo>> &infos,
+        KeyedVector<AString, CodecSettings> *results,
+        bool forceToMeasure) {
+    KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
+    for (size_t i = 0; i < infos.size(); ++i) {
+        const sp<MediaCodecInfo> info = infos[i];
+        AString name = info->getCodecName();
+        if (name.startsWith("OMX.google.") ||
+                // TODO: reenable below codecs once fixed
+                name == "OMX.Intel.VideoDecoder.VP9.hybrid") {
+            continue;
+        }
+
+        Vector<AString> mimes;
+        info->getSupportedMimes(&mimes);
+        for (size_t i = 0; i < mimes.size(); ++i) {
+            const sp<MediaCodecInfo::Capabilities> &caps =
+                    info->getCapabilitiesFor(mimes[i].c_str());
+            if (!forceToMeasure && caps->getDetails()->contains("max-supported-instances")) {
+                continue;
+            }
+
+            size_t max = doProfileCodecs(info->isEncoder(), name, mimes[i], caps);
+            if (max > 0) {
+                CodecSettings settings;
+                char maxStr[32];
+                sprintf(maxStr, "%u", max);
+                settings.add("max-supported-instances", maxStr);
+
+                AString key = name;
+                key.append(" ");
+                key.append(mimes[i]);
+                key.append(" ");
+                key.append(info->isEncoder() ? "encoder" : "decoder");
+                results->add(key, settings);
+            }
+        }
+    }
+}
+
+void applyCodecSettings(
+        const AString& codecInfo,
+        const CodecSettings &settings,
+        Vector<sp<MediaCodecInfo>> *infos) {
+    AString name;
+    AString mime;
+    AString type;
+    if (!splitString(codecInfo, " ", &name, &mime, &type)) {
+        return;
+    }
+
+    for (size_t i = 0; i < infos->size(); ++i) {
+        const sp<MediaCodecInfo> &info = infos->itemAt(i);
+        if (name != info->getCodecName()) {
+            continue;
+        }
+
+        Vector<AString> mimes;
+        info->getSupportedMimes(&mimes);
+        for (size_t j = 0; j < mimes.size(); ++j) {
+            if (mimes[j] != mime) {
+                continue;
+            }
+            const sp<MediaCodecInfo::Capabilities> &caps = info->getCapabilitiesFor(mime.c_str());
+            for (size_t k = 0; k < settings.size(); ++k) {
+                caps->getDetails()->setString(
+                        settings.keyAt(k).c_str(), settings.valueAt(k).c_str());
+            }
+        }
+    }
+}
+
+void exportResultsToXML(const char *fileName, const KeyedVector<AString, CodecSettings>& results) {
+#if LOG_NDEBUG == 0
+    ALOGE("measurement results");
+    for (size_t i = 0; i < results.size(); ++i) {
+        ALOGE("key %s", results.keyAt(i).c_str());
+        const CodecSettings &settings = results.valueAt(i);
+        for (size_t j = 0; j < settings.size(); ++j) {
+            ALOGE("name %s value %s", settings.keyAt(j).c_str(), settings.valueAt(j).c_str());
+        }
+    }
+#endif
+
+    AString overrides;
+    FILE *f = fopen(fileName, "rb");
+    if (f != NULL) {
+        fseek(f, 0, SEEK_END);
+        long size = ftell(f);
+        rewind(f);
+
+        char *buf = (char *)malloc(size);
+        if (fread(buf, size, 1, f) == 1) {
+            overrides.setTo(buf, size);
+#if LOG_NDEBUG == 0
+            ALOGV("Existing overrides:");
+            printLongString(buf, size);
+#endif
+        } else {
+            ALOGE("Failed to read %s", fileName);
+        }
+        fclose(f);
+        free(buf);
+    }
+
+    for (size_t i = 0; i < results.size(); ++i) {
+        AString name;
+        AString mime;
+        AString type;
+        if (!splitString(results.keyAt(i), " ", &name, &mime, &type)) {
+            continue;
+        }
+        name = AStringPrintf("\"%s\"", name.c_str());
+        mime = AStringPrintf("\"%s\"", mime.c_str());
+        ALOGV("name(%s) mime(%s) type(%s)", name.c_str(), mime.c_str(), type.c_str());
+        ssize_t posCodec = overrides.find(name.c_str());
+        size_t posInsert = 0;
+        if (posCodec < 0) {
+            AString encodersDecoders = (type == "encoder") ? "<Encoders>" : "<Decoders>";
+            AString encodersDecodersEnd = (type == "encoder") ? "</Encoders>" : "</Decoders>";
+            ssize_t posEncodersDecoders = overrides.find(encodersDecoders.c_str());
+            if (posEncodersDecoders < 0) {
+                AString mediaCodecs = "<MediaCodecs>";
+                ssize_t posMediaCodec = overrides.find(mediaCodecs.c_str());
+                if (posMediaCodec < 0) {
+                    posMediaCodec = overrides.size();
+                    overrides.insert("\n<MediaCodecs>\n</MediaCodecs>\n", posMediaCodec);
+                    posMediaCodec = overrides.find(mediaCodecs.c_str(), posMediaCodec);
+                }
+                posEncodersDecoders = posMediaCodec + mediaCodecs.size();
+                AString codecs = AStringPrintf(
+                        "\n    %s\n    %s", encodersDecoders.c_str(), encodersDecodersEnd.c_str());
+                overrides.insert(codecs.c_str(), posEncodersDecoders);
+                posEncodersDecoders = overrides.find(encodersDecoders.c_str(), posEncodersDecoders);
+            }
+            posCodec = posEncodersDecoders + encodersDecoders.size();
+            AString codec = AStringPrintf(
+                        "\n        <MediaCodec name=%s type=%s update=\"true\" >\n        </MediaCodec>",
+                        name.c_str(),
+                        mime.c_str());
+            overrides.insert(codec.c_str(), posCodec);
+            posCodec = overrides.find(name.c_str());
+        }
+
+        // insert to existing entry
+        ssize_t posMime = overrides.find(mime.c_str(), posCodec);
+        ssize_t posEnd = overrides.find(">", posCodec);
+        if (posEnd < 0) {
+            ALOGE("Format error in overrides file.");
+            return;
+        }
+        if (posMime < 0 || posMime > posEnd) {
+            // new mime for an existing component
+            AString codecEnd = "</MediaCodec>";
+            posInsert = overrides.find(codecEnd.c_str(), posCodec) + codecEnd.size();
+            AString codec = AStringPrintf(
+                    "\n        <MediaCodec name=%s type=%s update=\"true\" >\n        </MediaCodec>",
+                    name.c_str(),
+                    mime.c_str());
+            overrides.insert(codec.c_str(), posInsert);
+            posInsert = overrides.find(">", posInsert) + 1;
+        } else {
+            posInsert = posEnd + 1;
+        }
+
+        CodecSettings settings = results.valueAt(i);
+        for (size_t i = 0; i < settings.size(); ++i) {
+            // WARNING: we assume all the settings are "Limit". Currently we have only one type
+            // of setting in this case, which is "max-supported-instances".
+            AString strInsert = AStringPrintf(
+                    "\n            <Limit name=\"%s\" value=\"%s\" />",
+                    settings.keyAt(i).c_str(),
+                    settings.valueAt(i).c_str());
+            overrides.insert(strInsert, posInsert);
+        }
+    }
+
+#if LOG_NDEBUG == 0
+    ALOGV("New overrides:");
+    printLongString(overrides.c_str(), overrides.size());
+#endif
+
+    f = fopen(fileName, "wb");
+    if (f == NULL) {
+        ALOGE("Failed to open %s for writing.", fileName);
+        return;
+    }
+    if (fwrite(overrides.c_str(), 1, overrides.size(), f) != overrides.size()) {
+        ALOGE("Failed to write to %s.", fileName);
+    }
+    fclose(f);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
new file mode 100644
index 0000000..f97ce63
--- /dev/null
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015 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 MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#define MEDIA_CODEC_LIST_OVERRIDES_H_
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class MediaCodecInfo;
+
+bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2);
+
+bool splitString(
+        const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3);
+
+void profileCodecs(
+        const Vector<sp<MediaCodecInfo>> &infos,
+        KeyedVector<AString, CodecSettings> *results,
+        bool forceToMeasure = false);  // forceToMeasure is mainly for testing
+
+void applyCodecSettings(
+        const AString& codecInfo,
+        const CodecSettings &settings,
+        Vector<sp<MediaCodecInfo>> *infos);
+
+void exportResultsToXML(const char *fileName, const KeyedVector<AString, CodecSettings>& results);
+
+}  // namespace android
+
+#endif  // MEDIA_CODEC_LIST_OVERRIDES_H_
diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk
index 8d6ff5b..51e1c78 100644
--- a/media/libstagefright/tests/Android.mk
+++ b/media/libstagefright/tests/Android.mk
@@ -62,6 +62,33 @@
 
 include $(BUILD_NATIVE_TEST)
 
+include $(CLEAR_VARS)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_MODULE := MediaCodecListOverrides_test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+	MediaCodecListOverrides_test.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	libmedia \
+	libstagefright \
+	libstagefright_foundation \
+	libstagefright_omx \
+	libutils \
+	liblog
+
+LOCAL_C_INCLUDES := \
+	frameworks/av/media/libstagefright \
+	frameworks/av/media/libstagefright/include \
+	frameworks/native/include/media/openmax \
+
+LOCAL_32_BIT_ONLY := true
+
+include $(BUILD_NATIVE_TEST)
+
 # Include subdirectory makefiles
 # ============================================================
 
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
new file mode 100644
index 0000000..cacaa84
--- /dev/null
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2015 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.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecListOverrides_test"
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include "MediaCodecListOverrides.h"
+
+#include <media/MediaCodecInfo.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodecList.h>
+
+namespace android {
+
+static const char kTestOverridesStr[] =
+"<MediaCodecs>\n"
+"    <Settings>\n"
+"        <Setting name=\"max-max-supported-instances\" value=\"8\" update=\"true\" />\n"
+"    </Settings>\n"
+"    <Encoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+"            <Limit name=\"bitrate\" range=\"1-20000000\" />\n"
+"            <Feature name=\"can-swap-width-height\" />\n"
+"        </MediaCodec>\n"
+"    </Encoders>\n"
+"    <Decoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+"            <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+"            <Limit name=\"size\" min=\"64x64\" max=\"1920x1088\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"different_mime\" update=\"true\" >\n"
+"        </MediaCodec>\n"
+"    </Decoders>\n"
+"</MediaCodecs>\n";
+
+static const char kTestOverridesStrNew1[] =
+"<MediaCodecs>\n"
+"    <Settings>\n"
+"        <Setting name=\"max-max-supported-instances\" value=\"8\" update=\"true\" />\n"
+"    </Settings>\n"
+"    <Encoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"            <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+"            <Limit name=\"bitrate\" range=\"1-20000000\" />\n"
+"            <Feature name=\"can-swap-width-height\" />\n"
+"        </MediaCodec>\n"
+"    </Encoders>\n"
+"    <Decoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"1\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+"            <Quirk name=\"requires-allocate-on-input-ports\" />\n"
+"            <Limit name=\"size\" min=\"64x64\" max=\"1920x1088\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"different_mime\" update=\"true\" >\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+"        </MediaCodec>\n"
+"    </Decoders>\n"
+"</MediaCodecs>\n";
+
+static const char kTestOverridesStrNew2[] =
+"\n"
+"<MediaCodecs>\n"
+"    <Encoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.encoder.avc\" type=\"video/avc\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"    </Encoders>\n"
+"    <Decoders>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg4\" type=\"video/mp4v-es\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.mpeg2\" type=\"video/mpeg2\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"3\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.h263\" type=\"video/3gpp\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"4\" />\n"
+"        </MediaCodec>\n"
+"        <MediaCodec name=\"OMX.qcom.video.decoder.avc.secure\" type=\"video/avc\" update=\"true\" >\n"
+"            <Limit name=\"max-supported-instances\" value=\"1\" />\n"
+"        </MediaCodec>\n"
+"    </Decoders>\n"
+"</MediaCodecs>\n";
+
+class MediaCodecListOverridesTest : public ::testing::Test {
+public:
+    MediaCodecListOverridesTest() {}
+
+    void verifyOverrides(const KeyedVector<AString, CodecSettings> &overrides) {
+        EXPECT_EQ(3u, overrides.size());
+
+        EXPECT_TRUE(overrides.keyAt(0) == "OMX.qcom.video.decoder.avc video/avc decoder");
+        const CodecSettings &settings0 = overrides.valueAt(0);
+        EXPECT_EQ(1u, settings0.size());
+        EXPECT_TRUE(settings0.keyAt(0) == "max-supported-instances");
+        EXPECT_TRUE(settings0.valueAt(0) == "4");
+
+        EXPECT_TRUE(overrides.keyAt(1) == "OMX.qcom.video.encoder.avc video/avc encoder");
+        const CodecSettings &settings1 = overrides.valueAt(1);
+        EXPECT_EQ(1u, settings1.size());
+        EXPECT_TRUE(settings1.keyAt(0) == "max-supported-instances");
+        EXPECT_TRUE(settings1.valueAt(0) == "3");
+
+        EXPECT_TRUE(overrides.keyAt(2) == "global");
+        const CodecSettings &settings2 = overrides.valueAt(2);
+        EXPECT_EQ(3u, settings2.size());
+        EXPECT_TRUE(settings2.keyAt(0) == "max-max-supported-instances");
+        EXPECT_TRUE(settings2.valueAt(0) == "8");
+        EXPECT_TRUE(settings2.keyAt(1) == "supports-multiple-secure-codecs");
+        EXPECT_TRUE(settings2.valueAt(1) == "false");
+        EXPECT_TRUE(settings2.keyAt(2) == "supports-secure-with-non-secure-codec");
+        EXPECT_TRUE(settings2.valueAt(2) == "true");
+    }
+
+    void verifySetting(const sp<AMessage> &details, const char *name, const char *value) {
+        AString value1;
+        EXPECT_TRUE(details->findString(name, &value1));
+        EXPECT_TRUE(value1 == value);
+    }
+
+    void createTestInfos(Vector<sp<MediaCodecInfo>> *infos) {
+        const char *name = "OMX.qcom.video.decoder.avc";
+        const bool encoder = false;
+        const char *mime = "video/avc";
+        sp<MediaCodecInfo> info = new MediaCodecInfo(name, encoder, mime);
+        infos->push_back(info);
+        const sp<MediaCodecInfo::Capabilities> caps = info->getCapabilitiesFor(mime);
+        const sp<AMessage> details = caps->getDetails();
+        details->setString("cap1", "value1");
+        details->setString("max-max-supported-instances", "16");
+
+        info = new MediaCodecInfo("anothercodec", true, "anothermime");
+        infos->push_back(info);
+    }
+
+    void addMaxInstancesSetting(
+            const AString &key,
+            const AString &value,
+            KeyedVector<AString, CodecSettings> *results) {
+        CodecSettings settings;
+        settings.add("max-supported-instances", value);
+        results->add(key, settings);
+    }
+
+    void exportTestResultsToXML(const char *fileName) {
+        KeyedVector<AString, CodecSettings> r;
+        addMaxInstancesSetting("OMX.qcom.video.decoder.avc.secure video/avc decoder", "1", &r);
+        addMaxInstancesSetting("OMX.qcom.video.decoder.h263 video/3gpp decoder", "4", &r);
+        addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg2 video/mpeg2 decoder", "3", &r);
+        addMaxInstancesSetting("OMX.qcom.video.decoder.mpeg4 video/mp4v-es decoder", "3", &r);
+        addMaxInstancesSetting("OMX.qcom.video.encoder.avc video/avc encoder", "4", &r);
+        addMaxInstancesSetting("OMX.qcom.video.encoder.mpeg4 video/mp4v-es encoder", "4", &r);
+
+        exportResultsToXML(fileName, r);
+    }
+};
+
+TEST_F(MediaCodecListOverridesTest, splitString) {
+    AString s = "abc123";
+    AString delimiter = " ";
+    AString s1;
+    AString s2;
+    EXPECT_FALSE(splitString(s, delimiter, &s1, &s2));
+    s = "abc 123";
+    EXPECT_TRUE(splitString(s, delimiter, &s1, &s2));
+    EXPECT_TRUE(s1 == "abc");
+    EXPECT_TRUE(s2 == "123");
+
+    s = "abc123xyz";
+    delimiter = ",";
+    AString s3;
+    EXPECT_FALSE(splitString(s, delimiter, &s1, &s2, &s3));
+    s = "abc,123xyz";
+    EXPECT_FALSE(splitString(s, delimiter, &s1, &s2, &s3));
+    s = "abc,123,xyz";
+    EXPECT_TRUE(splitString(s, delimiter, &s1, &s2, &s3));
+    EXPECT_TRUE(s1 == "abc");
+    EXPECT_TRUE(s2 == "123" );
+    EXPECT_TRUE(s3 == "xyz");
+}
+
+// TODO: the codec component never returns OMX_EventCmdComplete in unit test.
+TEST_F(MediaCodecListOverridesTest, DISABLED_profileCodecs) {
+    sp<IMediaCodecList> list = MediaCodecList::getInstance();
+    Vector<sp<MediaCodecInfo>> infos;
+    for (size_t i = 0; i < list->countCodecs(); ++i) {
+        infos.push_back(list->getCodecInfo(i));
+    }
+    KeyedVector<AString, CodecSettings> results;
+    profileCodecs(infos, &results, true /* forceToMeasure */);
+    EXPECT_LT(0u, results.size());
+    for (size_t i = 0; i < results.size(); ++i) {
+        AString key = results.keyAt(i);
+        CodecSettings settings = results.valueAt(i);
+        EXPECT_EQ(1u, settings.size());
+        EXPECT_TRUE(settings.keyAt(0) == "max-supported-instances");
+        AString valueS = settings.valueAt(0);
+        int32_t value = strtol(valueS.c_str(), NULL, 10);
+        EXPECT_LT(0, value);
+        ALOGV("profileCodecs results %s %s", key.c_str(), valueS.c_str());
+    }
+}
+
+TEST_F(MediaCodecListOverridesTest, applyCodecSettings) {
+    AString codecInfo = "OMX.qcom.video.decoder.avc video/avc decoder";
+    Vector<sp<MediaCodecInfo>> infos;
+    createTestInfos(&infos);
+    CodecSettings settings;
+    settings.add("max-supported-instances", "3");
+    settings.add("max-max-supported-instances", "8");
+    applyCodecSettings(codecInfo, settings, &infos);
+
+    EXPECT_EQ(2u, infos.size());
+    EXPECT_TRUE(AString(infos[0]->getCodecName()) == "OMX.qcom.video.decoder.avc");
+    const sp<AMessage> details = infos[0]->getCapabilitiesFor("video/avc")->getDetails();
+    verifySetting(details, "max-supported-instances", "3");
+    verifySetting(details, "max-max-supported-instances", "8");
+
+    EXPECT_TRUE(AString(infos[1]->getCodecName()) == "anothercodec");
+    EXPECT_EQ(0u, infos[1]->getCapabilitiesFor("anothermime")->getDetails()->countEntries());
+}
+
+TEST_F(MediaCodecListOverridesTest, exportResultsToExistingFile) {
+    const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml";
+    remove(fileName);
+
+    FILE *f = fopen(fileName, "wb");
+    if (f == NULL) {
+        ALOGW("Failed to open %s for writing.", fileName);
+        return;
+    }
+    EXPECT_EQ(
+            strlen(kTestOverridesStr),
+            fwrite(kTestOverridesStr, 1, strlen(kTestOverridesStr), f));
+    fclose(f);
+
+    exportTestResultsToXML(fileName);
+
+    // verify
+    AString overrides;
+    f = fopen(fileName, "rb");
+    ASSERT_TRUE(f != NULL);
+    fseek(f, 0, SEEK_END);
+    long size = ftell(f);
+    rewind(f);
+
+    char *buf = (char *)malloc(size);
+    EXPECT_EQ(1, fread(buf, size, 1, f));
+    overrides.setTo(buf, size);
+    fclose(f);
+    free(buf);
+
+    EXPECT_TRUE(overrides == kTestOverridesStrNew1);
+
+    remove(fileName);
+}
+
+TEST_F(MediaCodecListOverridesTest, exportResultsToEmptyFile) {
+    const char *fileName = "/sdcard/mediacodec_list_overrides_test.xml";
+    remove(fileName);
+
+    exportTestResultsToXML(fileName);
+
+    // verify
+    AString overrides;
+    FILE *f = fopen(fileName, "rb");
+    ASSERT_TRUE(f != NULL);
+    fseek(f, 0, SEEK_END);
+    long size = ftell(f);
+    rewind(f);
+
+    char *buf = (char *)malloc(size);
+    EXPECT_EQ(1, fread(buf, size, 1, f));
+    overrides.setTo(buf, size);
+    fclose(f);
+    free(buf);
+
+    EXPECT_TRUE(overrides == kTestOverridesStrNew2);
+
+    remove(fileName);
+}
+
+} // namespace android