Merge "Fix failure at AudioTrack_ListenerTest#testAudioTrackCallback" into oc-mr1-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f3946f0..793cbf4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -79,6 +79,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libaudiopreprocessing.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libmediacodecservice.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_xmlparser@1.0.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_soft_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk/libstagefright_soft_*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index a9302ea..57ff5b8 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -44,6 +44,13 @@
         analyticsItem.setInt64(kParentAttribute, *parentId);
     }
 
+    // Report the package name.
+    if (metricsGroup.has_app_package_name()) {
+      AString app_package_name(metricsGroup.app_package_name().c_str(),
+                               metricsGroup.app_package_name().size());
+      analyticsItem.setPkgName(app_package_name);
+    }
+
     for (int i = 0; i < metricsGroup.metric_size(); ++i) {
         const MetricsGroup_Metric& metric = metricsGroup.metric(i);
         if (!metric.has_name()) {
@@ -73,7 +80,12 @@
     }
 
     analyticsItem.setFinalized(true);
-    analyticsItem.selfrecord();
+    if (!analyticsItem.selfrecord()) {
+      // Note the cast to int is because we build on 32 and 64 bit.
+      // The cast prevents a peculiar printf problem where one format cannot
+      // satisfy both.
+      ALOGE("selfrecord() returned false. sessioId %d", (int) sessionId);
+    }
 
     for (int i = 0; i < metricsGroup.metric_sub_group_size(); ++i) {
         const MetricsGroup& subGroup = metricsGroup.metric_sub_group(i);
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
index 2d26f14..7e3bcf5 100644
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ b/drm/libmediadrm/protos/plugin_metrics.proto
@@ -44,4 +44,7 @@
 
   // Allow multiple sub groups of metrics.
   repeated MetricsGroup metric_sub_group = 2;
+
+  // Name of the application package associated with the metrics.
+  optional string app_package_name = 3;
 }
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
index 5fdac5c..ec07d87 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -25,10 +25,28 @@
 
 #include "Session.h"
 
+namespace {
+const android::String8 kStreaming("Streaming");
+const android::String8 kOffline("Offline");
+const android::String8 kTrue("True");
+
+const android::String8 kQueryKeyLicenseType("LicenseType");
+    // Value: "Streaming" or "Offline"
+const android::String8 kQueryKeyPlayAllowed("PlayAllowed");
+    // Value: "True" or "False"
+const android::String8 kQueryKeyRenewAllowed("RenewAllowed");
+    // Value: "True" or "False"
+};
+
 namespace clearkeydrm {
 
 using android::sp;
 
+DrmPlugin::DrmPlugin(SessionLibrary* sessionLibrary)
+        : mSessionLibrary(sessionLibrary) {
+    mPlayPolicy.clear();
+}
+
 status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
     sp<Session> session = mSessionLibrary->createSession();
     sessionId = session->sessionId();
@@ -60,18 +78,28 @@
     if (scope.size() == 0) {
         return android::BAD_VALUE;
     }
+
     if (keyType != kKeyType_Streaming) {
         return android::ERROR_DRM_CANNOT_HANDLE;
     }
+
     *keyRequestType = DrmPlugin::kKeyRequestType_Initial;
     defaultUrl.clear();
     sp<Session> session = mSessionLibrary->findSession(scope);
     if (!session.get()) {
         return android::ERROR_DRM_SESSION_NOT_OPENED;
     }
+
     return session->getKeyRequest(initData, mimeType, &request);
 }
 
+void DrmPlugin::setPlayPolicy() {
+    mPlayPolicy.clear();
+    mPlayPolicy.add(kQueryKeyLicenseType, kStreaming);
+    mPlayPolicy.add(kQueryKeyPlayAllowed, kTrue);
+    mPlayPolicy.add(kQueryKeyRenewAllowed, kTrue);
+}
+
 status_t DrmPlugin::provideKeyResponse(
         const Vector<uint8_t>& scope,
         const Vector<uint8_t>& response,
@@ -83,6 +111,8 @@
     if (!session.get()) {
         return android::ERROR_DRM_SESSION_NOT_OPENED;
     }
+
+    setPlayPolicy();
     status_t res = session->provideKeyResponse(response);
     if (res == android::OK) {
         // This is for testing AMediaDrm_setOnEventListener only.
@@ -111,4 +141,18 @@
     return android::OK;
 }
 
+status_t DrmPlugin::queryKeyStatus(
+        const Vector<uint8_t>& sessionId,
+        KeyedVector<String8, String8>& infoMap) const {
+
+    if (sessionId.size() == 0) {
+        return android::BAD_VALUE;
+    }
+
+    infoMap.clear();
+    for (size_t i = 0; i < mPlayPolicy.size(); ++i) {
+        infoMap.add(mPlayPolicy.keyAt(i), mPlayPolicy.valueAt(i));
+    }
+    return android::OK;
+}
 }  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
index 58421b9..f37a706 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.h
@@ -39,8 +39,8 @@
 
 class DrmPlugin : public android::DrmPlugin {
 public:
-    explicit DrmPlugin(SessionLibrary* sessionLibrary)
-            : mSessionLibrary(sessionLibrary) {}
+    explicit DrmPlugin(SessionLibrary* sessionLibrary);
+
     virtual ~DrmPlugin() {}
 
     virtual status_t openSession(Vector<uint8_t>& sessionId);
@@ -81,13 +81,7 @@
 
     virtual status_t queryKeyStatus(
             const Vector<uint8_t>& sessionId,
-            KeyedVector<String8, String8>& infoMap) const {
-        if (sessionId.size() == 0) {
-            return android::BAD_VALUE;
-        }
-        UNUSED(infoMap);
-        return android::ERROR_DRM_CANNOT_HANDLE;
-    }
+            KeyedVector<String8, String8>& infoMap) const;
 
     virtual status_t getProvisionRequest(
             const String8& cert_type,
@@ -248,9 +242,12 @@
     }
 
 private:
-    DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
+    void setPlayPolicy();
 
+    android::KeyedVector<android::String8, android::String8> mPlayPolicy;
     SessionLibrary* mSessionLibrary;
+
+    DISALLOW_EVIL_CONSTRUCTORS(DrmPlugin);
 };
 
 } // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
index 6a4f8d5..caff393 100644
--- a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -136,7 +136,7 @@
     AString encodedId;
     for (size_t i = 0; i < keyIds.size(); ++i) {
         encodedId.clear();
-        android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId);
+        android::encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId);
         if (i != 0) {
             request.append(",");
         }
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
index 84ed242..8c49656 100644
--- a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -59,7 +59,7 @@
                   (size_t)requestString.find(kRequestSuffix));
         for (size_t i = 0; i < expectedKeys.size(); ++i) {
             AString encodedIdAString;
-            android::encodeBase64(expectedKeys[i], kKeyIdSize,
+            android::encodeBase64Url(expectedKeys[i], kKeyIdSize,
                                   &encodedIdAString);
             String8 encodedId(encodedIdAString.c_str());
             encodedId.removeAll(kBase64Padding);
@@ -231,5 +231,4 @@
 
     attemptParseExpectingFailure(initData, kCencMimeType);
 }
-
 }  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
index c3b0d84..d9f3ea6 100644
--- a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
@@ -284,14 +284,14 @@
                 "\"keys\":"
                     "[{"
                         "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
-                        "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kICE-Pw\""
                         "\"kty\":\"oct\""
                         "\"alg\":\"A128KW1\""
                     "}"
                     "{"
                         "\"kty\":\"oct\""
                         "\"alg\":\"A128KW2\""
-                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kICE_\""
                         "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
                     "}"
                     "{"
@@ -303,7 +303,7 @@
                     "{"
                         "\"alg\":\"A128KW3\""
                         "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
-                        "\"k\":\"R29vZCBkYXkh\""
+                        "\"k\":\"SGVsbG8gPz4-IEZyaWVuZCA_Pg\""
                         "\"kty\":\"oct\""
                     "}]"
             "}");
@@ -313,8 +313,8 @@
     EXPECT_TRUE(keys.size() == 3);
 
     const String8 clearKeys[] =
-            { String8("Hello Friend!!"), String8("Hello Friend!"),
-              String8("Good day!") };
+            { String8("Hello Friend !>?"), String8("Hello Friend !?"),
+              String8("Hello ?>> Friend ?>") };
     verifyKeys(keys, clearKeys);
 }
 
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
index 2dfd0a7..9feb118 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
@@ -61,7 +61,7 @@
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Monitor input level using AAudio V0.1.1\n", argv[0]);
+    printf("%s - Monitor input level using AAudio read, V0.1.2\n", argv[0]);
 
     argParser.setFormat(REQUIRED_FORMAT);
     if (argParser.parseArgs(argc, argv)) {
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
index 9de2eb0..893795b 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
@@ -41,7 +41,7 @@
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
-    printf("%s - Display audio input using an AAudio callback\n", argv[0]);
+    printf("%s - Display audio input using an AAudio callback, V0.1.2\n", argv[0]);
 
     result = recorder.open(2, 48000, AAUDIO_FORMAT_PCM_I16,
                        SimpleRecorderDataCallbackProc, SimpleRecorderErrorCallbackProc, &myData);
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 30c3ccd..ada37e2 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -150,6 +150,9 @@
                     setChannelCount(atoi(&arg[2]));
                     break;
                 case 'd':
+                    setDeviceId(atoi(&arg[2]));
+                    break;
+                case 's':
                     mDurationSeconds = atoi(&arg[2]);
                     break;
                 case 'm': {
@@ -202,7 +205,8 @@
         printf("      Default values are UNSPECIFIED unless otherwise stated.\n");
         printf("      -b{bufferCapacity} frames\n");
         printf("      -c{channels} for example 2 for stereo\n");
-        printf("      -d{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
+        printf("      -d{deviceId} default is %d\n", AAUDIO_UNSPECIFIED);
+        printf("      -s{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
         printf("      -m{0|1|2|3} set MMAP policy\n");
         printf("          0 = _UNSPECIFIED, default\n");
         printf("          1 = _NEVER\n");
diff --git a/media/libaaudio/examples/utils/AAudioExampleUtils.h b/media/libaaudio/examples/utils/AAudioExampleUtils.h
index 6cbcc58..c179ce6 100644
--- a/media/libaaudio/examples/utils/AAudioExampleUtils.h
+++ b/media/libaaudio/examples/utils/AAudioExampleUtils.h
@@ -17,27 +17,71 @@
 #ifndef AAUDIO_EXAMPLE_UTILS_H
 #define AAUDIO_EXAMPLE_UTILS_H
 
-#include <unistd.h>
+#include <atomic>
+#include <linux/futex.h>
 #include <sched.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
 #include <aaudio/AAudio.h>
+#include <utils/Errors.h>
 
 #define NANOS_PER_MICROSECOND ((int64_t)1000)
 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
 #define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * 1000)
 
 const char *getSharingModeText(aaudio_sharing_mode_t mode) {
-    const char *modeText = "unknown";
+    const char *text = "unknown";
     switch (mode) {
-    case AAUDIO_SHARING_MODE_EXCLUSIVE:
-        modeText = "EXCLUSIVE";
-        break;
-    case AAUDIO_SHARING_MODE_SHARED:
-        modeText = "SHARED";
-        break;
-    default:
-        break;
+        case AAUDIO_SHARING_MODE_EXCLUSIVE:
+            text = "EXCLUSIVE";
+            break;
+        case AAUDIO_SHARING_MODE_SHARED:
+            text = "SHARED";
+            break;
+        default:
+            break;
     }
-    return modeText;
+    return text;
+}
+
+const char *getPerformanceModeText(aaudio_performance_mode_t mode) {
+    const char *text = "unknown";
+    switch (mode) {
+        case AAUDIO_PERFORMANCE_MODE_NONE:
+            text = "NONE";
+            break;
+        case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+            text = "LOW_LATENCY";
+            break;
+        case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+            text = "POWER_SAVING";
+            break;
+        default:
+            break;
+    }
+    return text;
+}
+
+const char *getDirectionText(aaudio_direction_t direction) {
+    const char *text = "unknown";
+    switch (direction) {
+        case AAUDIO_DIRECTION_INPUT:
+            text = "INPUT";
+            break;
+        case AAUDIO_DIRECTION_OUTPUT:
+            text = "OUTPUT";
+            break;
+        default:
+            break;
+    }
+    return text;
+}
+
+static void convertNanosecondsToTimespec(int64_t nanoseconds, struct timespec *time) {
+    time->tv_sec = nanoseconds / NANOS_PER_SECOND;
+    // Calculate the fractional nanoseconds. Avoids expensive % operation.
+    time->tv_nsec = nanoseconds - (time->tv_sec * NANOS_PER_SECOND);
 }
 
 static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
@@ -79,4 +123,77 @@
     return latencyMillis;
 }
 
+// ================================================================================
+// These Futex calls are common online examples.
+static android::status_t sys_futex(void *addr1, int op, int val1,
+                      struct timespec *timeout, void *addr2, int val3) {
+    android::status_t result = (android::status_t) syscall(SYS_futex, addr1,
+                                                           op, val1, timeout,
+                                                           addr2, val3);
+    return (result == 0) ? 0 : -errno;
+}
+
+static android::status_t futex_wake(void *addr, int numWake) {
+    // Use _PRIVATE because we are just using the futex in one process.
+    return sys_futex(addr, FUTEX_WAKE_PRIVATE, numWake, NULL, NULL, 0);
+}
+
+static android::status_t futex_wait(void *addr, int current, struct timespec *time) {
+    // Use _PRIVATE because we are just using the futex in one process.
+    return sys_futex(addr, FUTEX_WAIT_PRIVATE, current, time, NULL, 0);
+}
+
+// TODO better name?
+/**
+ * The WakeUp class is used to send a wakeup signal to one or more sleeping threads.
+ */
+class WakeUp {
+public:
+    WakeUp() : mValue(0) {}
+    explicit WakeUp(int32_t value) : mValue(value) {}
+
+    /**
+     * Wait until the internal value no longer matches the given value.
+     * Note that this code uses a futex, which is subject to spurious wake-ups.
+     * So check to make sure that the desired condition has been met.
+     *
+     * @return zero if the value changes or various negative errors including
+     *    -ETIMEDOUT if a timeout occurs,
+     *    or -EINTR if interrupted by a signal,
+     *    or -EAGAIN or -EWOULDBLOCK if the internal value does not match the specified value
+     */
+    android::status_t wait(int32_t value, int64_t timeoutNanoseconds) {
+        struct timespec time;
+        convertNanosecondsToTimespec(timeoutNanoseconds, &time);
+        return futex_wait(&mValue, value, &time);
+    }
+
+    /**
+     * Increment value and wake up any threads that need to be woken.
+     *
+     * @return number of waiters woken up
+     */
+    android::status_t wake() {
+        ++mValue;
+        return futex_wake(&mValue, INT_MAX);
+    }
+
+    /**
+     * Set value and wake up any threads that need to be woken.
+     *
+     * @return number of waiters woken up
+     */
+    android::status_t wake(int32_t value) {
+        mValue.store(value);
+        return futex_wake(&mValue, INT_MAX);
+    }
+
+    int32_t get() {
+        return mValue.load();
+    }
+
+private:
+    std::atomic<int32_t>   mValue;
+};
+
 #endif // AAUDIO_EXAMPLE_UTILS_H
diff --git a/media/libaaudio/examples/utils/AAudioSimplePlayer.h b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
index cc0cb34..606c4ba 100644
--- a/media/libaaudio/examples/utils/AAudioSimplePlayer.h
+++ b/media/libaaudio/examples/utils/AAudioSimplePlayer.h
@@ -23,6 +23,7 @@
 #include <sched.h>
 
 #include <aaudio/AAudio.h>
+#include <atomic>
 #include "AAudioArgsParser.h"
 #include "SineGenerator.h"
 
@@ -35,6 +36,13 @@
 // How long to sleep in a callback to cause an intentional glitch. For testing.
 #define FORCED_UNDERRUN_SLEEP_MICROS     (10 * 1000)
 
+#define MAX_TIMESTAMPS   16
+
+typedef struct Timestamp {
+    int64_t position;
+    int64_t nanoseconds;
+} Timestamp;
+
 /**
  * Simple wrapper for AAudio that opens an output stream either in callback or blocking write mode.
  */
@@ -219,18 +227,28 @@
     AAudioStream             *mStream = nullptr;
     aaudio_sharing_mode_t     mRequestedSharingMode = SHARING_MODE;
     aaudio_performance_mode_t mRequestedPerformanceMode = PERFORMANCE_MODE;
+
 };
 
 typedef struct SineThreadedData_s {
+
     SineGenerator  sineOsc1;
     SineGenerator  sineOsc2;
+    Timestamp      timestamps[MAX_TIMESTAMPS];
     int64_t        framesTotal = 0;
     int64_t        nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
     int32_t        minNumFrames = INT32_MAX;
     int32_t        maxNumFrames = 0;
-    int            scheduler;
+    int32_t        timestampCount = 0; // in timestamps
+
+    int            scheduler = 0;
     bool           schedulerChecked = false;
     bool           forceUnderruns = false;
+
+    AAudioSimplePlayer simplePlayer;
+    int32_t            callbackCount = 0;
+    WakeUp             waker{AAUDIO_OK};
+
 } SineThreadedData_t;
 
 // Callback function that fills the audio output buffer.
@@ -247,6 +265,7 @@
         return AAUDIO_CALLBACK_RESULT_STOP;
     }
     SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
+    sineData->callbackCount++;
 
     sineData->framesTotal += numFrames;
 
@@ -263,6 +282,17 @@
         sineData->schedulerChecked = true;
     }
 
+    if (sineData->timestampCount < MAX_TIMESTAMPS) {
+        Timestamp *timestamp = &sineData->timestamps[sineData->timestampCount];
+        aaudio_result_t result = AAudioStream_getTimestamp(stream,
+            CLOCK_MONOTONIC, &timestamp->position, &timestamp->nanoseconds);
+        if (result == AAUDIO_OK && // valid?
+                (sineData->timestampCount == 0 || // first one?
+                (timestamp->position != (timestamp - 1)->position))) { // advanced position?
+            sineData->timestampCount++; // keep this one
+        }
+    }
+
     if (numFrames > sineData->maxNumFrames) {
         sineData->maxNumFrames = numFrames;
     }
@@ -304,9 +334,16 @@
 void SimplePlayerErrorCallbackProc(
         AAudioStream *stream __unused,
         void *userData __unused,
-        aaudio_result_t error)
-{
-    printf("Error Callback, error: %d\n",(int)error);
+        aaudio_result_t error) {
+    // should not happen but just in case...
+    if (userData == nullptr) {
+        printf("ERROR - MyPlayerErrorCallbackProc needs userData\n");
+        return;
+    }
+    SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
+    android::status_t ret = sineData->waker.wake(error);
+    printf("Error Callback, error: %d, futex wake returns %d\n", error, ret);
 }
 
+
 #endif //AAUDIO_SIMPLE_PLAYER_H
diff --git a/media/libaaudio/examples/utils/SineGenerator.h b/media/libaaudio/examples/utils/SineGenerator.h
index 64b772d..a755582 100644
--- a/media/libaaudio/examples/utils/SineGenerator.h
+++ b/media/libaaudio/examples/utils/SineGenerator.h
@@ -58,6 +58,13 @@
         }
     }
 
+    void setAmplitude(double amplitude) {
+        mAmplitude = amplitude;
+    }
+    double getAmplitude() const {
+        return mAmplitude;
+    }
+
 private:
     void advancePhase() {
         mPhase += mPhaseIncrement;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 87fb40b..677fb6c 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -57,7 +57,7 @@
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Play a sine wave using AAudio V0.1.1\n", argv[0]);
+    printf("%s - Play a sine wave using AAudio V0.1.2\n", argv[0]);
 
     if (argParser.parseArgs(argc, argv)) {
         return EXIT_FAILURE;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index b5602e9..4f9cde6 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -15,6 +15,7 @@
  */
 
 // Play sine waves using an AAudio callback.
+// If a disconnection occurs then reopen the stream on the new device.
 
 #include <assert.h>
 #include <unistd.h>
@@ -22,33 +23,32 @@
 #include <sched.h>
 #include <stdio.h>
 #include <math.h>
+#include <string.h>
 #include <time.h>
 #include <aaudio/AAudio.h>
 #include "AAudioExampleUtils.h"
 #include "AAudioSimplePlayer.h"
 #include "../../utils/AAudioSimplePlayer.h"
 
-int main(int argc, const char **argv)
+/**
+ * Open stream, play some sine waves, then close the stream.
+ *
+ * @param argParser
+ * @return AAUDIO_OK or negative error code
+ */
+static aaudio_result_t testOpenPlayClose(AAudioArgsParser &argParser)
 {
-    AAudioArgsParser   argParser;
-    AAudioSimplePlayer player;
     SineThreadedData_t myData;
-    aaudio_result_t result;
-    int32_t actualSampleRate;
+    AAudioSimplePlayer &player = myData.simplePlayer;
+    aaudio_result_t    result = AAUDIO_OK;
+    bool               disconnected = false;
+    int64_t            startedAtNanos;
 
-    // Make printf print immediately so that debug info is not stuck
-    // in a buffer if we hang or crash.
-    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
-
-    printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]);
-
+    printf("----------------------- run complete test --------------------------\n");
     myData.schedulerChecked = false;
+    myData.callbackCount = 0;
     myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
 
-    if (argParser.parseArgs(argc, argv)) {
-        return EXIT_FAILURE;
-    }
-
     result = player.open(argParser,
                          SimplePlayerDataCallbackProc, SimplePlayerErrorCallbackProc, &myData);
     if (result != AAUDIO_OK) {
@@ -58,13 +58,19 @@
 
     argParser.compareWithStream(player.getStream());
 
-    actualSampleRate = player.getSampleRate();
-    myData.sineOsc1.setup(440.0, actualSampleRate);
-    myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
-    myData.sineOsc2.setup(660.0, actualSampleRate);
-    myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
+    // Setup sine wave generators.
+    {
+        int32_t actualSampleRate = player.getSampleRate();
+        myData.sineOsc1.setup(440.0, actualSampleRate);
+        myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
+        myData.sineOsc1.setAmplitude(0.2);
+        myData.sineOsc2.setup(660.0, actualSampleRate);
+        myData.sineOsc2.setSweep(350.0, 900.0, 7.0);
+        myData.sineOsc2.setAmplitude(0.2);
+    }
 
 #if 0
+    //  writes not allowed for callback streams
     result = player.prime(); // FIXME crashes AudioTrack.cpp
     if (result != AAUDIO_OK) {
         fprintf(stderr, "ERROR - player.prime() returned %d\n", result);
@@ -78,34 +84,32 @@
         goto error;
     }
 
+    // Play a sine wave in the background.
     printf("Sleep for %d seconds while audio plays in a callback thread.\n",
            argParser.getDurationSeconds());
+    startedAtNanos = getNanoseconds(CLOCK_MONOTONIC);
     for (int second = 0; second < argParser.getDurationSeconds(); second++)
     {
-        const struct timespec request = { .tv_sec = 1, .tv_nsec = 0 };
-        (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
-
-        aaudio_stream_state_t state;
-        result = AAudioStream_waitForStateChange(player.getStream(),
-                                                 AAUDIO_STREAM_STATE_CLOSED,
-                                                 &state,
-                                                 0);
+        // Sleep a while. Wake up early if there is an error, for example a DISCONNECT.
+        long ret = myData.waker.wait(AAUDIO_OK, NANOS_PER_SECOND);
+        int64_t millis = (getNanoseconds(CLOCK_MONOTONIC) - startedAtNanos) / NANOS_PER_MILLISECOND;
+        result = myData.waker.get();
+        printf("wait() returns %ld, aaudio_result = %d, at %6d millis"
+               ", second = %d, framesWritten = %8d, underruns = %d\n",
+               ret, result, (int) millis,
+               second,
+               (int) AAudioStream_getFramesWritten(player.getStream()),
+               (int) AAudioStream_getXRunCount(player.getStream()));
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_waitForStateChange() returned %d\n", result);
-            goto error;
-        }
-        if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
-            printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
+            if (result == AAUDIO_ERROR_DISCONNECTED) {
+                disconnected = true;
+            }
             break;
         }
-        printf("framesWritten = %d, underruns = %d\n",
-               (int) AAudioStream_getFramesWritten(player.getStream()),
-               (int) AAudioStream_getXRunCount(player.getStream())
-        );
     }
-    printf("Woke up now.\n");
+    printf("AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
 
-    printf("call stop()\n");
+    printf("call stop() callback # = %d\n", myData.callbackCount);
     result = player.stop();
     if (result != AAUDIO_OK) {
         goto error;
@@ -116,6 +120,18 @@
         goto error;
     }
 
+    for (int i = 0; i < myData.timestampCount; i++) {
+        Timestamp *timestamp = &myData.timestamps[i];
+        bool retro = (i > 0 &&
+                      ((timestamp->position < (timestamp - 1)->position)
+                       || ((timestamp->nanoseconds < (timestamp - 1)->nanoseconds))));
+        const char *message = retro ? "  <= RETROGRADE!" : "";
+        printf("Timestamp %3d : %8lld, %8lld %s\n", i,
+               (long long) timestamp->position,
+               (long long) timestamp->nanoseconds,
+               message);
+    }
+
     if (myData.schedulerChecked) {
         printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
                myData.scheduler,
@@ -126,10 +142,28 @@
     printf("max numFrames = %8d\n", (int) myData.maxNumFrames);
 
     printf("SUCCESS\n");
-    return EXIT_SUCCESS;
 error:
     player.close();
-    printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
-    return EXIT_FAILURE;
+    return disconnected ? AAUDIO_ERROR_DISCONNECTED : result;
 }
 
+int main(int argc, const char **argv)
+{
+    AAudioArgsParser   argParser;
+    aaudio_result_t    result;
+
+    // Make printf print immediately so that debug info is not stuck
+    // in a buffer if we hang or crash.
+    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+    printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]);
+
+    if (argParser.parseArgs(argc, argv)) {
+        return EXIT_FAILURE;
+    }
+
+    // Keep looping until we can complete the test without disconnecting.
+    while((result = testOpenPlayClose(argParser)) == AAUDIO_ERROR_DISCONNECTED);
+
+    return (result) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index a268e49..07ee2de 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -45,6 +45,7 @@
 using android::IInterface;
 using android::IAAudioService;
 using android::Mutex;
+using android::ProcessState;
 using android::sp;
 using android::wp;
 
@@ -52,15 +53,19 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(AAudioBinderClient);
 
+// If we don't keep a strong pointer here then this singleton can get deleted!
+android::sp<AAudioBinderClient> gKeepBinderClient;
+
 AAudioBinderClient::AAudioBinderClient()
         : AAudioServiceInterface()
         , Singleton<AAudioBinderClient>() {
-
+    gKeepBinderClient = this; // so this singleton won't get deleted
     mAAudioClient = new AAudioClient(this);
-    ALOGV("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+    ALOGV("AAudioBinderClient() this = %p, created mAAudioClient = %p", this, mAAudioClient.get());
 }
 
 AAudioBinderClient::~AAudioBinderClient() {
+    ALOGV("AAudioBinderClient()::~AAudioBinderClient() destroying %p", this);
     Mutex::Autolock _l(mServiceLock);
     if (mAAudioService != 0) {
         IInterface::asBinder(mAAudioService)->unlinkToDeath(mAAudioClient);
@@ -75,19 +80,19 @@
     bool needToRegister = false;
     {
         Mutex::Autolock _l(mServiceLock);
-        if (mAAudioService == 0) {
+        if (mAAudioService.get() == nullptr) {
             sp<IBinder> binder;
             sp<IServiceManager> sm = defaultServiceManager();
             // Try several times to get the service.
             int retries = 4;
             do {
                 binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
-                if (binder != 0) {
+                if (binder.get() != nullptr) {
                     break;
                 }
             } while (retries-- > 0);
 
-            if (binder != 0) {
+            if (binder.get() != nullptr) {
                 // Ask for notification if the service dies.
                 status_t status = binder->linkToDeath(mAAudioClient);
                 // TODO review what we should do if this fails
@@ -98,7 +103,7 @@
                 mAAudioService = interface_cast<IAAudioService>(binder);
                 needToRegister = true;
                 // Make sure callbacks can be received by mAAudioClient
-                android::ProcessState::self()->startThreadPool();
+                ProcessState::self()->startThreadPool();
             } else {
                 ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
             }
@@ -106,7 +111,7 @@
         aaudioService = mAAudioService;
     }
     // Do this outside the mutex lock.
-    if (needToRegister && aaudioService != 0) { // new client?
+    if (needToRegister && aaudioService.get() != nullptr) { // new client?
         aaudioService->registerClient(mAAudioClient);
     }
     return aaudioService;
@@ -117,7 +122,6 @@
     mAAudioService.clear(); // force a reconnect
 }
 
-
 /**
 * @param request info needed to create the stream
 * @param configuration contains information about the created stream
@@ -128,14 +132,12 @@
     aaudio_handle_t stream;
     for (int i = 0; i < 2; i++) {
         const sp<IAAudioService> &service = getAAudioService();
-        if (service == 0) {
-            return AAUDIO_ERROR_NO_SERVICE;
-        }
+        if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
 
         stream = service->openStream(request, configurationOutput);
 
         if (stream == AAUDIO_ERROR_NO_SERVICE) {
-            ALOGE("AAudioBinderClient: lost connection to AAudioService.");
+            ALOGE("AAudioBinderClient::openStream lost connection to AAudioService.");
             dropAAudioService(); // force a reconnect
         } else {
             break;
@@ -145,8 +147,8 @@
 }
 
 aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->closeStream(streamHandle);
 }
 
@@ -155,32 +157,32 @@
 */
 aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
                                                          AudioEndpointParcelable &parcelable) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->getStreamDescription(streamHandle, parcelable);
 }
 
 aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->startStream(streamHandle);
 }
 
 aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->pauseStream(streamHandle);
 }
 
 aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->stopStream(streamHandle);
 }
 
 aaudio_result_t AAudioBinderClient::flushStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->flushStream(streamHandle);
 }
 
@@ -190,8 +192,8 @@
 aaudio_result_t AAudioBinderClient::registerAudioThread(aaudio_handle_t streamHandle,
                                                         pid_t clientThreadId,
                                                         int64_t periodNanoseconds) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->registerAudioThread(streamHandle,
                                         clientThreadId,
                                         periodNanoseconds);
@@ -199,8 +201,8 @@
 
 aaudio_result_t AAudioBinderClient::unregisterAudioThread(aaudio_handle_t streamHandle,
                                                           pid_t clientThreadId) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->unregisterAudioThread(streamHandle,
                                           clientThreadId);
 }
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 89ae85c..f9da8b4 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -118,13 +118,13 @@
     {
     public:
         AAudioClient(android::wp<AAudioBinderClient> aaudioBinderClient)
-            : mBinderClient(aaudioBinderClient) {
+                : mBinderClient(aaudioBinderClient) {
         }
 
         // implement DeathRecipient
         virtual void binderDied(const android::wp<android::IBinder>& who __unused) {
             android::sp<AAudioBinderClient> client = mBinderClient.promote();
-            if (client != 0) {
+            if (client.get() != nullptr) {
                 client->dropAAudioService();
             }
             ALOGW("AAudio service binderDied()!");
@@ -133,7 +133,7 @@
         // implement BnAAudioClient
         void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
             android::sp<AAudioBinderClient> client = mBinderClient.promote();
-            if (client != 0) {
+            if (client.get() != nullptr) {
                 client->onStreamChange(handle, opcode, value);
             }
         }
@@ -141,10 +141,11 @@
         android::wp<AAudioBinderClient> mBinderClient;
     };
 
+private:
 
-    android::Mutex               mServiceLock;
+    android::Mutex                  mServiceLock;
     android::sp<android::IAAudioService>  mAAudioService;
-    android::sp<AAudioClient>    mAAudioClient;
+    android::sp<AAudioClient>       mAAudioClient;
 
 };
 
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 036d931..bbbd439 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -55,7 +55,7 @@
 // Wait at least this many times longer than the operation should take.
 #define MIN_TIMEOUT_OPERATIONS    4
 
-#define LOG_TIMESTAMPS   0
+#define LOG_TIMESTAMPS            0
 
 AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface  &serviceInterface, bool inService)
         : AudioStream()
@@ -66,9 +66,9 @@
         , mStreamVolume(1.0f)
         , mInService(inService)
         , mServiceInterface(serviceInterface)
+        , mAtomicTimestamp()
         , mWakeupDelayNanos(AAudioProperty_getWakeupDelayMicros() * AAUDIO_NANOS_PER_MICROSECOND)
         , mMinimumSleepNanos(AAudioProperty_getMinimumSleepMicros() * AAUDIO_NANOS_PER_MICROSECOND)
-        , mAtomicTimestamp()
         {
     ALOGD("AudioStreamInternal(): mWakeupDelayNanos = %d, mMinimumSleepNanos = %d",
           mWakeupDelayNanos, mMinimumSleepNanos);
@@ -250,25 +250,45 @@
     }
 }
 
+/*
+ * It normally takes about 20-30 msec to start a stream on the server.
+ * But the first time can take as much as 200-300 msec. The HW
+ * starts right away so by the time the client gets a chance to write into
+ * the buffer, it is already in a deep underflow state. That can cause the
+ * XRunCount to be non-zero, which could lead an app to tune its latency higher.
+ * To avoid this problem, we set a request for the processing code to start the
+ * client stream at the same position as the server stream.
+ * The processing code will then save the current offset
+ * between client and server and apply that to any position given to the app.
+ */
 aaudio_result_t AudioStreamInternal::requestStart()
 {
     int64_t startTime;
-    ALOGD("AudioStreamInternal()::requestStart()");
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::requestStart() mServiceStreamHandle invalid");
+        ALOGE("requestStart() mServiceStreamHandle invalid");
         return AAUDIO_ERROR_INVALID_STATE;
     }
     if (isActive()) {
-        ALOGE("AudioStreamInternal::requestStart() already active");
+        ALOGE("requestStart() already active");
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    aaudio_stream_state_t originalState = getState();
 
+    aaudio_stream_state_t originalState = getState();
+    if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
+        ALOGE("requestStart() but DISCONNECTED");
+        return AAUDIO_ERROR_DISCONNECTED;
+    }
     setState(AAUDIO_STREAM_STATE_STARTING);
-    aaudio_result_t result = AAudioConvert_androidToAAudioResult(startWithStatus());
+
+    // Clear any stale timestamps from the previous run.
+    drainTimestampsFromService();
+
+    status_t status = startWithStatus(); // Call PlayerBase, which will start the device stream.
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
 
     startTime = AudioClock::getNanoseconds();
     mClockModel.start(startTime);
+    mNeedCatchUp.request();  // Ask data processing code to catch up when first timestamp received.
 
     if (result == AAUDIO_OK && getDataCallbackProc() != nullptr) {
         // Launch the callback loop thread.
@@ -314,13 +334,14 @@
 aaudio_result_t AudioStreamInternal::requestStopInternal()
 {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+        ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
               mServiceStreamHandle);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
     mClockModel.stop(AudioClock::getNanoseconds());
     setState(AAUDIO_STREAM_STATE_STOPPING);
+    mAtomicTimestamp.clear();
     return AAudioConvert_androidToAAudioResult(stopWithStatus());
 }
 
@@ -336,7 +357,7 @@
 
 aaudio_result_t AudioStreamInternal::registerThread() {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::registerThread() mServiceStreamHandle invalid");
+        ALOGE("registerThread() mServiceStreamHandle invalid");
         return AAUDIO_ERROR_INVALID_STATE;
     }
     return mServiceInterface.registerAudioThread(mServiceStreamHandle,
@@ -346,7 +367,7 @@
 
 aaudio_result_t AudioStreamInternal::unregisterThread() {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::unregisterThread() mServiceStreamHandle invalid");
+        ALOGE("unregisterThread() mServiceStreamHandle invalid");
         return AAUDIO_ERROR_INVALID_STATE;
     }
     return mServiceInterface.unregisterAudioThread(mServiceStreamHandle, gettid());
@@ -374,12 +395,14 @@
     // Generated in server and passed to client. Return latest.
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
-        *framePosition = timestamp.getPosition();
-        *timeNanoseconds = timestamp.getNanoseconds();
-        return AAUDIO_OK;
-    } else {
-        return AAUDIO_ERROR_UNAVAILABLE;
+        int64_t position = timestamp.getPosition() + mFramesOffsetFromService;
+        if (position >= 0) {
+            *framePosition = position;
+            *timeNanoseconds = timestamp.getNanoseconds();
+            return AAUDIO_OK;
+        }
     }
+    return AAUDIO_ERROR_UNAVAILABLE;
 }
 
 aaudio_result_t AudioStreamInternal::updateStateMachine() {
@@ -394,14 +417,14 @@
     static int64_t oldTime = 0;
     int64_t framePosition = command.timestamp.position;
     int64_t nanoTime = command.timestamp.timestamp;
-    ALOGD("AudioStreamInternal: timestamp says framePosition = %08lld at nanoTime %lld",
+    ALOGD("logTimestamp: timestamp says framePosition = %8lld at nanoTime %lld",
          (long long) framePosition,
          (long long) nanoTime);
     int64_t nanosDelta = nanoTime - oldTime;
     if (nanosDelta > 0 && oldTime > 0) {
         int64_t framesDelta = framePosition - oldPosition;
         int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
-        ALOGD("AudioStreamInternal: framesDelta = %08lld, nanosDelta = %08lld, rate = %lld",
+        ALOGD("logTimestamp:     framesDelta = %8lld, nanosDelta = %8lld, rate = %lld",
               (long long) framesDelta, (long long) nanosDelta, (long long) rate);
     }
     oldPosition = framePosition;
@@ -478,6 +501,34 @@
     return result;
 }
 
+aaudio_result_t AudioStreamInternal::drainTimestampsFromService() {
+    aaudio_result_t result = AAUDIO_OK;
+
+    while (result == AAUDIO_OK) {
+        AAudioServiceMessage message;
+        if (mAudioEndpoint.readUpCommand(&message) != 1) {
+            break; // no command this time, no problem
+        }
+        switch (message.what) {
+            // ignore most messages
+            case AAudioServiceMessage::code::TIMESTAMP_SERVICE:
+            case AAudioServiceMessage::code::TIMESTAMP_HARDWARE:
+                break;
+
+            case AAudioServiceMessage::code::EVENT:
+                result = onEventFromServer(&message);
+                break;
+
+            default:
+                ALOGE("WARNING - drainTimestampsFromService() Unrecognized what = %d",
+                      (int) message.what);
+                result = AAUDIO_ERROR_INTERNAL;
+                break;
+        }
+    }
+    return result;
+}
+
 // Process all the commands coming from the server.
 aaudio_result_t AudioStreamInternal::processCommands() {
     aaudio_result_t result = AAUDIO_OK;
@@ -502,7 +553,7 @@
             break;
 
         default:
-            ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
+            ALOGE("WARNING - processCommands() Unrecognized what = %d",
                  (int) message.what);
             result = AAUDIO_ERROR_INTERNAL;
             break;
@@ -554,12 +605,19 @@
                 wakeTimeNanos += mWakeupDelayNanos;
             }
 
+            currentTimeNanos = AudioClock::getNanoseconds();
+            int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
+            // Guarantee a minimum sleep time.
+            if (wakeTimeNanos < earliestWakeTime) {
+                wakeTimeNanos = earliestWakeTime;
+            }
+
             if (wakeTimeNanos > deadlineNanos) {
                 // If we time out, just return the framesWritten so far.
                 // TODO remove after we fix the deadline bug
                 ALOGW("AudioStreamInternal::processData(): entered at %lld nanos, currently %lld",
                       (long long) entryTimeNanos, (long long) currentTimeNanos);
-                ALOGW("AudioStreamInternal::processData(): timed out after %lld nanos",
+                ALOGW("AudioStreamInternal::processData(): TIMEOUT after %lld nanos",
                       (long long) timeoutNanoseconds);
                 ALOGW("AudioStreamInternal::processData(): wakeTime = %lld, deadline = %lld nanos",
                       (long long) wakeTimeNanos, (long long) deadlineNanos);
@@ -570,13 +628,6 @@
                 break;
             }
 
-            currentTimeNanos = AudioClock::getNanoseconds();
-            int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
-            // Guarantee a minimum sleep time.
-            if (wakeTimeNanos < earliestWakeTime) {
-                wakeTimeNanos = earliestWakeTime;
-            }
-
             if (ATRACE_ENABLED()) {
                 int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
                 ATRACE_INT(fifoName, fullFrames);
@@ -613,7 +664,7 @@
     }
 
     aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(requestedFrames, &actualFrames);
-    ALOGD("AudioStreamInternal::setBufferSize() req = %d => %d", requestedFrames, actualFrames);
+    ALOGD("setBufferSize() req = %d => %d", requestedFrames, actualFrames);
     if (result < 0) {
         return result;
     } else {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 3523294..899d455 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -115,12 +115,15 @@
                             int64_t currentTimeNanos,
                             int64_t *wakeTimePtr) = 0;
 
+    aaudio_result_t drainTimestampsFromService();
+
     aaudio_result_t processCommands();
 
     aaudio_result_t requestStopInternal();
 
     aaudio_result_t stopCallback();
 
+    virtual void advanceClientToMatchServerPosition() = 0;
 
     virtual void onFlushFromServer() {}
 
@@ -167,6 +170,10 @@
 
     AAudioServiceInterface  &mServiceInterface;   // abstract interface to the service
 
+    SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
+
+    AtomicRequestor          mNeedCatchUp;   // Ask read() or write() to sync on first timestamp.
+
 private:
     /*
      * Asynchronous write with data conversion.
@@ -188,8 +195,6 @@
     AudioEndpointParcelable  mEndPointParcelable; // description of the buffers filled by service
     EndpointDescriptor       mEndpointDescriptor; // buffer description with resolved addresses
 
-    SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
-
     int64_t                  mServiceLatencyNanos = 0;
 
 };
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 7b1e53e..b792ecd 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -39,6 +39,21 @@
 
 AudioStreamInternalCapture::~AudioStreamInternalCapture() {}
 
+void AudioStreamInternalCapture::advanceClientToMatchServerPosition() {
+    int64_t readCounter = mAudioEndpoint.getDataReadCounter();
+    int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+
+    // Bump offset so caller does not see the retrograde motion in getFramesRead().
+    int64_t offset = readCounter - writeCounter;
+    mFramesOffsetFromService += offset;
+    ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
+          (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
+
+    // Force readCounter to match writeCounter.
+    // This is because we cannot change the write counter in the hardware.
+    mAudioEndpoint.setDataReadCounter(writeCounter);
+}
+
 // Write the data, block if needed and timeoutMillis > 0
 aaudio_result_t AudioStreamInternalCapture::read(void *buffer, int32_t numFrames,
                                                int64_t timeoutNanoseconds)
@@ -57,6 +72,18 @@
     const char *traceName = "aaRdNow";
     ATRACE_BEGIN(traceName);
 
+    if (mClockModel.isStarting()) {
+        // Still haven't got any timestamps from server.
+        // Keep waiting until we get some valid timestamps then start writing to the
+        // current buffer position.
+        ALOGD("processDataNow() wait for valid timestamps");
+        // Sleep very briefly and hope we get a timestamp soon.
+        *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
+        ATRACE_END();
+        return 0;
+    }
+    // If we have gotten this far then we have at least one timestamp from server.
+
     if (mAudioEndpoint.isFreeRunning()) {
         //ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
         // Update data queue based on the timing model.
@@ -65,6 +92,14 @@
         mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
     }
 
+    // This code assumes that we have already received valid timestamps.
+    if (mNeedCatchUp.isRequested()) {
+        // Catch an MMAP pointer that is already advancing.
+        // This will avoid initial underruns caused by a slow cold start.
+        advanceClientToMatchServerPosition();
+        mNeedCatchUp.acknowledge();
+    }
+
     // If the write index passed the read index then consider it an overrun.
     if (mAudioEndpoint.getEmptyFramesAvailable() < 0) {
         mXRunCount++;
@@ -100,8 +135,8 @@
                 // Calculate frame position based off of the readCounter because
                 // the writeCounter might have just advanced in the background,
                 // causing us to sleep until a later burst.
-                int64_t nextReadPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
-                wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
+                int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
+                wakeTime = mClockModel.convertPositionToTime(nextPosition);
             }
                 break;
             default:
@@ -186,8 +221,7 @@
 }
 
 int64_t AudioStreamInternalCapture::getFramesRead() {
-    int64_t frames = mAudioEndpoint.getDataWriteCounter()
-                               + mFramesOffsetFromService;
+    int64_t frames = mAudioEndpoint.getDataReadCounter() + mFramesOffsetFromService;
     //ALOGD("AudioStreamInternalCapture::getFramesRead() returns %lld", (long long)frames);
     return frames;
 }
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.h b/media/libaaudio/src/client/AudioStreamInternalCapture.h
index 17f37e8..294dbaf 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.h
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.h
@@ -46,6 +46,8 @@
     }
 protected:
 
+    void advanceClientToMatchServerPosition() override;
+
 /**
  * Low level data processing that will not block. It will just read or write as much as it can.
  *
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 31e0a40..f2e40a2 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -48,6 +48,7 @@
 
     mClockModel.stop(AudioClock::getNanoseconds());
     setState(AAUDIO_STREAM_STATE_PAUSING);
+    mAtomicTimestamp.clear();
     return AAudioConvert_androidToAAudioResult(pauseWithStatus());
 }
 
@@ -72,21 +73,25 @@
     return mServiceInterface.flushStream(mServiceStreamHandle);
 }
 
-void AudioStreamInternalPlay::onFlushFromServer() {
+void AudioStreamInternalPlay::advanceClientToMatchServerPosition() {
     int64_t readCounter = mAudioEndpoint.getDataReadCounter();
     int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
 
     // Bump offset so caller does not see the retrograde motion in getFramesRead().
-    int64_t framesFlushed = writeCounter - readCounter;
-    mFramesOffsetFromService += framesFlushed;
-    ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
+    int64_t offset = writeCounter - readCounter;
+    mFramesOffsetFromService += offset;
+    ALOGD("advanceClientToMatchServerPosition() readN = %lld, writeN = %lld, offset = %lld",
           (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
 
-    // Flush written frames by forcing writeCounter to readCounter.
-    // This is because we cannot move the read counter in the hardware.
+    // Force writeCounter to match readCounter.
+    // This is because we cannot change the read counter in the hardware.
     mAudioEndpoint.setDataWriteCounter(readCounter);
 }
 
+void AudioStreamInternalPlay::onFlushFromServer() {
+    advanceClientToMatchServerPosition();
+}
+
 // Write the data, block if needed and timeoutMillis > 0
 aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
                                            int64_t timeoutNanoseconds)
@@ -106,6 +111,18 @@
     const char *traceName = "aaWrNow";
     ATRACE_BEGIN(traceName);
 
+    if (mClockModel.isStarting()) {
+        // Still haven't got any timestamps from server.
+        // Keep waiting until we get some valid timestamps then start writing to the
+        // current buffer position.
+        ALOGD("processDataNow() wait for valid timestamps");
+        // Sleep very briefly and hope we get a timestamp soon.
+        *wakeTimePtr = currentNanoTime + (2000 * AAUDIO_NANOS_PER_MICROSECOND);
+        ATRACE_END();
+        return 0;
+    }
+    // If we have gotten this far then we have at least one timestamp from server.
+
     // If a DMA channel or DSP is reading the other end then we have to update the readCounter.
     if (mAudioEndpoint.isFreeRunning()) {
         // Update data queue based on the timing model.
@@ -114,6 +131,13 @@
         mAudioEndpoint.setDataReadCounter(estimatedReadCounter);
     }
 
+    if (mNeedCatchUp.isRequested()) {
+        // Catch an MMAP pointer that is already advancing.
+        // This will avoid initial underruns caused by a slow cold start.
+        advanceClientToMatchServerPosition();
+        mNeedCatchUp.acknowledge();
+    }
+
     // If the read index passed the write index then consider it an underrun.
     if (mAudioEndpoint.getFullFramesAvailable() < 0) {
         mXRunCount++;
@@ -153,9 +177,9 @@
                 // Calculate frame position based off of the writeCounter because
                 // the readCounter might have just advanced in the background,
                 // causing us to sleep until a later burst.
-                int64_t nextReadPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
+                int64_t nextPosition = mAudioEndpoint.getDataWriteCounter() + mFramesPerBurst
                         - mAudioEndpoint.getBufferSizeInFrames();
-                wakeTime = mClockModel.convertPositionToTime(nextReadPosition);
+                wakeTime = mClockModel.convertPositionToTime(nextPosition);
             }
                 break;
             default:
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index e59d02c..fdb1fd7 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -54,6 +54,8 @@
 
     aaudio_result_t requestPauseInternal();
 
+    void advanceClientToMatchServerPosition() override;
+
     void onFlushFromServer() override;
 
 /**
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index c06c8a9..bac69f1 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -48,19 +48,26 @@
 }
 
 void IsochronousClockModel::start(int64_t nanoTime) {
-    ALOGD("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
+    ALOGV("IsochronousClockModel::start(nanos = %lld)\n", (long long) nanoTime);
     mMarkerNanoTime = nanoTime;
     mState = STATE_STARTING;
 }
 
 void IsochronousClockModel::stop(int64_t nanoTime) {
-    ALOGD("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
+    ALOGV("IsochronousClockModel::stop(nanos = %lld)\n", (long long) nanoTime);
     setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
     // TODO should we set position?
     mState = STATE_STOPPED;
 }
 
+bool IsochronousClockModel::isStarting() {
+    return mState == STATE_STARTING;
+}
+
 void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
+//    ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
+//         (long long)framePosition,
+//         (long long)nanoTime);
     int64_t framesDelta = framePosition - mMarkerFramePosition;
     int64_t nanosDelta = nanoTime - mMarkerNanoTime;
     if (nanosDelta < 1000) {
@@ -70,9 +77,6 @@
 //    ALOGD("processTimestamp() - mMarkerFramePosition = %lld at mMarkerNanoTime %llu",
 //         (long long)mMarkerFramePosition,
 //         (long long)mMarkerNanoTime);
-//    ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
-//         (long long)framePosition,
-//         (long long)nanoTime);
 
     int64_t expectedNanosDelta = convertDeltaPositionToTime(framesDelta);
 //    ALOGD("processTimestamp() - expectedNanosDelta = %lld, nanosDelta = %llu",
@@ -116,6 +120,8 @@
     default:
         break;
     }
+
+//    ALOGD("processTimestamp() - mState = %d", mState);
 }
 
 void IsochronousClockModel::setSampleRate(int32_t sampleRate) {
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 585f53a..7182376 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -36,6 +36,8 @@
     void start(int64_t nanoTime);
     void stop(int64_t nanoTime);
 
+    bool isStarting();
+
     void processTimestamp(int64_t framePosition, int64_t nanoTime);
 
     /**
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 2816bac..ee29177 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <utils/String16.h>
 #include <media/AudioTrack.h>
+#include <media/AudioTimestamp.h>
 #include <aaudio/AAudio.h>
 
 #include "core/AudioStream.h"
@@ -46,16 +47,32 @@
     return AudioStreamLegacy_callback;
 }
 
-// Implement FixedBlockProcessor
-int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
-    int32_t frameCount = numBytes / getBytesPerFrame();
+int32_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) {
+    if (getDirection() == AAUDIO_DIRECTION_INPUT) {
+        // Increment before because we already got the data from the device.
+        incrementFramesRead(numFrames);
+    }
+
     // Call using the AAudio callback interface.
     AAudioStream_dataCallback appCallback = getDataCallbackProc();
-    return (*appCallback)(
+    aaudio_data_callback_result_t callbackResult = (*appCallback)(
             (AAudioStream *) this,
             getDataCallbackUserData(),
             buffer,
-            frameCount);
+            numFrames);
+
+    if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
+            && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
+        // Increment after because we are going to write the data to the device.
+        incrementFramesWritten(numFrames);
+    }
+    return callbackResult;
+}
+
+// Implement FixedBlockProcessor
+int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
+    int32_t numFrames = numBytes / getBytesPerFrame();
+    return callDataCallbackFrames(buffer, numFrames);
 }
 
 void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
@@ -81,16 +98,11 @@
                             (uint8_t *) audioBuffer->raw, byteCount);
                 } else {
                     // Call using the AAudio callback interface.
-                    callbackResult = (*getDataCallbackProc())(
-                            (AAudioStream *) this,
-                            getDataCallbackUserData(),
-                            audioBuffer->raw,
-                            audioBuffer->frameCount
-                            );
+                    callbackResult = callDataCallbackFrames((uint8_t *)audioBuffer->raw,
+                                                            audioBuffer->frameCount);
                 }
                 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
                     audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
-                    incrementClientFrameCounter(audioBuffer->frameCount);
                 } else {
                     audioBuffer->size = 0;
                 }
@@ -139,7 +151,18 @@
             return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
             break;
     }
-    status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
+    ExtendedTimestamp::Location location = ExtendedTimestamp::Location::LOCATION_INVALID;
+    int64_t localPosition;
+    status_t status = extendedTimestamp->getBestTimestamp(&localPosition, timeNanoseconds,
+                                                          timebase, &location);
+    // use MonotonicCounter to prevent retrograde motion.
+    mTimestampPosition.update32((int32_t)localPosition);
+    *framePosition = mTimestampPosition.get();
+
+//    ALOGD("getBestTimestamp() fposition: server = %6lld, kernel = %6lld, location = %d",
+//          (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_SERVER],
+//          (long long) extendedTimestamp->mPosition[ExtendedTimestamp::Location::LOCATION_KERNEL],
+//          (int)location);
     return AAudioConvert_androidToAAudioResult(status);
 }
 
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index d2ef3c7..66c216c 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -63,6 +63,8 @@
 
     aaudio_legacy_callback_t getLegacyCallback();
 
+    int32_t callDataCallbackFrames(uint8_t *buffer, int32_t numFrames);
+
     // This is public so it can be called from the C callback function.
     // This is called from the AudioTrack/AudioRecord client.
     virtual void processCallback(int event, void *info) = 0;
@@ -122,6 +124,7 @@
 
     MonotonicCounter           mFramesWritten;
     MonotonicCounter           mFramesRead;
+    MonotonicCounter           mTimestampPosition;
 
     FixedBlockAdapter         *mBlockAdapter = nullptr;
     aaudio_wrapping_frames_t   mPositionWhenStarting = 0;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 041280d..c8b94ae 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -233,8 +233,10 @@
     onStop();
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review
+    mTimestampPosition.set(getFramesRead());
     mAudioRecord->stop();
     mFramesRead.reset32();
+    mTimestampPosition.reset32();
     return AAUDIO_OK;
 }
 
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 155362c..702b12a 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -285,6 +285,7 @@
     incrementFramesRead(getFramesWritten() - getFramesRead());
     mAudioTrack->flush();
     mFramesWritten.reset32();
+    mTimestampPosition.reset32();
     return AAUDIO_OK;
 }
 
@@ -298,8 +299,10 @@
     onStop();
     setState(AAUDIO_STREAM_STATE_STOPPING);
     incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
+    mTimestampPosition.set(getFramesWritten());
     stop();
     mFramesWritten.reset32();
+    mTimestampPosition.reset32();
     return AAUDIO_OK;
 }
 
@@ -447,5 +450,18 @@
     if (status != NO_ERROR) {
         return AAudioConvert_androidToAAudioResult(status);
     }
-    return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
+    int64_t position = 0;
+    int64_t nanoseconds = 0;
+    aaudio_result_t result = getBestTimestamp(clockId, &position,
+                                              &nanoseconds, &extendedTimestamp);
+    if (result == AAUDIO_OK) {
+        if (position < getFramesWritten()) {
+            *framePosition = position;
+            *timeNanoseconds = nanoseconds;
+            return result;
+        } else {
+            return AAUDIO_ERROR_INVALID_STATE; // TODO review, documented but not consistent
+        }
+    }
+    return result;
 }
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index b0c6c94..f56be32 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -272,8 +272,7 @@
 class SimpleDoubleBuffer {
 public:
     SimpleDoubleBuffer()
-            : mValues()
-            , mCounter(0) {}
+            : mValues() {}
 
     __attribute__((no_sanitize("integer")))
     void write(T value) {
@@ -282,6 +281,14 @@
         mCounter++; // Increment AFTER updating storage, OK if it wraps.
     }
 
+    /**
+     * This should only be called by the same thread that calls write() or when
+     * no other thread is calling write.
+     */
+    void clear() {
+        mCounter.store(0);
+    }
+
     T read() const {
         T result;
         int before;
@@ -293,7 +300,7 @@
             int index = (before & 1) ^ 1;
             result = mValues[index];
             after = mCounter.load();
-        } while ((after != before) && --timeout > 0);
+        } while ((after != before) && (after > 0) && (--timeout > 0));
         return result;
     }
 
@@ -306,7 +313,7 @@
 
 private:
     T                    mValues[2];
-    std::atomic<int>     mCounter;
+    std::atomic<int>     mCounter{0};
 };
 
 class Timestamp {
@@ -328,4 +335,32 @@
     int64_t mNanoseconds;
 };
 
+
+/**
+ * Pass a request to another thread.
+ * This is used when one thread, A, wants another thread, B, to do something.
+ * A naive approach would be for A to set a flag and for B to clear it when done.
+ * But that creates a race condition. This technique avoids the race condition.
+ *
+ * Assumes only one requester and one acknowledger.
+ */
+class AtomicRequestor {
+public:
+    void request() {
+        // TODO handle overflows, very unlikely
+        mRequested++;
+    }
+
+    bool isRequested() {
+        return mRequested.load() > mAcknowledged.load();
+    }
+
+    void acknowledge() {
+        mAcknowledged++;
+    }
+
+private:
+    std::atomic<int> mRequested{0};
+    std::atomic<int> mAcknowledged{0};
+};
 #endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libaaudio/src/utility/MonotonicCounter.h b/media/libaaudio/src/utility/MonotonicCounter.h
index 81d7f89..13c92a2 100644
--- a/media/libaaudio/src/utility/MonotonicCounter.h
+++ b/media/libaaudio/src/utility/MonotonicCounter.h
@@ -41,6 +41,13 @@
     }
 
     /**
+     * set the current value of the counter
+     */
+    void set(int64_t counter) {
+        mCounter64 = counter;
+    }
+
+    /**
      * Advance the counter if delta is positive.
      * @return current value of the counter
      */
diff --git a/media/libaaudio/tests/test_timestamps.cpp b/media/libaaudio/tests/test_timestamps.cpp
index d9ca391..49de05a 100644
--- a/media/libaaudio/tests/test_timestamps.cpp
+++ b/media/libaaudio/tests/test_timestamps.cpp
@@ -17,23 +17,99 @@
 // Play silence and recover from dead servers or disconnected devices.
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <unistd.h>
 
 #include <aaudio/AAudio.h>
 #include <aaudio/AAudioTesting.h>
-
 #include "utils/AAudioExampleUtils.h"
+#include "../examples/utils/AAudioExampleUtils.h"
 
-#define DEFAULT_TIMEOUT_NANOS  ((int64_t)1000000000)
+// Arbitrary period for glitches, once per second at 48000 Hz.
+#define FORCED_UNDERRUN_PERIOD_FRAMES    48000
+// How long to sleep in a callback to cause an intentional glitch. For testing.
+#define FORCED_UNDERRUN_SLEEP_MICROS     (10 * 1000)
 
-int main(int argc, char **argv) {
-    (void) argc;
-    (void *)argv;
+#define MAX_TIMESTAMPS          1000
 
+#define DEFAULT_TIMEOUT_NANOS   ((int64_t)1000000000)
+
+#define NUM_SECONDS             1
+#define NUM_LOOPS               4
+
+typedef struct TimestampInfo {
+    int64_t         framesTotal;
+    int64_t         appPosition; // frames
+    int64_t         appNanoseconds;
+    int64_t         timestampPosition;  // frames
+    int64_t         timestampNanos;
+    aaudio_result_t result;
+} TimestampInfo;
+
+typedef struct TimestampCallbackData_s {
+    TimestampInfo  timestamps[MAX_TIMESTAMPS];
+    int64_t        framesTotal = 0;
+    int64_t        nextFrameToGlitch = FORCED_UNDERRUN_PERIOD_FRAMES;
+    int32_t        timestampCount = 0; // in timestamps
+    bool           forceUnderruns = false;
+} TimestampCallbackData_t;
+
+// Callback function that fills the audio output buffer.
+aaudio_data_callback_result_t timestampDataCallbackProc(
+        AAudioStream *stream,
+        void *userData,
+        void *audioData __unused,
+        int32_t numFrames
+) {
+
+    // should not happen but just in case...
+    if (userData == nullptr) {
+        printf("ERROR - SimplePlayerDataCallbackProc needs userData\n");
+        return AAUDIO_CALLBACK_RESULT_STOP;
+    }
+    TimestampCallbackData_t *timestampData = (TimestampCallbackData_t *) userData;
+
+    aaudio_direction_t direction = AAudioStream_getDirection(stream);
+    if (direction == AAUDIO_DIRECTION_INPUT) {
+        timestampData->framesTotal += numFrames;
+    }
+
+    if (timestampData->forceUnderruns) {
+        if (timestampData->framesTotal > timestampData->nextFrameToGlitch) {
+            usleep(FORCED_UNDERRUN_SLEEP_MICROS);
+            printf("Simulate glitch at %lld\n", (long long) timestampData->framesTotal);
+            timestampData->nextFrameToGlitch += FORCED_UNDERRUN_PERIOD_FRAMES;
+        }
+    }
+
+    if (timestampData->timestampCount < MAX_TIMESTAMPS) {
+        TimestampInfo *timestamp = &timestampData->timestamps[timestampData->timestampCount];
+        timestamp->result = AAudioStream_getTimestamp(stream,
+                                                      CLOCK_MONOTONIC,
+                                                      &timestamp->timestampPosition,
+                                                      &timestamp->timestampNanos);
+        timestamp->framesTotal = timestampData->framesTotal;
+        timestamp->appPosition = (direction == AAUDIO_DIRECTION_OUTPUT)
+                ? AAudioStream_getFramesWritten(stream)
+                : AAudioStream_getFramesRead(stream);
+        timestamp->appNanoseconds = getNanoseconds();
+        timestampData->timestampCount++;
+    }
+
+    if (direction == AAUDIO_DIRECTION_OUTPUT) {
+        timestampData->framesTotal += numFrames;
+    }
+    return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+static TimestampCallbackData_t sTimestampData;
+
+static aaudio_result_t testTimeStamps(aaudio_policy_t mmapPolicy,
+                           aaudio_sharing_mode_t sharingMode,
+                           aaudio_performance_mode_t performanceMode,
+                           aaudio_direction_t direction) {
     aaudio_result_t result = AAUDIO_OK;
 
-    int32_t triesLeft = 3;
-    int32_t bufferCapacity;
     int32_t framesPerBurst = 0;
     float *buffer = nullptr;
 
@@ -44,22 +120,20 @@
     int32_t finalBufferSize = 0;
     aaudio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_FLOAT;
     aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
-    int32_t framesMax;
-    int64_t framesTotal;
-    int64_t printAt;
-    int samplesPerBurst;
-    int64_t previousFramePosition = -1;
+    aaudio_sharing_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
     AAudioStreamBuilder *aaudioBuilder = nullptr;
     AAudioStream *aaudioStream = nullptr;
 
-    // Make printf print immediately so that debug info is not stuck
-    // in a buffer if we hang or crash.
-    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+    memset(&sTimestampData, 0, sizeof(sTimestampData));
 
-    printf("Test Timestamps V0.1.1\n");
+    printf("------------ testTimeStamps(policy = %d, sharing = %s, perf = %s, dir = %s) -----------\n",
+           mmapPolicy,
+           getSharingModeText(sharingMode),
+           getPerformanceModeText(performanceMode),
+           getDirectionText(direction));
 
-    AAudio_setMMapPolicy(AAUDIO_POLICY_AUTO);
+    AAudio_setMMapPolicy(mmapPolicy);
 
     // Use an AAudioStreamBuilder to contain requested parameters.
     result = AAudio_createStreamBuilder(&aaudioBuilder);
@@ -70,9 +144,11 @@
     }
 
     // Request stream properties.
-    AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_FLOAT);
-    //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
-    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+    AAudioStreamBuilder_setFormat(aaudioBuilder, AAUDIO_FORMAT_PCM_I16);
+    AAudioStreamBuilder_setSharingMode(aaudioBuilder, sharingMode);
+    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, performanceMode);
+    AAudioStreamBuilder_setDirection(aaudioBuilder, direction);
+    AAudioStreamBuilder_setDataCallback(aaudioBuilder, timestampDataCallbackProc, &sTimestampData);
 
     // Create an AAudioStream using the Builder.
     result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
@@ -87,10 +163,25 @@
     actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
     actualDataFormat = AAudioStream_getFormat(aaudioStream);
 
-    printf("-------- chans = %3d, rate = %6d format = %d\n",
-            actualChannelCount, actualSampleRate, actualDataFormat);
+    actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
+    if (actualSharingMode != sharingMode) {
+        printf("did not get expected sharingMode, got %3d, skipping test\n",
+               actualSharingMode);
+        result = AAUDIO_OK;
+        goto finish;
+    }
+    actualPerformanceMode = AAudioStream_getPerformanceMode(aaudioStream);
+    if (actualPerformanceMode != performanceMode) {
+        printf("did not get expected performanceMode, got %3d, skipping test\n",
+               actualPerformanceMode);
+        result = AAUDIO_OK;
+        goto finish;
+    }
+
+    printf("    chans = %3d, rate = %6d format = %d\n",
+           actualChannelCount, actualSampleRate, actualDataFormat);
     printf("    Is MMAP used? %s\n", AAudioStream_isMMapUsed(aaudioStream)
-                                   ? "yes" : "no");
+                                     ? "yes" : "no");
 
     // This is the number of frames that are read in one chunk by a DMA controller
     // or a DSP or a mixer.
@@ -98,91 +189,143 @@
     printf("    framesPerBurst = %3d\n", framesPerBurst);
 
     originalBufferSize = AAudioStream_getBufferSizeInFrames(aaudioStream);
-    requestedBufferSize = 2 * framesPerBurst;
+    requestedBufferSize = 4 * framesPerBurst;
     finalBufferSize = AAudioStream_setBufferSizeInFrames(aaudioStream, requestedBufferSize);
 
     printf("    BufferSize: original = %4d, requested = %4d, final = %4d\n",
            originalBufferSize, requestedBufferSize, finalBufferSize);
 
-    samplesPerBurst = framesPerBurst * actualChannelCount;
-    buffer = new float[samplesPerBurst];
-
-    result = AAudioStream_requestStart(aaudioStream);
-    if (result != AAUDIO_OK) {
-        printf("AAudioStream_requestStart returned %s",
+    {
+        int64_t position;
+        int64_t nanoseconds;
+        result = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC, &position, &nanoseconds);
+        printf("before start, AAudioStream_getTimestamp() returns %s\n",
                AAudio_convertResultToText(result));
-        goto finish;
     }
 
-    // Play silence very briefly.
-    framesMax = actualSampleRate * 4;
-    framesTotal = 0;
-    printAt = actualSampleRate;
-    while (result == AAUDIO_OK && framesTotal < framesMax) {
-        int32_t framesWritten = AAudioStream_write(aaudioStream,
-                                                   buffer, framesPerBurst,
-                                                   DEFAULT_TIMEOUT_NANOS);
-        if (framesWritten < 0) {
-            result = framesWritten;
-            printf("write() returned %s, frames = %d\n",
-                   AAudio_convertResultToText(result), (int)framesTotal);
-            printf("  frames = %d\n", (int)framesTotal);
-        } else if (framesWritten != framesPerBurst) {
-            printf("write() returned %d, frames = %d\n", framesWritten, (int)framesTotal);
-            result = AAUDIO_ERROR_TIMEOUT;
-        } else {
-            framesTotal += framesWritten;
-            if (framesTotal >= printAt) {
-                printf("frames = %d\n", (int)framesTotal);
-                printAt += actualSampleRate;
+    for (int runs = 0; runs < NUM_LOOPS; runs++) {
+        printf("------------------ loop #%d\n", runs);
+
+        int64_t temp = sTimestampData.framesTotal;
+        memset(&sTimestampData, 0, sizeof(sTimestampData));
+        sTimestampData.framesTotal = temp;
+
+        sTimestampData.forceUnderruns = false;
+
+        result = AAudioStream_requestStart(aaudioStream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStream_requestStart returned %s",
+                   AAudio_convertResultToText(result));
+            goto finish;
+        }
+
+        for (int second = 0; second < NUM_SECONDS; second++) {
+            // Give AAudio callback time to run in the background.
+            sleep(1);
+
+            // Periodically print the progress so we know it hasn't died.
+            printf("framesWritten = %d, XRuns = %d\n",
+                   (int) AAudioStream_getFramesWritten(aaudioStream),
+                   (int) AAudioStream_getXRunCount(aaudioStream)
+            );
+        }
+
+        result = AAudioStream_requestStop(aaudioStream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStream_requestStop returned %s\n",
+                   AAudio_convertResultToText(result));
+        }
+
+        printf("timestampCount = %d\n", sTimestampData.timestampCount);
+        int printed = 0;
+        for (int i = 0; i < sTimestampData.timestampCount; i++) {
+            TimestampInfo *timestamp = &sTimestampData.timestamps[i];
+            bool posChanged = (timestamp->timestampPosition != (timestamp - 1)->timestampPosition);
+            bool timeChanged = (timestamp->timestampNanos != (timestamp - 1)->timestampNanos);
+            if ((printed < 20) && ((i < 10) || posChanged || timeChanged)) {
+                printf("  %3d : frames %8lld, xferd %8lld", i,
+                       (long long) timestamp->framesTotal,
+                       (long long) timestamp->appPosition);
+                if (timestamp->result != AAUDIO_OK) {
+                    printf(", result = %s\n", AAudio_convertResultToText(timestamp->result));
+                } else {
+                    bool negative = timestamp->timestampPosition < 0;
+                    bool retro = (i > 0 && (timestamp->timestampPosition <
+                                            (timestamp - 1)->timestampPosition));
+                    const char *message = negative ? " <=NEGATIVE!"
+                                                   : (retro ? "  <= RETROGRADE!" : "");
+
+                    double latency = calculateLatencyMillis(timestamp->timestampPosition,
+                                             timestamp->timestampNanos,
+                                             timestamp->appPosition,
+                                             timestamp->appNanoseconds,
+                                             actualSampleRate);
+                    printf(", STAMP: pos = %8lld, nanos = %8lld, lat = %7.1f msec %s\n",
+                           (long long) timestamp->timestampPosition,
+                           (long long) timestamp->timestampNanos,
+                           latency,
+                           message);
+                }
+                printed++;
             }
         }
 
-        // Print timestamps.
-        int64_t framePosition = 0;
-        int64_t frameTime = 0;
-        aaudio_result_t timeResult;
-        timeResult = AAudioStream_getTimestamp(aaudioStream, CLOCK_MONOTONIC,
-                                           &framePosition, &frameTime);
-
-        if (timeResult == AAUDIO_OK) {
-            if (framePosition > (previousFramePosition + 5000)) {
-                int64_t realTime = getNanoseconds();
-                int64_t framesWritten = AAudioStream_getFramesWritten(aaudioStream);
-
-                double latencyMillis = calculateLatencyMillis(framePosition, frameTime,
-                                                              framesWritten, realTime,
-                                                              actualSampleRate);
-
-                printf("--- timestamp: result = %4d, position = %lld, at %lld nanos"
-                               ", latency = %7.2f msec\n",
-                       timeResult,
-                       (long long) framePosition,
-                       (long long) frameTime,
-                       latencyMillis);
-                previousFramePosition = framePosition;
-            }
-        }
+        // Avoid race conditions in AudioFlinger.
+        // There is normally a delay between a real user stopping and restarting a stream.
+        sleep(1);
     }
 
-    result = AAudioStream_requestStop(aaudioStream);
-    if (result != AAUDIO_OK) {
-        printf("AAudioStream_requestStop returned %s\n",
-               AAudio_convertResultToText(result));
-    }
-    result = AAudioStream_close(aaudioStream);
-    if (result != AAUDIO_OK) {
-        printf("AAudioStream_close returned %s\n",
-               AAudio_convertResultToText(result));
-    }
-    aaudioStream = nullptr;
-
-
 finish:
     if (aaudioStream != nullptr) {
         AAudioStream_close(aaudioStream);
     }
     AAudioStreamBuilder_delete(aaudioBuilder);
-    delete[] buffer;
     printf("result = %d = %s\n", result, AAudio_convertResultToText(result));
+
+    return result;
+}
+
+int main(int argc, char **argv) {
+    (void) argc;
+    (void *) argv;
+
+    aaudio_result_t result = AAUDIO_OK;
+
+    // Make printf print immediately so that debug info is not stuck
+    // in a buffer if we hang or crash.
+    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+    printf("Test Timestamps V0.1.2\n");
+    // Legacy
+//    result = testTimeStamps(AAUDIO_POLICY_NEVER,
+//                            AAUDIO_SHARING_MODE_SHARED,
+//                            AAUDIO_PERFORMANCE_MODE_NONE,
+//                            AAUDIO_DIRECTION_INPUT);
+//    result = testTimeStamps(AAUDIO_POLICY_NEVER,
+//                            AAUDIO_SHARING_MODE_SHARED,
+//                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+//                            AAUDIO_DIRECTION_INPUT);
+//    result = testTimeStamps(AAUDIO_POLICY_NEVER, AAUDIO_SHARING_MODE_SHARED,
+//                            AAUDIO_PERFORMANCE_MODE_NONE,
+//                            AAUDIO_DIRECTION_OUTPUT);
+    result = testTimeStamps(AAUDIO_POLICY_NEVER, AAUDIO_SHARING_MODE_SHARED,
+                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+                            AAUDIO_DIRECTION_OUTPUT);
+   // MMAP
+//    result = testTimeStamps(AAUDIO_POLICY_ALWAYS,
+//                            AAUDIO_SHARING_MODE_EXCLUSIVE,
+//                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+//                            AAUDIO_DIRECTION_INPUT);
+//    result = testTimeStamps(AAUDIO_POLICY_ALWAYS,
+//                            AAUDIO_SHARING_MODE_EXCLUSIVE,
+//                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+//                            AAUDIO_DIRECTION_OUTPUT);
+//    result = testTimeStamps(AAUDIO_POLICY_ALWAYS, AAUDIO_SHARING_MODE_SHARED,
+//                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+//                            AAUDIO_DIRECTION_INPUT);
+//    result = testTimeStamps(AAUDIO_POLICY_ALWAYS, AAUDIO_SHARING_MODE_SHARED,
+//                            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY,
+//                            AAUDIO_DIRECTION_OUTPUT);
+
+    return (result == AAUDIO_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index a5f9ab6..b1cb0e7 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -135,7 +135,11 @@
             &mStatus, &mId, &enabled);
 
     if (iEffect == 0 || (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS)) {
-        ALOGE("set(): AudioFlinger could not create effect, status: %d", mStatus);
+        char typeBuffer[64], uuidBuffer[64];
+        guidToString(type, typeBuffer, sizeof(typeBuffer));
+        guidToString(uuid, uuidBuffer, sizeof(uuidBuffer));
+        ALOGE("set(): AudioFlinger could not create effect %s / %s, status: %d",
+                typeBuffer, uuidBuffer, mStatus);
         if (iEffect == 0) {
             mStatus = NO_INIT;
         }
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ccadb6c..b99eaff 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -63,6 +63,14 @@
     return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
 }
 
+// TODO move to audio_utils.
+static inline struct timespec convertNsToTimespec(int64_t ns) {
+    struct timespec tv;
+    tv.tv_sec = static_cast<time_t>(ns / NANOS_PER_SECOND);
+    tv.tv_nsec = static_cast<long>(ns % NANOS_PER_SECOND);
+    return tv;
+}
+
 // current monotonic time in microseconds.
 static int64_t getNowUs()
 {
@@ -539,7 +547,8 @@
     mUpdatePeriod = 0;
     mPosition = 0;
     mReleased = 0;
-    mStartUs = 0;
+    mStartNs = 0;
+    mStartFromZeroUs = 0;
     AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
     mSequence = 1;
     mObservedSequence = mSequence;
@@ -587,6 +596,7 @@
             mStartEts.clear();
         }
     }
+    mStartNs = systemTime(); // save this for timestamp adjustment after starting.
     if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
         // reset current position as seen by client to 0
         mPosition = 0;
@@ -605,7 +615,8 @@
                             + mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER]),
                     (long long)mStartEts.mFlushed,
                     (long long)mFramesWritten);
-            mFramesWrittenServerOffset = -mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
+            // mStartEts is already adjusted by mFramesWrittenServerOffset, so we delta adjust.
+            mFramesWrittenServerOffset -= mStartEts.mPosition[ExtendedTimestamp::LOCATION_SERVER];
         }
         mFramesWritten = 0;
         mProxy->clearTimestamp(); // need new server push for valid timestamp
@@ -615,7 +626,7 @@
         // since the flush is asynchronous and stop may not fully drain.
         // We save the time when the track is started to later verify whether
         // the counters are realistic (i.e. start from zero after this time).
-        mStartUs = getNowUs();
+        mStartFromZeroUs = mStartNs / 1000;
 
         // force refresh of remaining frames by processAudioBuffer() as last
         // write before stop could be partial.
@@ -2578,8 +2589,7 @@
                     if (at < limit) {
                         ALOGV("timestamp pause lag:%lld adjusting from %lld to %lld",
                                 (long long)lag, (long long)at, (long long)limit);
-                        timestamp.mTime.tv_sec = limit / NANOS_PER_SECOND;
-                        timestamp.mTime.tv_nsec = limit % NANOS_PER_SECOND; // compiler opt.
+                        timestamp.mTime = convertNsToTimespec(limit);
                     }
                 }
                 mPreviousLocation = location;
@@ -2622,18 +2632,18 @@
         // the previous song under gapless playback.
         // However, we sometimes see zero timestamps, then a glitch of
         // the previous song's position, and then correct timestamps afterwards.
-        if (mStartUs != 0 && mSampleRate != 0) {
+        if (mStartFromZeroUs != 0 && mSampleRate != 0) {
             static const int kTimeJitterUs = 100000; // 100 ms
             static const int k1SecUs = 1000000;
 
             const int64_t timeNow = getNowUs();
 
-            if (timeNow < mStartUs + k1SecUs) { // within first second of starting
+            if (timeNow < mStartFromZeroUs + k1SecUs) { // within first second of starting
                 const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
-                if (timestampTimeUs < mStartUs) {
+                if (timestampTimeUs < mStartFromZeroUs) {
                     return WOULD_BLOCK;  // stale timestamp time, occurs before start.
                 }
-                const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
+                const int64_t deltaTimeUs = timestampTimeUs - mStartFromZeroUs;
                 const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
                         / ((double)mSampleRate * mPlaybackRate.mSpeed);
 
@@ -2656,10 +2666,10 @@
                     return WOULD_BLOCK;
                 }
                 if (deltaPositionByUs != 0) {
-                    mStartUs = 0; // don't check again, we got valid nonzero position.
+                    mStartFromZeroUs = 0; // don't check again, we got valid nonzero position.
                 }
             } else {
-                mStartUs = 0; // don't check again, start time expired.
+                mStartFromZeroUs = 0; // don't check again, start time expired.
             }
             mTimestampStartupGlitchReported = false;
         }
@@ -2697,14 +2707,33 @@
     // Prevent retrograde motion in timestamp.
     // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
     if (status == NO_ERROR) {
+        // previousTimestampValid is set to false when starting after a stop or flush.
         if (previousTimestampValid) {
             const int64_t previousTimeNanos =
                     audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
-            const int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
+            int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
+
+            // Fix stale time when checking timestamp right after start().
+            //
+            // For offload compatibility, use a default lag value here.
+            // Any time discrepancy between this update and the pause timestamp is handled
+            // by the retrograde check afterwards.
+            const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
+            const int64_t limitNs = mStartNs - lagNs;
+            if (currentTimeNanos < limitNs) {
+                ALOGD("correcting timestamp time for pause, "
+                        "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
+                        (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
+                timestamp.mTime = convertNsToTimespec(limitNs);
+                currentTimeNanos = limitNs;
+            }
+
+            // retrograde check
             if (currentTimeNanos < previousTimeNanos) {
                 ALOGW("retrograde timestamp time corrected, %lld < %lld",
                         (long long)currentTimeNanos, (long long)previousTimeNanos);
                 timestamp.mTime = mPreviousTimestamp.mTime;
+                // currentTimeNanos not used below.
             }
 
             // Looking at signed delta will work even when the timestamps
@@ -2914,7 +2943,7 @@
     case STATE_STOPPED:
         if (isOffloadedOrDirect_l()) {
             // check if we have started in the past to return true.
-            return mStartUs > 0;
+            return mStartFromZeroUs > 0;
         }
         // A normal audio track may still be draining, so
         // check if stream has ended.  This covers fasttrack position
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index b168fc9..7ae8ec5 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1085,8 +1085,10 @@
                                                     // reset by stop() but continues monotonically
                                                     // after new IAudioTrack to restore mPosition,
                                                     // and could be easily widened to uint64_t
-    int64_t                 mStartUs;               // the start time after flush or stop.
+    int64_t                 mStartFromZeroUs;       // the start time after flush or stop,
+                                                    // when position should be 0.
                                                     // only used for offloaded and direct tracks.
+    int64_t                 mStartNs;               // the time when start() is called.
     ExtendedTimestamp       mStartEts;              // Extended timestamp at start for normal
                                                     // AudioTracks.
     AudioTimestamp          mStartTs;               // Timestamp at start for offloaded or direct
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 1717b49..ee9406d 100644
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -429,9 +429,7 @@
     size_t ii;
     LVM_INT32 temp;
 
-    src += n-1;
-    dst += n-1;
-    for (ii = n; ii != 0; ii--) {
+    for (ii = 0; ii < n; ii++) {
         temp = (LVM_INT32)((*src) * 32768.0f);
         if (temp >= 32767) {
             *dst = 32767;
@@ -440,8 +438,8 @@
         } else {
             *dst = (LVM_INT16)temp;
         }
-        src--;
-        dst--;
+        src++;
+        dst++;
     }
     return;
 }
diff --git a/media/libmedia/IMediaCodecService.cpp b/media/libmedia/IMediaCodecService.cpp
index 2d62419..adfa93d 100644
--- a/media/libmedia/IMediaCodecService.cpp
+++ b/media/libmedia/IMediaCodecService.cpp
@@ -27,7 +27,8 @@
 namespace android {
 
 enum {
-    GET_OMX = IBinder::FIRST_CALL_TRANSACTION
+    GET_OMX = IBinder::FIRST_CALL_TRANSACTION,
+    GET_OMX_STORE
 };
 
 class BpMediaCodecService : public BpInterface<IMediaCodecService>
@@ -45,6 +46,13 @@
         return interface_cast<IOMX>(reply.readStrongBinder());
     }
 
+    virtual sp<IOMXStore> getOMXStore() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaCodecService::getInterfaceDescriptor());
+        remote()->transact(GET_OMX_STORE, data, &reply);
+        return interface_cast<IOMXStore>(reply.readStrongBinder());
+    }
+
 };
 
 IMPLEMENT_META_INTERFACE(MediaCodecService, "android.media.IMediaCodecService");
@@ -62,6 +70,12 @@
             reply->writeStrongBinder(IInterface::asBinder(omx));
             return NO_ERROR;
         }
+        case GET_OMX_STORE: {
+            CHECK_INTERFACE(IMediaCodecService, data, reply);
+            sp<IOMXStore> omxStore = getOMXStore();
+            reply->writeStrongBinder(IInterface::asBinder(omxStore));
+            return NO_ERROR;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 2a74512..a570ffe 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -101,42 +101,46 @@
     return OK;
 }
 
-void MediaCodecInfo::CapabilitiesBuilder::addProfileLevel(uint32_t profile, uint32_t level) {
+void MediaCodecInfo::CapabilitiesWriter::addDetail(
+        const char* key, const char* value) {
+    mCap->mDetails->setString(key, value);
+}
+
+void MediaCodecInfo::CapabilitiesWriter::addDetail(
+        const char* key, int32_t value) {
+    mCap->mDetails->setInt32(key, value);
+}
+
+void MediaCodecInfo::CapabilitiesWriter::addProfileLevel(
+        uint32_t profile, uint32_t level) {
     ProfileLevel profileLevel;
     profileLevel.mProfile = profile;
     profileLevel.mLevel = level;
-    if (mProfileLevelsSorted.indexOf(profileLevel) < 0) {
-        mProfileLevels.push_back(profileLevel);
-        mProfileLevelsSorted.add(profileLevel);
+    if (mCap->mProfileLevelsSorted.indexOf(profileLevel) < 0) {
+        mCap->mProfileLevels.push_back(profileLevel);
+        mCap->mProfileLevelsSorted.add(profileLevel);
     }
 }
 
-void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
-    if (mColorFormatsSorted.indexOf(format) < 0) {
-        mColorFormats.push(format);
-        mColorFormatsSorted.add(format);
+void MediaCodecInfo::CapabilitiesWriter::addColorFormat(uint32_t format) {
+    if (mCap->mColorFormatsSorted.indexOf(format) < 0) {
+        mCap->mColorFormats.push(format);
+        mCap->mColorFormatsSorted.add(format);
     }
 }
 
-void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
-    mFlags |= flags;
+void MediaCodecInfo::CapabilitiesWriter::addFlags(uint32_t flags) {
+    mCap->mFlags |= flags;
+}
+
+MediaCodecInfo::CapabilitiesWriter::CapabilitiesWriter(
+        MediaCodecInfo::Capabilities* cap) : mCap(cap) {
 }
 
 bool MediaCodecInfo::isEncoder() const {
     return mIsEncoder;
 }
 
-bool MediaCodecInfo::hasQuirk(const char *name) const {
-    if (name) {
-        for (size_t ix = 0; ix < mQuirks.size(); ix++) {
-            if (mQuirks.itemAt(ix).equalsIgnoreCase(name)) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
-
 void MediaCodecInfo::getSupportedMimes(Vector<AString> *mimes) const {
     mimes->clear();
     for (size_t ix = 0; ix < mCaps.size(); ix++) {
@@ -157,20 +161,21 @@
     return mName.c_str();
 }
 
+const char *MediaCodecInfo::getOwnerName() const {
+    return mOwner.c_str();
+}
+
 // static
 sp<MediaCodecInfo> MediaCodecInfo::FromParcel(const Parcel &parcel) {
     AString name = AString::FromParcel(parcel);
+    AString owner = AString::FromParcel(parcel);
     bool isEncoder = static_cast<bool>(parcel.readInt32());
-    sp<MediaCodecInfo> info = new MediaCodecInfo(name, isEncoder, NULL);
+    sp<MediaCodecInfo> info = new MediaCodecInfo;
+    info->mName = name;
+    info->mOwner = owner;
+    info->mIsEncoder = isEncoder;
     size_t size = static_cast<size_t>(parcel.readInt32());
     for (size_t i = 0; i < size; i++) {
-        AString quirk = AString::FromParcel(parcel);
-        if (info != NULL) {
-            info->mQuirks.push_back(quirk);
-        }
-    }
-    size = static_cast<size_t>(parcel.readInt32());
-    for (size_t i = 0; i < size; i++) {
         AString mime = AString::FromParcel(parcel);
         sp<Capabilities> caps = Capabilities::FromParcel(parcel);
         if (caps == NULL)
@@ -184,11 +189,8 @@
 
 status_t MediaCodecInfo::writeToParcel(Parcel *parcel) const {
     mName.writeToParcel(parcel);
+    mOwner.writeToParcel(parcel);
     parcel->writeInt32(mIsEncoder);
-    parcel->writeInt32(mQuirks.size());
-    for (size_t i = 0; i < mQuirks.size(); i++) {
-        mQuirks.itemAt(i).writeToParcel(parcel);
-    }
     parcel->writeInt32(mCaps.size());
     for (size_t i = 0; i < mCaps.size(); i++) {
         mCaps.keyAt(i).writeToParcel(parcel);
@@ -208,86 +210,46 @@
     return -1;
 }
 
-MediaCodecInfo::MediaCodecInfo(AString name, bool encoder, const char *mime)
-    : mName(name),
-      mIsEncoder(encoder),
-      mHasSoleMime(false) {
-    if (mime != NULL) {
-        addMime(mime);
-        mHasSoleMime = true;
-    }
+MediaCodecInfo::MediaCodecInfo() {
 }
 
-status_t MediaCodecInfo::addMime(const char *mime) {
-    if (mHasSoleMime) {
-        ALOGE("Codec '%s' already had its type specified", mName.c_str());
-        return -EINVAL;
-    }
-    ssize_t ix = getCapabilityIndex(mime);
+void MediaCodecInfoWriter::setName(const char* name) {
+    mInfo->mName = name;
+}
+
+void MediaCodecInfoWriter::setOwner(const char* owner) {
+    mInfo->mOwner = owner;
+}
+
+void MediaCodecInfoWriter::setEncoder(bool isEncoder) {
+    mInfo->mIsEncoder = isEncoder;
+}
+
+std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>
+        MediaCodecInfoWriter::addMime(const char *mime) {
+    ssize_t ix = mInfo->getCapabilityIndex(mime);
     if (ix >= 0) {
-        mCurrentCaps = mCaps.valueAt(ix);
-    } else {
-        mCurrentCaps = new Capabilities();
-        mCaps.add(AString(mime), mCurrentCaps);
+        return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
+                new MediaCodecInfo::CapabilitiesWriter(
+                mInfo->mCaps.valueAt(ix).get()));
     }
-    return OK;
+    sp<MediaCodecInfo::Capabilities> caps = new MediaCodecInfo::Capabilities();
+    mInfo->mCaps.add(AString(mime), caps);
+    return std::unique_ptr<MediaCodecInfo::CapabilitiesWriter>(
+            new MediaCodecInfo::CapabilitiesWriter(caps.get()));
 }
 
-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);
+bool MediaCodecInfoWriter::removeMime(const char *mime) {
+    ssize_t ix = mInfo->getCapabilityIndex(mime);
     if (ix >= 0) {
-        mCaps.removeItemsAt(ix);
-        // mCurrentCaps will be removed when completed
+        mInfo->mCaps.removeItemsAt(ix);
+        return true;
     }
+    return false;
 }
 
-status_t MediaCodecInfo::initializeCapabilities(const sp<Capabilities> &caps) {
-    // TRICKY: copy data to mCurrentCaps as it is a reference to
-    // an element of the capabilites map.
-    mCurrentCaps->mColorFormats.clear();
-    mCurrentCaps->mColorFormats.appendVector(caps->mColorFormats);
-    mCurrentCaps->mProfileLevels.clear();
-    mCurrentCaps->mProfileLevels.appendVector(caps->mProfileLevels);
-    mCurrentCaps->mFlags = caps->mFlags;
-    mCurrentCaps->mDetails = caps->mDetails;
-    return OK;
-}
-
-void MediaCodecInfo::addQuirk(const char *name) {
-    if (!hasQuirk(name)) {
-        mQuirks.push(name);
-    }
-}
-
-void MediaCodecInfo::complete() {
-    mCurrentCaps = NULL;
-}
-
-void MediaCodecInfo::addDetail(const AString &key, const AString &value) {
-    mCurrentCaps->mDetails->setString(key.c_str(), value.c_str());
-}
-
-void MediaCodecInfo::addFeature(const AString &key, int32_t value) {
-    AString tag = "feature-";
-    tag.append(key);
-    mCurrentCaps->mDetails->setInt32(tag.c_str(), value);
-}
-
-void MediaCodecInfo::addFeature(const AString &key, const char *value) {
-    AString tag = "feature-";
-    tag.append(key);
-    mCurrentCaps->mDetails->setString(tag.c_str(), value);
+MediaCodecInfoWriter::MediaCodecInfoWriter(MediaCodecInfo* info) :
+    mInfo(info) {
 }
 
 }  // namespace android
diff --git a/media/libmedia/include/media/IMediaCodecService.h b/media/libmedia/include/media/IMediaCodecService.h
index da3c5a03..59fb1c0 100644
--- a/media/libmedia/include/media/IMediaCodecService.h
+++ b/media/libmedia/include/media/IMediaCodecService.h
@@ -22,6 +22,7 @@
 #include <binder/Parcel.h>
 #include <media/IDataSource.h>
 #include <media/IOMX.h>
+#include <media/IOMXStore.h>
 
 namespace android {
 
@@ -31,6 +32,7 @@
     DECLARE_META_INTERFACE(MediaCodecService);
 
     virtual sp<IOMX> getOMX() = 0;
+    virtual sp<IOMXStore> getOMXStore() = 0;
 };
 
 class BnMediaCodecService: public BnInterface<IMediaCodecService>
diff --git a/media/libmedia/include/media/IOMXStore.h b/media/libmedia/include/media/IOMXStore.h
index f739c3b..628db70 100644
--- a/media/libmedia/include/media/IOMXStore.h
+++ b/media/libmedia/include/media/IOMXStore.h
@@ -27,11 +27,6 @@
 #include <vector>
 #include <string>
 
-/*
-#include <OMX_Core.h>
-#include <OMX_Video.h>
-*/
-
 namespace android {
 
 using hardware::media::omx::V1_0::IOmxStore;
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index ef641d2..ab2cd24 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -18,6 +18,7 @@
 
 #define MEDIA_CODEC_INFO_H_
 
+#include <android-base/macros.h>
 #include <binder/Parcel.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AString.h>
@@ -36,6 +37,9 @@
 
 typedef KeyedVector<AString, AString> CodecSettings;
 
+struct MediaCodecInfoWriter;
+struct MediaCodecListWriter;
+
 struct MediaCodecInfo : public RefBase {
     struct ProfileLevel {
         uint32_t mProfile;
@@ -45,6 +49,8 @@
         }
     };
 
+    struct CapabilitiesWriter;
+
     struct Capabilities : public RefBase {
         enum {
             // decoder flags
@@ -77,72 +83,191 @@
         static sp<Capabilities> FromParcel(const Parcel &parcel);
         status_t writeToParcel(Parcel *parcel) const;
 
-        DISALLOW_EVIL_CONSTRUCTORS(Capabilities);
+        DISALLOW_COPY_AND_ASSIGN(Capabilities);
 
         friend struct MediaCodecInfo;
+        friend struct MediaCodecInfoWriter;
+        friend struct CapabilitiesWriter;
     };
 
-    // Use a subclass to allow setting fields on construction without allowing
-    // to do the same throughout the framework.
-    struct CapabilitiesBuilder : public Capabilities {
+    /**
+     * This class is used for modifying information inside a `Capabilities`
+     * object. An object of type `CapabilitiesWriter` can be obtained by calling
+     * `MediaCodecInfoWriter::addMime()` or
+     * `MediaCodecInfoWriter::updateMime()`.
+     */
+    struct CapabilitiesWriter {
+        /**
+         * Add a key-value pair to the list of details. If the key already
+         * exists, the old value will be replaced.
+         *
+         * A pair added by this function will be accessible by
+         * `Capabilities::getDetails()`. Call `AMessage::getString()` with the
+         * same key to retrieve the value.
+         *
+         * @param key The key.
+         * @param value The string value.
+         */
+        void addDetail(const char* key, const char* value);
+        /**
+         * Add a key-value pair to the list of details. If the key already
+         * exists, the old value will be replaced.
+         *
+         * A pair added by this function will be accessible by
+         * `Capabilities::getDetails()`. Call `AMessage::getInt32()` with the
+         * same key to retrieve the value.
+         *
+         * @param key The key.
+         * @param value The `int32_t` value.
+         */
+        void addDetail(const char* key, int32_t value);
+        /**
+         * Add a profile-level pair. If this profile-level pair already exists,
+         * it will be ignored.
+         *
+         * @param profile The "profile" component.
+         * @param level The "level" component.
+         */
         void addProfileLevel(uint32_t profile, uint32_t level);
+        /**
+         * Add a color format. If this color format already exists, it will be
+         * ignored.
+         *
+         * @param format The color format.
+         */
         void addColorFormat(uint32_t format);
+        /**
+         * Add flags. The underlying operation is bitwise-or. In other words,
+         * bits that have already been set will be ignored.
+         *
+         * @param flags The additional flags.
+         */
         void addFlags(uint32_t flags);
+    private:
+        /**
+         * The associated `Capabilities` object.
+         */
+        Capabilities* mCap;
+        /**
+         * Construct a writer for the given `Capabilities` object.
+         *
+         * @param cap The `Capabilities` object to be written to.
+         */
+        CapabilitiesWriter(Capabilities* cap);
+
+        friend MediaCodecInfoWriter;
     };
 
     bool isEncoder() const;
-    bool hasQuirk(const char *name) const;
     void getSupportedMimes(Vector<AString> *mimes) const;
     const sp<Capabilities> getCapabilitiesFor(const char *mime) const;
     const char *getCodecName() const;
 
     /**
+     * Return the name of the service that hosts the codec. This value is not
+     * visible at the Java level.
+     *
+     * Currently, this is the "instance name" of the IOmx service.
+     */
+    const char *getOwnerName() const;
+
+    /**
      * Serialization over Binder
      */
     static sp<MediaCodecInfo> FromParcel(const Parcel &parcel);
     status_t writeToParcel(Parcel *parcel) const;
 
 private:
-    // variable set only in constructor - these are accessed by MediaCodecList
-    // to avoid duplication of same variables
     AString mName;
+    AString mOwner;
     bool mIsEncoder;
-    bool mHasSoleMime; // was initialized with mime
-
-    Vector<AString> mQuirks;
     KeyedVector<AString, sp<Capabilities> > mCaps;
 
-    sp<Capabilities> mCurrentCaps; // currently initalized capabilities
-
     ssize_t getCapabilityIndex(const char *mime) const;
 
-    /* Methods used by MediaCodecList to construct the info
-     * object from XML.
-     *
-     * After info object is created:
-     * - additional quirks can be added
-     * - additional mimes can be added
-     *   - OMX codec capabilities can be set for the current mime-type
-     *   - a capability detail can be set for the current mime-type
-     *   - a feature can be set for the current mime-type
-     *   - info object can be completed when parsing of a mime-type is done
+    /**
+     * Construct an `MediaCodecInfo` object. After the construction, its
+     * information can be set via an `MediaCodecInfoWriter` object obtained from
+     * `MediaCodecListWriter::addMediaCodecInfo()`.
      */
-    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);
+    MediaCodecInfo();
 
-    status_t initializeCapabilities(const sp<Capabilities> &caps);
-    void addDetail(const AString &key, const AString &value);
-    void addFeature(const AString &key, int32_t value);
-    void addFeature(const AString &key, const char *value);
-    void removeMime(const char *mime);
-    void complete();
+    DISALLOW_COPY_AND_ASSIGN(MediaCodecInfo);
 
-    DISALLOW_EVIL_CONSTRUCTORS(MediaCodecInfo);
-
-    friend struct MediaCodecList;
     friend class MediaCodecListOverridesTest;
+    friend struct MediaCodecInfoWriter;
+    friend struct MediaCodecListWriter;
+};
+
+/**
+ * This class is to be used by a `MediaCodecListBuilderBase` instance to
+ * populate information inside the associated `MediaCodecInfo` object.
+ *
+ * The only place where an instance of `MediaCodecInfoWriter` can be constructed
+ * is `MediaCodecListWriter::addMediaCodecInfo()`. A `MediaCodecListBuilderBase`
+ * instance should call `MediaCodecListWriter::addMediaCodecInfo()` on the given
+ * `MediaCodecListWriter` object given as an input to
+ * `MediaCodecListBuilderBase::buildMediaCodecList()`.
+ */
+struct MediaCodecInfoWriter {
+    /**
+     * Set the name of the codec.
+     *
+     * @param name The new name.
+     */
+    void setName(const char* name);
+    /**
+     * Set the owner name of the codec.
+     *
+     * This "owner name" is the name of the `IOmx` instance that supports this
+     * codec.
+     *
+     * @param owner The new owner name.
+     */
+    void setOwner(const char* owner);
+    /**
+     * Set whether this codec is an encoder or a decoder.
+     *
+     * @param isEncoder Whether this codec is an encoder or a decoder.
+     */
+    void setEncoder(bool isEncoder = true);
+    /**
+     * Add a mime to an indexed list and return a `CapabilitiesWriter` object
+     * that can be used for modifying the associated `Capabilities`.
+     *
+     * If the mime already exists, this function will return the
+     * `CapabilitiesWriter` associated with the mime.
+     *
+     * @param[in] mime The name of a new mime to add.
+     * @return writer The `CapabilitiesWriter` object for modifying the
+     * `Capabilities` associated with the mime. `writer` will be valid
+     * regardless of whether `mime` already exists or not.
+     */
+    std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> addMime(
+            const char* mime);
+    /**
+     * Remove a mime.
+     *
+     * @param mime The name of the mime to remove.
+     * @return `true` if `mime` is removed; `false` if `mime` is not found.
+     */
+    bool removeMime(const char* mime);
+private:
+    /**
+     * The associated `MediaCodecInfo`.
+     */
+    MediaCodecInfo* mInfo;
+    /**
+     * Construct the `MediaCodecInfoWriter` object associated with the given
+     * `MediaCodecInfo` object.
+     *
+     * @param info The underlying `MediaCodecInfo` object.
+     */
+    MediaCodecInfoWriter(MediaCodecInfo* info);
+
+    DISALLOW_COPY_AND_ASSIGN(MediaCodecInfoWriter);
+
+    friend struct MediaCodecListWriter;
 };
 
 }  // namespace android
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 43881b3..47a147e 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -120,6 +120,8 @@
         // key as part of constructor
         dst->mPid = this->mPid;
         dst->mUid = this->mUid;
+        dst->mPkgName = this->mPkgName;
+        dst->mPkgVersionCode = this->mPkgVersionCode;
         dst->mSessionID = this->mSessionID;
         dst->mTimestamp = this->mTimestamp;
         dst->mFinalized = this->mFinalized;
@@ -201,6 +203,24 @@
     return mUid;
 }
 
+MediaAnalyticsItem &MediaAnalyticsItem::setPkgName(AString pkgName) {
+    mPkgName = pkgName;
+    return *this;
+}
+
+AString MediaAnalyticsItem::getPkgName() const {
+    return mPkgName;
+}
+
+MediaAnalyticsItem &MediaAnalyticsItem::setPkgVersionCode(int32_t pkgVersionCode) {
+    mPkgVersionCode = pkgVersionCode;
+    return *this;
+}
+
+int32_t MediaAnalyticsItem::getPkgVersionCode() const {
+    return mPkgVersionCode;
+}
+
 // this key is for the overall record -- "codec", "player", "drm", etc
 MediaAnalyticsItem &MediaAnalyticsItem::setKey(MediaAnalyticsItem::Key key) {
     mKey = key;
@@ -263,11 +283,29 @@
         i = mPropCount++;
         prop = &mProps[i];
         prop->setName(name, len);
+        prop->mType = kTypeNone;        // make caller set type info
     }
 
     return prop;
 }
 
+// used within the summarizers; return whether property existed
+bool MediaAnalyticsItem::removeProp(const char *name) {
+    size_t len = strlen(name);
+    size_t i = findPropIndex(name, len);
+    if (i < mPropCount) {
+        Prop *prop = &mProps[i];
+        clearProp(prop);
+        if (i != mPropCount-1) {
+            // in the middle, bring last one down to fill gap
+            mProps[i] = mProps[mPropCount-1];
+        }
+        mPropCount--;
+        return true;
+    }
+    return false;
+}
+
 // set the values
 void MediaAnalyticsItem::setInt32(MediaAnalyticsItem::Attr name, int32_t value) {
     Prop *prop = allocateProp(name);
@@ -568,6 +606,10 @@
     // into 'this' object
     // .. we make a copy of the string to put away.
     mKey = data.readCString();
+    mPid = data.readInt32();
+    mUid = data.readInt32();
+    mPkgName = data.readCString();
+    mPkgVersionCode = data.readInt32();
     mSessionID = data.readInt64();
     mFinalized = data.readInt32();
     mTimestamp = data.readInt64();
@@ -611,6 +653,10 @@
 
 
     data->writeCString(mKey.c_str());
+    data->writeInt32(mPid);
+    data->writeInt32(mUid);
+    data->writeCString(mPkgName.c_str());
+    data->writeInt32(mPkgVersionCode);
     data->writeInt64(mSessionID);
     data->writeInt32(mFinalized);
     data->writeInt64(mTimestamp);
@@ -651,21 +697,54 @@
 
 
 AString MediaAnalyticsItem::toString() {
+   return toString(-1);
+}
 
-    AString result = "(";
+AString MediaAnalyticsItem::toString(int version) {
+
+    // v0 : released with 'o'
+    // v1 : bug fix (missing pid/finalized separator),
+    //      adds apk name, apk version code
+
+    if (version <= PROTO_FIRST) {
+        // default to original v0 format, until proper parsers are in place
+        version = PROTO_V0;
+    } else if (version > PROTO_LAST) {
+        version = PROTO_LAST;
+    }
+
+    AString result;
     char buffer[512];
 
+    if (version == PROTO_V0) {
+        result = "(";
+    } else {
+        snprintf(buffer, sizeof(buffer), "[%d:", version);
+        result.append(buffer);
+    }
+
     // same order as we spill into the parcel, although not required
     // key+session are our primary matching criteria
-    //RBE ALOGD("mKey.c_str");
     result.append(mKey.c_str());
-    //RBE ALOGD("post-mKey.c_str");
     result.append(":");
     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
     result.append(buffer);
 
-    // we need these internally, but don't want to upload them
-    snprintf(buffer, sizeof(buffer), "%d:%d", mUid, mPid);
+    snprintf(buffer, sizeof(buffer), "%d:", mUid);
+    result.append(buffer);
+
+    if (version >= PROTO_V1) {
+        result.append(mPkgName);
+        snprintf(buffer, sizeof(buffer), ":%d:", mPkgVersionCode);
+        result.append(buffer);
+    }
+
+    // in 'o' (v1) , the separator between pid and finalized was omitted
+    if (version <= PROTO_V0) {
+        snprintf(buffer, sizeof(buffer), "%d", mPid);
+    } else {
+        snprintf(buffer, sizeof(buffer), "%d:", mPid);
+    }
     result.append(buffer);
 
     snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
@@ -713,7 +792,11 @@
             result.append(buffer);
     }
 
-    result.append(")");
+    if (version == PROTO_V0) {
+        result.append(")");
+    } else {
+        result.append("]");
+    }
 
     return result;
 }
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index dc501b2..41b9658 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -75,6 +75,14 @@
         typedef const char *Attr;
 
 
+        enum {
+            PROTO_V0 = 0,
+            PROTO_FIRST = PROTO_V0,
+            PROTO_V1 = 1,
+            PROTO_LAST = PROTO_V1,
+        };
+
+
     public:
 
         // access functions for the class
@@ -161,11 +169,18 @@
         MediaAnalyticsItem &setUid(uid_t);
         uid_t getUid() const;
 
+        MediaAnalyticsItem &setPkgName(AString);
+        AString getPkgName() const;
+
+        MediaAnalyticsItem &setPkgVersionCode(int32_t);
+        int32_t getPkgVersionCode() const;
+
         // our serialization code for binder calls
         int32_t writeToParcel(Parcel *);
         int32_t readFromParcel(const Parcel&);
 
         AString toString();
+        AString toString(int version);
 
         // are we collecting analytics data
         static bool isEnabled();
@@ -188,6 +203,8 @@
         // to help validate that A doesn't mess with B's records
         pid_t     mPid;
         uid_t     mUid;
+        AString   mPkgName;
+        int32_t   mPkgVersionCode;
 
         // let's reuse a binder connection
         static sp<IMediaAnalyticsService> sAnalyticsService;
@@ -228,6 +245,7 @@
         size_t findPropIndex(const char *name, size_t len);
         Prop *findProp(const char *name);
         Prop *allocateProp(const char *name);
+        bool removeProp(const char *name);
 
         size_t mPropCount;
         size_t mPropSize;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 0fc1aa7..dc29761 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -69,6 +69,7 @@
       mPlayer(new NuPlayer(pid)),
       mPlayerFlags(0),
       mAnalyticsItem(NULL),
+      mClientUid(-1),
       mAtEOS(false),
       mLooping(false),
       mAutoLoop(false) {
@@ -109,6 +110,10 @@
 
 status_t NuPlayerDriver::setUID(uid_t uid) {
     mPlayer->setUID(uid);
+    mClientUid = uid;
+    if (mAnalyticsItem) {
+        mAnalyticsItem->setUid(mClientUid);
+    }
 
     return OK;
 }
@@ -601,6 +606,7 @@
         mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
         if (mAnalyticsItem) {
             mAnalyticsItem->generateSessionID();
+            mAnalyticsItem->setUid(mClientUid);
         }
     } else {
         ALOGV("did not have anything to record");
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index c5ddcb0..d0cf1dd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -132,11 +132,13 @@
     uint32_t mPlayerFlags;
 
     MediaAnalyticsItem *mAnalyticsItem;
+    uid_t mClientUid;
 
     bool mAtEOS;
     bool mLooping;
     bool mAutoLoop;
 
+
     void updateMetrics(const char *where);
     void logMetrics(const char *where);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 483a9ff..bd866cb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1152,7 +1152,18 @@
             return writtenAudioDurationUs - (mediaUs - mAudioFirstAnchorTimeMediaUs);
         }
     }
-    return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);
+
+    const int64_t audioSinkPlayedUs = mAudioSink->getPlayedOutDurationUs(nowUs);
+    int64_t pendingUs = writtenAudioDurationUs - audioSinkPlayedUs;
+    if (pendingUs < 0) {
+        // This shouldn't happen unless the timestamp is stale.
+        ALOGW("%s: pendingUs %lld < 0, clamping to zero, potential resume after pause "
+                "writtenAudioDurationUs: %lld, audioSinkPlayedUs: %lld",
+                __func__, (long long)pendingUs,
+                (long long)writtenAudioDurationUs, (long long)audioSinkPlayedUs);
+        pendingUs = 0;
+    }
+    return pendingUs;
 }
 
 int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 0e60b2e..c9394bb 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -62,9 +62,6 @@
 #include "include/SharedMemoryBuffer.h"
 #include <media/stagefright/omx/OMXUtils.h>
 
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <android/hidl/memory/1.0/IMemory.h>
-
 namespace android {
 
 using binder::Status;
@@ -4322,9 +4319,14 @@
         h264type.bUseHadamard = OMX_TRUE;
         h264type.nRefFrames = 2;
         h264type.nBFrames = mLatency == 0 ? 1 : std::min(1U, mLatency - 1);
+
+        // disable B-frames until MPEG4Writer can guarantee finalizing files with B-frames
+        h264type.nRefFrames = 1;
+        h264type.nBFrames = 0;
+
         h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate, h264type.nBFrames);
         h264type.nAllowedPictureTypes =
-            OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP | OMX_VIDEO_PictureTypeB;
+            OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
         h264type.nRefIdx10ActiveMinus1 = 0;
         h264type.nRefIdx11ActiveMinus1 = 0;
         h264type.bEntropyCodingCABAC = OMX_TRUE;
@@ -6339,19 +6341,10 @@
 
     CHECK(mCodec->mOMXNode == NULL);
 
-    OMXClient client;
-    bool trebleFlag;
-    if (client.connect(&trebleFlag) != OK) {
-        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
-        return false;
-    }
-    mCodec->setTrebleFlag(trebleFlag);
-
-    sp<IOMX> omx = client.interface();
-
     sp<AMessage> notify = new AMessage(kWhatOMXDied, mCodec);
 
     Vector<AString> matchingCodecs;
+    Vector<AString> owners;
 
     AString mime;
 
@@ -6359,9 +6352,28 @@
     int32_t encoder = false;
     if (msg->findString("componentName", &componentName)) {
         sp<IMediaCodecList> list = MediaCodecList::getInstance();
-        if (list != NULL && list->findCodecByName(componentName.c_str()) >= 0) {
-            matchingCodecs.add(componentName);
+        if (list == nullptr) {
+            ALOGE("Unable to obtain MediaCodecList while "
+                    "attempting to create codec \"%s\"",
+                    componentName.c_str());
+            return false;
         }
+        ssize_t index = list->findCodecByName(componentName.c_str());
+        if (index < 0) {
+            ALOGE("Unable to find codec \"%s\"",
+                    componentName.c_str());
+            return false;
+        }
+        sp<MediaCodecInfo> info = list->getCodecInfo(index);
+        if (info == nullptr) {
+            ALOGE("Unexpected error (index out-of-bound) while "
+                    "retrieving information for codec \"%s\"",
+                    componentName.c_str());
+            return false;
+        }
+        matchingCodecs.add(info->getCodecName());
+        owners.add(info->getOwnerName() == nullptr ?
+                "default" : info->getOwnerName());
     } else {
         CHECK(msg->findString("mime", &mime));
 
@@ -6373,10 +6385,12 @@
                 mime.c_str(),
                 encoder, // createEncoder
                 0,       // flags
-                &matchingCodecs);
+                &matchingCodecs,
+                &owners);
     }
 
     sp<CodecObserver> observer = new CodecObserver;
+    sp<IOMX> omx;
     sp<IOMXNode> omxNode;
 
     status_t err = NAME_NOT_FOUND;
@@ -6384,6 +6398,14 @@
             ++matchIndex) {
         componentName = matchingCodecs[matchIndex];
 
+        OMXClient client;
+        bool trebleFlag;
+        if (client.connect(owners[matchIndex].c_str(), &trebleFlag) != OK) {
+            mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
+            return false;
+        }
+        omx = client.interface();
+
         pid_t tid = gettid();
         int prevPriority = androidGetThreadPriority(tid);
         androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
@@ -6391,6 +6413,7 @@
         androidSetThreadPriority(tid, prevPriority);
 
         if (err == OK) {
+            mCodec->setTrebleFlag(trebleFlag);
             break;
         } else {
             ALOGW("Allocating component '%s' failed, try next one.", componentName.c_str());
@@ -8214,16 +8237,15 @@
 }
 
 status_t ACodec::queryCapabilities(
-        const AString &name, const AString &mime, bool isEncoder,
-        sp<MediaCodecInfo::Capabilities> *caps) {
-    (*caps).clear();
-    const char *role = GetComponentRole(isEncoder, mime.c_str());
+        const char* owner, const char* name, const char* mime, bool isEncoder,
+        MediaCodecInfo::CapabilitiesWriter* caps) {
+    const char *role = GetComponentRole(isEncoder, mime);
     if (role == NULL) {
         return BAD_VALUE;
     }
 
     OMXClient client;
-    status_t err = client.connect();
+    status_t err = client.connect(owner);
     if (err != OK) {
         return err;
     }
@@ -8232,7 +8254,7 @@
     sp<CodecObserver> observer = new CodecObserver;
     sp<IOMXNode> omxNode;
 
-    err = omx->allocateNode(name.c_str(), observer, &omxNode);
+    err = omx->allocateNode(name, observer, &omxNode);
     if (err != OK) {
         client.disconnect();
         return err;
@@ -8245,8 +8267,7 @@
         return err;
     }
 
-    sp<MediaCodecInfo::CapabilitiesBuilder> builder = new MediaCodecInfo::CapabilitiesBuilder();
-    bool isVideo = mime.startsWithIgnoreCase("video/");
+    bool isVideo = strncasecmp(mime, "video/", 6) == 0;
 
     if (isVideo) {
         OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
@@ -8261,22 +8282,22 @@
             if (err != OK) {
                 break;
             }
-            builder->addProfileLevel(param.eProfile, param.eLevel);
+            caps->addProfileLevel(param.eProfile, param.eLevel);
 
             // AVC components may not list the constrained profiles explicitly, but
             // decoders that support a profile also support its constrained version.
             // Encoders must explicitly support constrained profiles.
-            if (!isEncoder && mime.equalsIgnoreCase(MEDIA_MIMETYPE_VIDEO_AVC)) {
+            if (!isEncoder && strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) == 0) {
                 if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
-                    builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
+                    caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
                 } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
-                    builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
+                    caps->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
                 }
             }
 
             if (index == kMaxIndicesToCheck) {
                 ALOGW("[%s] stopping checking profiles after %u: %x/%x",
-                        name.c_str(), index,
+                        name, index,
                         param.eProfile, param.eLevel);
             }
         }
@@ -8300,17 +8321,17 @@
             if (IsFlexibleColorFormat(
                     omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
                     &flexibleEquivalent)) {
-                builder->addColorFormat(flexibleEquivalent);
+                caps->addColorFormat(flexibleEquivalent);
             }
-            builder->addColorFormat(portFormat.eColorFormat);
+            caps->addColorFormat(portFormat.eColorFormat);
 
             if (index == kMaxIndicesToCheck) {
                 ALOGW("[%s] stopping checking formats after %u: %s(%x)",
-                        name.c_str(), index,
+                        name, index,
                         asString(portFormat.eColorFormat), portFormat.eColorFormat);
             }
         }
-    } else if (mime.equalsIgnoreCase(MEDIA_MIMETYPE_AUDIO_AAC)) {
+    } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC) == 0) {
         // More audio codecs if they have profiles.
         OMX_AUDIO_PARAM_ANDROID_PROFILETYPE param;
         InitOMXParams(&param);
@@ -8324,11 +8345,11 @@
                 break;
             }
             // For audio, level is ignored.
-            builder->addProfileLevel(param.eProfile, 0 /* level */);
+            caps->addProfileLevel(param.eProfile, 0 /* level */);
 
             if (index == kMaxIndicesToCheck) {
                 ALOGW("[%s] stopping checking profiles after %u: %x",
-                        name.c_str(), index,
+                        name, index,
                         param.eProfile);
             }
         }
@@ -8336,7 +8357,7 @@
         // NOTE: Without Android extensions, OMX does not provide a way to query
         // AAC profile support
         if (param.nProfileIndex == 0) {
-            ALOGW("component %s doesn't support profile query.", name.c_str());
+            ALOGW("component %s doesn't support profile query.", name);
         }
     }
 
@@ -8345,14 +8366,14 @@
         if (omxNode->configureVideoTunnelMode(
                 kPortIndexOutput, OMX_TRUE, 0, &sidebandHandle) == OK) {
             // tunneled playback includes adaptive playback
-            builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
+            caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback
                     | MediaCodecInfo::Capabilities::kFlagSupportsTunneledPlayback);
         } else if (omxNode->setPortMode(
                 kPortIndexOutput, IOMX::kPortModeDynamicANWBuffer) == OK ||
                 omxNode->prepareForAdaptivePlayback(
                 kPortIndexOutput, OMX_TRUE,
                 1280 /* width */, 720 /* height */) == OK) {
-            builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
+            caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsAdaptivePlayback);
         }
     }
 
@@ -8364,11 +8385,10 @@
         if (omxNode->getConfig(
                 (OMX_INDEXTYPE)OMX_IndexConfigAndroidIntraRefresh,
                 &params, sizeof(params)) == OK) {
-            builder->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
+            caps->addFlags(MediaCodecInfo::Capabilities::kFlagSupportsIntraRefresh);
         }
     }
 
-    *caps = builder;
     omxNode->freeNode();
     client.disconnect();
     return OK;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 348abf8..c8d4e4a 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -51,6 +51,7 @@
         "NuCachedSource2.cpp",
         "NuMediaExtractor.cpp",
         "OMXClient.cpp",
+        "OmxInfoBuilder.cpp",
         "OggExtractor.cpp",
         "SampleIterator.cpp",
         "SampleTable.cpp",
@@ -93,6 +94,7 @@
         "libnativewindow",
 
         "libmedia_helper",
+        "libstagefright_omx_utils",
         "libstagefright_flacdec",
         "libstagefright_foundation",
         "libstagefright_xmlparser",
@@ -114,7 +116,6 @@
         "libstagefright_aacenc",
         "libstagefright_matroska",
         "libstagefright_mediafilter",
-        "libstagefright_omx_utils",
         "libstagefright_webm",
         "libstagefright_timedtext",
         "libvpx",
@@ -124,9 +125,12 @@
         "libFLAC",
     ],
 
-    export_shared_lib_headers: ["libmedia"],
+    export_shared_lib_headers: [
+        "libmedia",
+        "android.hidl.allocator@1.0",
+    ],
+
     export_include_dirs: [
-        ".",
         "include",
     ],
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 5ef0f56..6f59fac 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -5224,6 +5224,7 @@
 
 void MPEG4Extractor::populateMetrics() {
     ALOGV("MPEG4Extractor::populateMetrics");
+    // write into mAnalyticsItem
 }
 
 static bool LegacySniffMPEG4(
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 98d101a..3cdb49e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -429,22 +429,6 @@
 }
 
 // static
-status_t MediaCodec::QueryCapabilities(
-        const AString &name, const AString &mime, bool isEncoder,
-        sp<MediaCodecInfo::Capabilities> *caps /* nonnull */) {
-    // TRICKY: this method is used by MediaCodecList/Info during its
-    // initialization. As such, we cannot create a MediaCodec instance
-    // because that requires an initialized MediaCodecList.
-
-    sp<CodecBase> codec = GetCodecBase(name);
-    if (codec == NULL) {
-        return NAME_NOT_FOUND;
-    }
-
-    return codec->queryCapabilities(name, mime, isEncoder, caps);
-}
-
-// static
 sp<PersistentSurface> MediaCodec::CreatePersistentInputSurface() {
     OMXClient client;
     if (client.connect() != OK) {
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 1dcba29..4652594 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -24,17 +24,17 @@
 
 #include <media/IMediaCodecList.h>
 #include <media/IMediaPlayerService.h>
-#include <media/IResourceManagerService.h>
+#include <media/IMediaCodecService.h>
 #include <media/MediaCodecInfo.h>
-#include <media/MediaResourcePolicy.h>
+#include <media/MediaDefs.h>
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/ACodec.h>
-#include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaErrors.h>
-#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OmxInfoBuilder.h>
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 
 #include <sys/stat.h>
 #include <utils/threads.h>
@@ -42,20 +42,20 @@
 #include <cutils/properties.h>
 #include <expat.h>
 
+#include <algorithm>
+
 namespace android {
 
-static Mutex sInitMutex;
+namespace {
 
-static bool parseBoolean(const char *s) {
-    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
-        return true;
-    }
-    char *end;
-    unsigned long res = strtoul(s, &end, 10);
-    return *s != '\0' && *end == '\0' && res > 0;
-}
+Mutex sInitMutex;
 
-static bool isProfilingNeeded() {
+Mutex sRemoteInitMutex;
+
+constexpr const char* kProfilingResults =
+        MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
+
+bool isProfilingNeeded() {
     int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
     if (value == 0) {
         return false;
@@ -78,6 +78,10 @@
     return profilingNeeded;
 }
 
+OmxInfoBuilder sOmxInfoBuilder;
+
+}  // unnamed namespace
+
 // static
 sp<IMediaCodecList> MediaCodecList::sCodecList;
 
@@ -86,42 +90,42 @@
     ALOGV("Enter profilerThreadWrapper.");
     remove(kProfilingResults);  // remove previous result so that it won't be loaded to
                                 // the new MediaCodecList
-    MediaCodecList *codecList = new MediaCodecList();
+    sp<MediaCodecList> codecList(new MediaCodecList(&sOmxInfoBuilder));
     if (codecList->initCheck() != OK) {
         ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
-        delete codecList;
-        return NULL;
+        return nullptr;
     }
 
-    Vector<sp<MediaCodecInfo>> infos;
-    for (size_t i = 0; i < codecList->countCodecs(); ++i) {
-        infos.push_back(codecList->getCodecInfo(i));
-    }
+    const auto& infos = codecList->mCodecInfos;
     ALOGV("Codec profiling started.");
-    profileCodecs(infos);
+    profileCodecs(infos, kProfilingResults);
     ALOGV("Codec profiling completed.");
-    codecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
+    codecList = new MediaCodecList(&sOmxInfoBuilder);
+    if (codecList->initCheck() != OK) {
+        ALOGW("Failed to parse profiling results.");
+        return nullptr;
+    }
 
     {
         Mutex::Autolock autoLock(sInitMutex);
         sCodecList = codecList;
     }
-    return NULL;
+    return nullptr;
 }
 
 // static
 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
     Mutex::Autolock autoLock(sInitMutex);
 
-    if (sCodecList == NULL) {
-        MediaCodecList *codecList = new MediaCodecList;
+    if (sCodecList == nullptr) {
+        MediaCodecList *codecList = new MediaCodecList(&sOmxInfoBuilder);
         if (codecList->initCheck() == OK) {
             sCodecList = codecList;
 
             if (isProfilingNeeded()) {
                 ALOGV("Codec profiling needed, will be run in separated thread.");
                 pthread_t profiler;
-                if (pthread_create(&profiler, NULL, profilerThreadWrapper, NULL) != 0) {
+                if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
                     ALOGW("Failed to create thread for codec profiling.");
                 }
             }
@@ -134,8 +138,6 @@
     return sCodecList;
 }
 
-static Mutex sRemoteInitMutex;
-
 sp<IMediaCodecList> MediaCodecList::sRemoteList;
 
 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
@@ -149,19 +151,19 @@
 // static
 sp<IMediaCodecList> MediaCodecList::getInstance() {
     Mutex::Autolock _l(sRemoteInitMutex);
-    if (sRemoteList == NULL) {
+    if (sRemoteList == nullptr) {
         sp<IBinder> binder =
             defaultServiceManager()->getService(String16("media.player"));
         sp<IMediaPlayerService> service =
             interface_cast<IMediaPlayerService>(binder);
-        if (service.get() != NULL) {
+        if (service.get() != nullptr) {
             sRemoteList = service->getCodecList();
-            if (sRemoteList != NULL) {
+            if (sRemoteList != nullptr) {
                 sBinderDeathObserver = new BinderDeathObserver();
                 binder->linkToDeath(sBinderDeathObserver.get());
             }
         }
-        if (sRemoteList == NULL) {
+        if (sRemoteList == nullptr) {
             // if failed to get remote list, create local list
             sRemoteList = getLocalInstance();
         }
@@ -169,175 +171,11 @@
     return sRemoteList;
 }
 
-// Treblized media codec list will be located in /odm/etc or /vendor/etc.
-static const char *kConfigLocationList[] =
-        {"/odm/etc", "/vendor/etc", "/etc"};
-static const int kConfigLocationListSize =
-        (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
-#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 128
-
-static bool findMediaCodecListFileFullPath(const char *file_name, char *out_path) {
-    for (int i = 0; i < kConfigLocationListSize; i++) {
-        snprintf(out_path,
-                 MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH,
-                 "%s/%s",
-                 kConfigLocationList[i],
-                 file_name);
-        struct stat file_stat;
-        if (stat(out_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-MediaCodecList::MediaCodecList()
-    : mInitCheck(NO_INIT),
-      mUpdate(false),
-      mGlobalSettings(new AMessage()) {
-    char config_file_path[MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH];
-    if (findMediaCodecListFileFullPath("media_codecs.xml", config_file_path)) {
-        parseTopLevelXMLFile(config_file_path);
-    }
-    if (findMediaCodecListFileFullPath("media_codecs_performance.xml",
-                                       config_file_path)) {
-        parseTopLevelXMLFile(config_file_path, true/* ignore_errors */);
-    }
-    parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
-}
-
-void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
-    // get href_base
-    const char *href_base_end = strrchr(codecs_xml, '/');
-    if (href_base_end != NULL) {
-        mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
-    }
-
-    mInitCheck = OK; // keeping this here for safety
-    mCurrentSection = SECTION_TOPLEVEL;
-    mDepth = 0;
-
-    OMXClient client;
-    mInitCheck = client.connect();
-    if (mInitCheck != OK) {
-        return;  // this may fail if IMediaPlayerService is not available.
-    }
-    parseXMLFile(codecs_xml);
-
-    if (mInitCheck != OK) {
-        if (ignore_errors) {
-            mInitCheck = OK;
-            return;
-        }
-        mCodecInfos.clear();
-        return;
-    }
-
-    Vector<MediaResourcePolicy> policies;
-    AString value;
-    if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
-        policies.push_back(
-                MediaResourcePolicy(
-                        String8(kPolicySupportsMultipleSecureCodecs),
-                        String8(value.c_str())));
-    }
-    if (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
-        policies.push_back(
-                MediaResourcePolicy(
-                        String8(kPolicySupportsSecureWithNonSecureCodec),
-                        String8(value.c_str())));
-    }
-    if (policies.size() > 0) {
-        sp<IServiceManager> sm = defaultServiceManager();
-        sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
-        sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
-        if (service == NULL) {
-            ALOGE("MediaCodecList: failed to get ResourceManagerService");
-        } else {
-            service->config(policies);
-        }
-    }
-
-    for (size_t i = mCodecInfos.size(); i > 0;) {
-        i--;
-        const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-        if (info.mCaps.size() == 0) {
-            // No types supported by this component???
-            ALOGW("Component %s does not support any type of media?",
-                  info.mName.c_str());
-
-            mCodecInfos.removeAt(i);
-#if LOG_NDEBUG == 0
-        } else {
-            for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) {
-                AString mime = info.mCaps.keyAt(type_ix);
-                const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix);
-
-                ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(),
-                        caps->getDetails()->debugString().c_str());
-                ALOGV("    flags=%d", caps->getFlags());
-                {
-                    Vector<uint32_t> colorFormats;
-                    caps->getSupportedColorFormats(&colorFormats);
-                    AString nice;
-                    for (size_t ix = 0; ix < colorFormats.size(); ix++) {
-                        if (ix > 0) {
-                            nice.append(", ");
-                        }
-                        nice.append(colorFormats.itemAt(ix));
-                    }
-                    ALOGV("    colors=[%s]", nice.c_str());
-                }
-                {
-                    Vector<MediaCodecInfo::ProfileLevel> profileLevels;
-                    caps->getSupportedProfileLevels(&profileLevels);
-                    AString nice;
-                    for (size_t ix = 0; ix < profileLevels.size(); ix++) {
-                        if (ix > 0) {
-                            nice.append(", ");
-                        }
-                        const MediaCodecInfo::ProfileLevel &pl =
-                            profileLevels.itemAt(ix);
-                        nice.append(pl.mProfile);
-                        nice.append("/");
-                        nice.append(pl.mLevel);
-                    }
-                    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
-        }
-    }
-
-#if 0
-    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
-        const CodecInfo &info = mCodecInfos.itemAt(i);
-
-        AString line = info.mName;
-        line.append(" supports ");
-        for (size_t j = 0; j < mTypes.size(); ++j) {
-            uint32_t value = mTypes.valueAt(j);
-
-            if (info.mTypes & (1ul << value)) {
-                line.append(mTypes.keyAt(j));
-                line.append(" ");
-            }
-        }
-
-        ALOGI("%s", line.c_str());
-    }
-#endif
+MediaCodecList::MediaCodecList(MediaCodecListBuilderBase* builder) {
+    mGlobalSettings = new AMessage();
+    mCodecInfos.clear();
+    MediaCodecListWriter writer(this);
+    mInitCheck = builder->buildMediaCodecList(&writer);
 }
 
 MediaCodecList::~MediaCodecList() {
@@ -347,531 +185,21 @@
     return mInitCheck;
 }
 
-void MediaCodecList::parseXMLFile(const char *path) {
-    FILE *file = fopen(path, "r");
-
-    if (file == NULL) {
-        ALOGW("unable to open media codecs configuration xml file: %s", path);
-        mInitCheck = NAME_NOT_FOUND;
-        return;
-    }
-
-    XML_Parser parser = ::XML_ParserCreate(NULL);
-    CHECK(parser != NULL);
-
-    ::XML_SetUserData(parser, this);
-    ::XML_SetElementHandler(
-            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
-
-    const int BUFF_SIZE = 512;
-    while (mInitCheck == OK) {
-        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
-        if (buff == NULL) {
-            ALOGE("failed in call to XML_GetBuffer()");
-            mInitCheck = UNKNOWN_ERROR;
-            break;
-        }
-
-        int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
-        if (bytes_read < 0) {
-            ALOGE("failed in call to read");
-            mInitCheck = ERROR_IO;
-            break;
-        }
-
-        XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
-        if (status != XML_STATUS_OK) {
-            ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
-            mInitCheck = ERROR_MALFORMED;
-            break;
-        }
-
-        if (bytes_read == 0) {
-            break;
-        }
-    }
-
-    ::XML_ParserFree(parser);
-
-    fclose(file);
-    file = NULL;
+MediaCodecListWriter::MediaCodecListWriter(MediaCodecList* list) :
+    mList(list) {
 }
 
-// static
-void MediaCodecList::StartElementHandlerWrapper(
-        void *me, const char *name, const char **attrs) {
-    static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
+void MediaCodecListWriter::addGlobalSetting(
+        const char* key, const char* value) {
+    mList->mGlobalSettings->setString(key, value);
 }
 
-// static
-void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
-    static_cast<MediaCodecList *>(me)->endElementHandler(name);
-}
-
-status_t MediaCodecList::includeXMLFile(const char **attrs) {
-    const char *href = NULL;
-    size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "href")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            href = attrs[i + 1];
-            ++i;
-        } else {
-            return -EINVAL;
-        }
-        ++i;
-    }
-
-    // For security reasons and for simplicity, file names can only contain
-    // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
-    for (i = 0; href[i] != '\0'; i++) {
-        if (href[i] == '.' || href[i] == '_' ||
-                (href[i] >= '0' && href[i] <= '9') ||
-                (href[i] >= 'A' && href[i] <= 'Z') ||
-                (href[i] >= 'a' && href[i] <= 'z')) {
-            continue;
-        }
-        ALOGE("invalid include file name: %s", href);
-        return -EINVAL;
-    }
-
-    AString filename = href;
-    if (!filename.startsWith("media_codecs_") ||
-        !filename.endsWith(".xml")) {
-        ALOGE("invalid include file name: %s", href);
-        return -EINVAL;
-    }
-    filename.insert(mHrefBase, 0);
-
-    parseXMLFile(filename.c_str());
-    return mInitCheck;
-}
-
-void MediaCodecList::startElementHandler(
-        const char *name, const char **attrs) {
-    if (mInitCheck != OK) {
-        return;
-    }
-
-    bool inType = true;
-
-    if (!strcmp(name, "Include")) {
-        mInitCheck = includeXMLFile(attrs);
-        if (mInitCheck == OK) {
-            mPastSections.push(mCurrentSection);
-            mCurrentSection = SECTION_INCLUDE;
-        }
-        ++mDepth;
-        return;
-    }
-
-    switch (mCurrentSection) {
-        case SECTION_TOPLEVEL:
-        {
-            if (!strcmp(name, "Decoders")) {
-                mCurrentSection = SECTION_DECODERS;
-            } else if (!strcmp(name, "Encoders")) {
-                mCurrentSection = SECTION_ENCODERS;
-            } else if (!strcmp(name, "Settings")) {
-                mCurrentSection = SECTION_SETTINGS;
-            }
-            break;
-        }
-
-        case SECTION_SETTINGS:
-        {
-            if (!strcmp(name, "Setting")) {
-                mInitCheck = addSettingFromAttributes(attrs);
-            }
-            break;
-        }
-
-        case SECTION_DECODERS:
-        {
-            if (!strcmp(name, "MediaCodec")) {
-                mInitCheck =
-                    addMediaCodecFromAttributes(false /* encoder */, attrs);
-
-                mCurrentSection = SECTION_DECODER;
-            }
-            break;
-        }
-
-        case SECTION_ENCODERS:
-        {
-            if (!strcmp(name, "MediaCodec")) {
-                mInitCheck =
-                    addMediaCodecFromAttributes(true /* encoder */, attrs);
-
-                mCurrentSection = SECTION_ENCODER;
-            }
-            break;
-        }
-
-        case SECTION_DECODER:
-        case SECTION_ENCODER:
-        {
-            if (!strcmp(name, "Quirk")) {
-                mInitCheck = addQuirk(attrs);
-            } else if (!strcmp(name, "Type")) {
-                mInitCheck = addTypeFromAttributes(attrs);
-                mCurrentSection =
-                    (mCurrentSection == SECTION_DECODER
-                            ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
-            }
-        }
-        inType = false;
-        // fall through
-
-        case SECTION_DECODER_TYPE:
-        case SECTION_ENCODER_TYPE:
-        {
-            // ignore limits and features specified outside of type
-            bool outside = !inType && !mCurrentInfo->mHasSoleMime;
-            if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
-                ALOGW("ignoring %s specified outside of a Type", name);
-            } else if (!strcmp(name, "Limit")) {
-                mInitCheck = addLimit(attrs);
-            } else if (!strcmp(name, "Feature")) {
-                mInitCheck = addFeature(attrs);
-            }
-            break;
-        }
-
-        default:
-            break;
-    }
-
-    ++mDepth;
-}
-
-void MediaCodecList::endElementHandler(const char *name) {
-    if (mInitCheck != OK) {
-        return;
-    }
-
-    switch (mCurrentSection) {
-        case SECTION_SETTINGS:
-        {
-            if (!strcmp(name, "Settings")) {
-                mCurrentSection = SECTION_TOPLEVEL;
-            }
-            break;
-        }
-
-        case SECTION_DECODERS:
-        {
-            if (!strcmp(name, "Decoders")) {
-                mCurrentSection = SECTION_TOPLEVEL;
-            }
-            break;
-        }
-
-        case SECTION_ENCODERS:
-        {
-            if (!strcmp(name, "Encoders")) {
-                mCurrentSection = SECTION_TOPLEVEL;
-            }
-            break;
-        }
-
-        case SECTION_DECODER_TYPE:
-        case SECTION_ENCODER_TYPE:
-        {
-            if (!strcmp(name, "Type")) {
-                mCurrentSection =
-                    (mCurrentSection == SECTION_DECODER_TYPE
-                            ? SECTION_DECODER : SECTION_ENCODER);
-
-                mCurrentInfo->complete();
-            }
-            break;
-        }
-
-        case SECTION_DECODER:
-        {
-            if (!strcmp(name, "MediaCodec")) {
-                mCurrentSection = SECTION_DECODERS;
-                mCurrentInfo->complete();
-                mCurrentInfo = NULL;
-            }
-            break;
-        }
-
-        case SECTION_ENCODER:
-        {
-            if (!strcmp(name, "MediaCodec")) {
-                mCurrentSection = SECTION_ENCODERS;
-                mCurrentInfo->complete();;
-                mCurrentInfo = NULL;
-            }
-            break;
-        }
-
-        case SECTION_INCLUDE:
-        {
-            if (!strcmp(name, "Include") && mPastSections.size() > 0) {
-                mCurrentSection = mPastSections.top();
-                mPastSections.pop();
-            }
-            break;
-        }
-
-        default:
-            break;
-    }
-
-    --mDepth;
-}
-
-status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
-    const char *name = NULL;
-    const char *value = NULL;
-    const char *update = NULL;
-
-    size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "value")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            value = 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;
-        }
-
-        ++i;
-    }
-
-    if (name == NULL || value == NULL) {
-        return -EINVAL;
-    }
-
-    mUpdate = (update != NULL) && parseBoolean(update);
-    if (mUpdate != mGlobalSettings->contains(name)) {
-        return -EINVAL;
-    }
-
-    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) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "type")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            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;
-        }
-
-        ++i;
-    }
-
-    if (name == NULL) {
-        return -EINVAL;
-    }
-
-    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;
-}
-
-status_t MediaCodecList::initializeCapabilities(const char *type) {
-    if (type == NULL) {
-        return OK;
-    }
-
-    ALOGV("initializeCapabilities %s:%s",
-            mCurrentInfo->mName.c_str(), type);
-
-    sp<MediaCodecInfo::Capabilities> caps;
-    status_t err = MediaCodec::QueryCapabilities(
-            mCurrentInfo->mName,
-            type,
-            mCurrentInfo->mIsEncoder,
-            &caps);
-    if (err != OK) {
-        return err;
-    } else if (caps == NULL) {
-        ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
-                mCurrentInfo->mName.c_str(), type,
-                mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
-        return UNKNOWN_ERROR;
-    }
-
-    return mCurrentInfo->initializeCapabilities(caps);
-}
-
-status_t MediaCodecList::addQuirk(const char **attrs) {
-    const char *name = NULL;
-
-    size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            name = attrs[i + 1];
-            ++i;
-        } else {
-            return -EINVAL;
-        }
-
-        ++i;
-    }
-
-    if (name == NULL) {
-        return -EINVAL;
-    }
-
-    mCurrentInfo->addQuirk(name);
-    return OK;
-}
-
-status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
-    const char *name = NULL;
-    const char *update = NULL;
-
-    size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
-                return -EINVAL;
-            }
-            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;
-        }
-
-        ++i;
-    }
-
-    if (name == NULL) {
-        return -EINVAL;
-    }
-
-    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 (!mUpdate && initializeCapabilities(name) != OK) {
-        mCurrentInfo->removeMime(name);
-    }
-    return OK;
+std::unique_ptr<MediaCodecInfoWriter>
+        MediaCodecListWriter::addMediaCodecInfo() {
+    sp<MediaCodecInfo> info = new MediaCodecInfo();
+    mList->mCodecInfos.push_back(info);
+    return std::unique_ptr<MediaCodecInfoWriter>(
+            new MediaCodecInfoWriter(info.get()));
 }
 
 // legacy method for non-advanced codecs
@@ -882,15 +210,15 @@
         "feature-tunneled-playback",
     };
 
-    size_t numCodecs = mCodecInfos.size();
-    for (; startIndex < numCodecs; ++startIndex) {
-        const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
+    size_t numCodecInfos = mCodecInfos.size();
+    for (; startIndex < numCodecInfos; ++startIndex) {
+        const MediaCodecInfo &info = *mCodecInfos[startIndex];
 
         if (info.isEncoder() != encoder) {
             continue;
         }
         sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
-        if (capabilities == NULL) {
+        if (capabilities == nullptr) {
             continue;
         }
         const sp<AMessage> &details = capabilities->getDetails();
@@ -913,223 +241,9 @@
     return -ENOENT;
 }
 
-static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
-    ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
-            (found ? "" : "no "), attr);
-    return -EINVAL;
-}
-
-static status_t limitError(const AString &name, const char *msg) {
-    ALOGE("limit '%s' %s", name.c_str(), msg);
-    return -EINVAL;
-}
-
-static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
-    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
-            attr, value.c_str());
-    return -EINVAL;
-}
-
-status_t MediaCodecList::addLimit(const char **attrs) {
-    sp<AMessage> msg = new AMessage();
-
-    size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (attrs[i + 1] == NULL) {
-            return -EINVAL;
-        }
-
-        // attributes with values
-        if (!strcmp(attrs[i], "name")
-                || !strcmp(attrs[i], "default")
-                || !strcmp(attrs[i], "in")
-                || !strcmp(attrs[i], "max")
-                || !strcmp(attrs[i], "min")
-                || !strcmp(attrs[i], "range")
-                || !strcmp(attrs[i], "ranges")
-                || !strcmp(attrs[i], "scale")
-                || !strcmp(attrs[i], "value")) {
-            msg->setString(attrs[i], attrs[i + 1]);
-            ++i;
-        } else {
-            return -EINVAL;
-        }
-        ++i;
-    }
-
-    AString name;
-    if (!msg->findString("name", &name)) {
-        ALOGE("limit with no 'name' attribute");
-        return -EINVAL;
-    }
-
-    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
-    // measured-frame-rate, measured-blocks-per-second: range
-    // quality: range + default + [scale]
-    // complexity: range + default
-    bool found;
-
-    if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
-            || name == "blocks-per-second" || name == "complexity"
-            || name == "frame-rate" || name == "quality" || name == "size"
-            || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
-        AString min, max;
-        if (msg->findString("min", &min) && msg->findString("max", &max)) {
-            min.append("-");
-            min.append(max);
-            if (msg->contains("range") || msg->contains("value")) {
-                return limitError(name, "has 'min' and 'max' as well as 'range' or "
-                        "'value' attributes");
-            }
-            msg->setString("range", min);
-        } else if (msg->contains("min") || msg->contains("max")) {
-            return limitError(name, "has only 'min' or 'max' attribute");
-        } else if (msg->findString("value", &max)) {
-            min = max;
-            min.append("-");
-            min.append(max);
-            if (msg->contains("range")) {
-                return limitError(name, "has both 'range' and 'value' attributes");
-            }
-            msg->setString("range", min);
-        }
-
-        AString range, scale = "linear", def, in_;
-        if (!msg->findString("range", &range)) {
-            return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
-        }
-
-        if ((name == "quality" || name == "complexity") ^
-                (found = msg->findString("default", &def))) {
-            return limitFoundMissingAttr(name, "default", found);
-        }
-        if (name != "quality" && msg->findString("scale", &scale)) {
-            return limitFoundMissingAttr(name, "scale");
-        }
-        if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
-            return limitFoundMissingAttr(name, "in", found);
-        }
-
-        if (name == "aspect-ratio") {
-            if (!(in_ == "pixels") && !(in_ == "blocks")) {
-                return limitInvalidAttr(name, "in", in_);
-            }
-            in_.erase(5, 1); // (pixel|block)-aspect-ratio
-            in_.append("-");
-            in_.append(name);
-            name = in_;
-        }
-        if (name == "quality") {
-            mCurrentInfo->addDetail("quality-scale", scale);
-        }
-        if (name == "quality" || name == "complexity") {
-            AString tag = name;
-            tag.append("-default");
-            mCurrentInfo->addDetail(tag, def);
-        }
-        AString tag = name;
-        tag.append("-range");
-        mCurrentInfo->addDetail(tag, range);
-    } else {
-        AString max, value, ranges;
-        if (msg->contains("default")) {
-            return limitFoundMissingAttr(name, "default");
-        } else if (msg->contains("in")) {
-            return limitFoundMissingAttr(name, "in");
-        } else if ((name == "channel-count" || name == "concurrent-instances") ^
-                (found = msg->findString("max", &max))) {
-            return limitFoundMissingAttr(name, "max", found);
-        } else if (msg->contains("min")) {
-            return limitFoundMissingAttr(name, "min");
-        } else if (msg->contains("range")) {
-            return limitFoundMissingAttr(name, "range");
-        } else if ((name == "sample-rate") ^
-                (found = msg->findString("ranges", &ranges))) {
-            return limitFoundMissingAttr(name, "ranges", found);
-        } else if (msg->contains("scale")) {
-            return limitFoundMissingAttr(name, "scale");
-        } else if ((name == "alignment" || name == "block-size") ^
-                (found = msg->findString("value", &value))) {
-            return limitFoundMissingAttr(name, "value", found);
-        }
-
-        if (max.size()) {
-            AString tag = "max-";
-            tag.append(name);
-            mCurrentInfo->addDetail(tag, max);
-        } else if (value.size()) {
-            mCurrentInfo->addDetail(name, value);
-        } else if (ranges.size()) {
-            AString tag = name;
-            tag.append("-ranges");
-            mCurrentInfo->addDetail(tag, ranges);
-        } else {
-            ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
-        }
-    }
-    return OK;
-}
-
-status_t MediaCodecList::addFeature(const char **attrs) {
-    size_t i = 0;
-    const char *name = NULL;
-    int32_t optional = -1;
-    int32_t required = -1;
-    const char *value = NULL;
-
-    while (attrs[i] != NULL) {
-        if (attrs[i + 1] == NULL) {
-            return -EINVAL;
-        }
-
-        // attributes with values
-        if (!strcmp(attrs[i], "name")) {
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
-            int value = (int)parseBoolean(attrs[i + 1]);
-            if (!strcmp(attrs[i], "optional")) {
-                optional = value;
-            } else {
-                required = value;
-            }
-            ++i;
-        } else if (!strcmp(attrs[i], "value")) {
-            value = attrs[i + 1];
-            ++i;
-        } else {
-            return -EINVAL;
-        }
-        ++i;
-    }
-    if (name == NULL) {
-        ALOGE("feature with no 'name' attribute");
-        return -EINVAL;
-    }
-
-    if (optional == required && optional != -1) {
-        ALOGE("feature '%s' is both/neither optional and required", name);
-        return -EINVAL;
-    }
-
-    if ((optional != -1 || required != -1) && (value != NULL)) {
-        ALOGE("feature '%s' has both a value and optional/required attribute", name);
-        return -EINVAL;
-    }
-
-    if (value != NULL) {
-        mCurrentInfo->addFeature(name, value);
-    } else {
-        mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
-    }
-    return OK;
-}
-
 ssize_t MediaCodecList::findCodecByName(const char *name) const {
     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
-        const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
-
-        if (info.mName == name) {
+        if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
             return i;
         }
     }
@@ -1167,11 +281,15 @@
 
 //static
 void MediaCodecList::findMatchingCodecs(
-        const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
+        const char *mime, bool encoder, uint32_t flags,
+        Vector<AString> *matches, Vector<AString> *owners) {
     matches->clear();
+    if (owners != nullptr) {
+        owners->clear();
+    }
 
     const sp<IMediaCodecList> list = getInstance();
-    if (list == NULL) {
+    if (list == nullptr) {
         return;
     }
 
@@ -1187,45 +305,27 @@
         index = matchIndex + 1;
 
         const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
-        CHECK(info != NULL);
+        CHECK(info != nullptr);
         AString componentName = info->getCodecName();
 
         if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
             ALOGV("skipping SW codec '%s'", componentName.c_str());
         } else {
             matches->push(componentName);
+            if (owners != nullptr) {
+                owners->push(AString(info->getOwnerName()));
+            }
             ALOGV("matching '%s'", componentName.c_str());
         }
     }
 
-    if (flags & kPreferSoftwareCodecs || property_get_bool("debug.stagefright.swcodec", false)) {
+    if (flags & kPreferSoftwareCodecs ||
+            property_get_bool("debug.stagefright.swcodec", false)) {
         matches->sort(compareSoftwareCodecsFirst);
     }
 }
 
-// static
-uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
-    const sp<IMediaCodecList> list = getInstance();
-    if (list == NULL) {
-        return 0;
-    }
-
-    ssize_t ix = list->findCodecByName(componentName);
-    if (ix < 0) {
-        return 0;
-    }
-
-    const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
-
-    uint32_t quirks = 0;
-    if (info->hasQuirk("requires-allocate-on-input-ports")) {
-        quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
-    }
-    if (info->hasQuirk("requires-allocate-on-output-ports")) {
-        quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
-    }
-
-    return quirks;
+MediaCodecListBuilderBase::~MediaCodecListBuilderBase() {
 }
 
 }  // namespace android
diff --git a/media/libstagefright/MediaCodecListOverrides.cpp b/media/libstagefright/MediaCodecListOverrides.cpp
index 095fc6a..6920e51 100644
--- a/media/libstagefright/MediaCodecListOverrides.cpp
+++ b/media/libstagefright/MediaCodecListOverrides.cpp
@@ -33,8 +33,6 @@
 
 namespace android {
 
-const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml";
-
 AString getProfilingVersionString() {
     char val[PROPERTY_VALUE_MAX];
     if (property_get("ro.build.display.id", val, NULL) && (strlen(val) > 0)) {
@@ -205,24 +203,24 @@
     return true;
 }
 
-void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) {
+void profileCodecs(const std::vector<sp<MediaCodecInfo>> &infos,
+        const char* profilingResults) {
     CodecSettings global_results;
     KeyedVector<AString, CodecSettings> encoder_results;
     KeyedVector<AString, CodecSettings> decoder_results;
     profileCodecs(infos, &global_results, &encoder_results, &decoder_results);
-    exportResultsToXML(kProfilingResults, global_results, encoder_results, decoder_results);
+    exportResultsToXML(profilingResults, global_results, encoder_results, decoder_results);
 }
 
 void profileCodecs(
-        const Vector<sp<MediaCodecInfo>> &infos,
+        const std::vector<sp<MediaCodecInfo>> &infos,
         CodecSettings *global_results,
         KeyedVector<AString, CodecSettings> *encoder_results,
         KeyedVector<AString, CodecSettings> *decoder_results,
         bool forceToMeasure) {
     KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure;
     AString supportMultipleSecureCodecs = "true";
-    for (size_t i = 0; i < infos.size(); ++i) {
-        const sp<MediaCodecInfo> info = infos[i];
+    for (const auto& info : infos) {
         AString name = info->getCodecName();
         if (name.startsWith("OMX.google.") ||
                 // TODO: reenable below codecs once fixed
diff --git a/media/libstagefright/MediaCodecListOverrides.h b/media/libstagefright/MediaCodecListOverrides.h
index d4bb225..4f8c2f5 100644
--- a/media/libstagefright/MediaCodecListOverrides.h
+++ b/media/libstagefright/MediaCodecListOverrides.h
@@ -23,12 +23,10 @@
 
 #include <utils/StrongPointer.h>
 #include <utils/KeyedVector.h>
+#include <vector>
 
 namespace android {
 
-extern const char *kProfilingVersionString;
-extern const char *kProfilingResults;
-
 struct MediaCodecInfo;
 
 AString getProfilingVersionString();
@@ -36,11 +34,12 @@
 bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2);
 
 // profile codecs and save the result to xml file named kProfilingResults.
-void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos);
+void profileCodecs(const std::vector<sp<MediaCodecInfo>> &infos,
+        const char* profilingResults);
 
 // profile codecs and save the result to global_results, encoder_results and decoder_results.
 void profileCodecs(
-        const Vector<sp<MediaCodecInfo>> &infos,
+        const std::vector<sp<MediaCodecInfo>> &infos,
         CodecSettings *global_results,
         KeyedVector<AString, CodecSettings> *encoder_results,
         KeyedVector<AString, CodecSettings> *decoder_results,
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 5f9aa01..5f50e46 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -38,7 +38,7 @@
 }
 
 status_t OMXClient::connect() {
-    return connect(nullptr);
+    return connect("default", nullptr);
 }
 
 status_t OMXClient::connect(bool* trebleFlag) {
@@ -54,6 +54,19 @@
     return connectLegacy();
 }
 
+status_t OMXClient::connect(const char* name, bool* trebleFlag) {
+    if (property_get_bool("persist.media.treble_omx", true)) {
+        if (trebleFlag != nullptr) {
+            *trebleFlag = true;
+        }
+        return connectTreble(name);
+    }
+    if (trebleFlag != nullptr) {
+        *trebleFlag = false;
+    }
+    return connectLegacy();
+}
+
 status_t OMXClient::connectLegacy() {
     sp<IServiceManager> sm = defaultServiceManager();
     sp<IBinder> codecbinder = sm->getService(String16("media.codec"));
@@ -73,9 +86,12 @@
     return OK;
 }
 
-status_t OMXClient::connectTreble() {
+status_t OMXClient::connectTreble(const char* name) {
     using namespace ::android::hardware::media::omx::V1_0;
-    sp<IOmx> tOmx = IOmx::getService("default");
+    if (name == nullptr) {
+        name = "default";
+    }
+    sp<IOmx> tOmx = IOmx::getService(name);
     if (tOmx.get() == nullptr) {
         ALOGE("Cannot obtain Treble IOmx.");
         return NO_INIT;
diff --git a/media/libstagefright/OmxInfoBuilder.cpp b/media/libstagefright/OmxInfoBuilder.cpp
new file mode 100644
index 0000000..686bcc8
--- /dev/null
+++ b/media/libstagefright/OmxInfoBuilder.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2017 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 "OmxInfoBuilder"
+
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+#include <utils/Log.h>
+#include <cutils/properties.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaCodecService.h>
+#include <media/stagefright/OmxInfoBuilder.h>
+#include <media/stagefright/ACodec.h>
+
+#include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+#include <media/IOMXStore.h>
+#include <media/IOMX.h>
+#include <media/MediaDefs.h>
+#include <media/omx/1.0/WOmx.h>
+
+#include <media/openmax/OMX_Index.h>
+#include <media/openmax/OMX_IndexExt.h>
+#include <media/openmax/OMX_Audio.h>
+#include <media/openmax/OMX_AudioExt.h>
+#include <media/openmax/OMX_Video.h>
+#include <media/openmax/OMX_VideoExt.h>
+
+namespace android {
+
+namespace /* unnamed */ {
+
+status_t queryCapabilities(
+        const IOMXStore::NodeInfo& node, const char* mime, bool isEncoder,
+        MediaCodecInfo::CapabilitiesWriter* caps) {
+    sp<ACodec> codec = new ACodec();
+    status_t err = codec->queryCapabilities(
+            node.owner.c_str(), node.name.c_str(), mime, isEncoder, caps);
+    if (err != OK) {
+        return err;
+    }
+    for (const auto& attribute : node.attributes) {
+        // All features have an int32 value except
+        // "feature-bitrate-control", which has a string value.
+        if ((attribute.key.compare(0, 8, "feature-") == 0) &&
+                (attribute.key.compare(8, 15, "bitrate-control")
+                 != 0)) {
+            // If this attribute.key is a feature that is not a bitrate
+            // control, add an int32 value.
+            caps->addDetail(
+                    attribute.key.c_str(),
+                    attribute.value == "1" ? 1 : 0);
+        } else {
+            // Non-feature attributes
+            caps->addDetail(
+                    attribute.key.c_str(), attribute.value.c_str());
+        }
+    }
+    return OK;
+}
+
+}  // unnamed namespace
+
+OmxInfoBuilder::OmxInfoBuilder() {
+}
+
+status_t OmxInfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
+    bool treble;
+    sp<IOMX> omx;
+    std::vector<IOMXStore::RoleInfo> roles;
+
+    treble = property_get_bool("persist.media.treble_omx", true);
+    if (treble) {
+        using namespace ::android::hardware::media::omx::V1_0;
+        using ::android::hardware::hidl_vec;
+        using ::android::hardware::hidl_string;
+
+        // Obtain IOmxStore
+        sp<IOmxStore> omxStore = IOmxStore::getService();
+        if (omxStore == nullptr) {
+            ALOGE("Cannot connect to an IOmxStore instance.");
+            return NO_INIT;
+        }
+
+        // List service attributes (global settings)
+        Status status;
+        hidl_vec<IOmxStore::ServiceAttribute> serviceAttributes;
+        auto transStatus = omxStore->listServiceAttributes(
+                [&status, &serviceAttributes]
+                (Status inStatus, const hidl_vec<IOmxStore::ServiceAttribute>&
+                        inAttributes) {
+                    status = inStatus;
+                    serviceAttributes = inAttributes;
+                });
+        if (!transStatus.isOk()) {
+            ALOGE("Fail to obtain global settings from IOmxStore.");
+            return NO_INIT;
+        }
+        if (status != Status::OK) {
+            ALOGE("IOmxStore reports parsing error.");
+            return NO_INIT;
+        }
+        for (const auto& p : serviceAttributes) {
+            writer->addGlobalSetting(
+                    p.key.c_str(), p.value.c_str());
+        }
+
+        // List roles and convert to IOMXStore's format
+        transStatus = omxStore->listRoles(
+                [&roles]
+                (const hidl_vec<IOmxStore::RoleInfo>& inRoleList) {
+                    roles.reserve(inRoleList.size());
+                    for (const auto& inRole : inRoleList) {
+                        IOMXStore::RoleInfo role;
+                        role.role = inRole.role;
+                        role.type = inRole.type;
+                        role.isEncoder = inRole.isEncoder;
+                        role.preferPlatformNodes = inRole.preferPlatformNodes;
+                        std::vector<IOMXStore::NodeInfo>& nodes =
+                                role.nodes;
+                        nodes.reserve(inRole.nodes.size());
+                        for (const auto& inNode : inRole.nodes) {
+                            IOMXStore::NodeInfo node;
+                            node.name = inNode.name;
+                            node.owner = inNode.owner;
+                            std::vector<IOMXStore::Attribute>& attributes =
+                                    node.attributes;
+                            attributes.reserve(inNode.attributes.size());
+                            for (const auto& inAttr : inNode.attributes) {
+                                IOMXStore::Attribute attr;
+                                attr.key = inAttr.key;
+                                attr.value = inAttr.value;
+                                attributes.push_back(std::move(attr));
+                            }
+                            nodes.push_back(std::move(node));
+                        }
+                        roles.push_back(std::move(role));
+                    }
+                });
+        if (!transStatus.isOk()) {
+            ALOGE("Fail to obtain codec roles from IOmxStore.");
+            return NO_INIT;
+        }
+    } else {
+        // Obtain IOMXStore
+        sp<IServiceManager> sm = defaultServiceManager();
+        if (sm == nullptr) {
+            ALOGE("Cannot obtain the default service manager.");
+            return NO_INIT;
+        }
+        sp<IBinder> codecBinder = sm->getService(String16("media.codec"));
+        if (codecBinder == nullptr) {
+            ALOGE("Cannot obtain the media codec service.");
+            return NO_INIT;
+        }
+        sp<IMediaCodecService> codecService =
+                interface_cast<IMediaCodecService>(codecBinder);
+        if (codecService == nullptr) {
+            ALOGE("Wrong type of media codec service obtained.");
+            return NO_INIT;
+        }
+        omx = codecService->getOMX();
+        if (omx == nullptr) {
+            ALOGE("Cannot connect to an IOMX instance.");
+        }
+        sp<IOMXStore> omxStore = codecService->getOMXStore();
+        if (omxStore == nullptr) {
+            ALOGE("Cannot connect to an IOMXStore instance.");
+            return NO_INIT;
+        }
+
+        // List service attributes (global settings)
+        std::vector<IOMXStore::Attribute> serviceAttributes;
+        status_t status = omxStore->listServiceAttributes(&serviceAttributes);
+        if (status != OK) {
+            ALOGE("Fail to obtain global settings from IOMXStore.");
+            return NO_INIT;
+        }
+        for (const auto& p : serviceAttributes) {
+            writer->addGlobalSetting(
+                    p.key.c_str(), p.value.c_str());
+        }
+
+        // List roles
+        status = omxStore->listRoles(&roles);
+        if (status != OK) {
+            ALOGE("Fail to obtain codec roles from IOMXStore.");
+            return NO_INIT;
+        }
+    }
+
+    // Convert roles to lists of codecs
+
+    // codec name -> index into swCodecs
+    std::map<std::string, std::unique_ptr<MediaCodecInfoWriter> >
+            swCodecName2Info;
+    // codec name -> index into hwCodecs
+    std::map<std::string, std::unique_ptr<MediaCodecInfoWriter> >
+            hwCodecName2Info;
+    // owner name -> MediaCodecInfo
+    // This map will be used to obtain the correct IOmx service(s) needed for
+    // creating IOmxNode instances and querying capabilities.
+    std::map<std::string, std::vector<sp<MediaCodecInfo> > >
+            owner2CodecInfo;
+
+    for (const auto& role : roles) {
+        const auto& typeName = role.type;
+        bool isEncoder = role.isEncoder;
+        bool preferPlatformNodes = role.preferPlatformNodes;
+        // If preferPlatformNodes is true, hardware nodes must be added after
+        // platform (software) nodes. hwCodecs is used to hold hardware nodes
+        // that need to be added after software nodes for the same role.
+        std::vector<const IOMXStore::NodeInfo*> hwCodecs;
+        for (const auto& node : role.nodes) {
+            const auto& nodeName = node.name;
+            bool isSoftware = nodeName.compare(0, 10, "OMX.google") == 0;
+            MediaCodecInfoWriter* info;
+            if (isSoftware) {
+                auto c2i = swCodecName2Info.find(nodeName);
+                if (c2i == swCodecName2Info.end()) {
+                    // Create a new MediaCodecInfo for a new node.
+                    c2i = swCodecName2Info.insert(std::make_pair(
+                            nodeName, writer->addMediaCodecInfo())).first;
+                    info = c2i->second.get();
+                    info->setName(nodeName.c_str());
+                    info->setOwner(node.owner.c_str());
+                    info->setEncoder(isEncoder);
+                } else {
+                    // The node has been seen before. Simply retrieve the
+                    // existing MediaCodecInfoWriter.
+                    info = c2i->second.get();
+                }
+            } else {
+                auto c2i = hwCodecName2Info.find(nodeName);
+                if (c2i == hwCodecName2Info.end()) {
+                    // Create a new MediaCodecInfo for a new node.
+                    if (!preferPlatformNodes) {
+                        c2i = hwCodecName2Info.insert(std::make_pair(
+                                nodeName, writer->addMediaCodecInfo())).first;
+                        info = c2i->second.get();
+                        info->setName(nodeName.c_str());
+                        info->setOwner(node.owner.c_str());
+                        info->setEncoder(isEncoder);
+                    } else {
+                        // If preferPlatformNodes is true, this node must be
+                        // added after all software nodes.
+                        hwCodecs.push_back(&node);
+                        continue;
+                    }
+                } else {
+                    // The node has been seen before. Simply retrieve the
+                    // existing MediaCodecInfoWriter.
+                    info = c2i->second.get();
+                }
+            }
+            std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+                    info->addMime(typeName.c_str());
+            if (queryCapabilities(
+                    node, typeName.c_str(), isEncoder, caps.get()) != OK) {
+                ALOGW("Fail to add mime %s to codec %s",
+                        typeName.c_str(), nodeName.c_str());
+                info->removeMime(typeName.c_str());
+            }
+        }
+
+        // If preferPlatformNodes is true, hardware nodes will not have been
+        // added in the loop above, but rather saved in hwCodecs. They are
+        // going to be added here.
+        if (preferPlatformNodes) {
+            for (const auto& node : hwCodecs) {
+                MediaCodecInfoWriter* info;
+                const auto& nodeName = node->name;
+                auto c2i = hwCodecName2Info.find(nodeName);
+                if (c2i == hwCodecName2Info.end()) {
+                    // Create a new MediaCodecInfo for a new node.
+                    c2i = hwCodecName2Info.insert(std::make_pair(
+                            nodeName, writer->addMediaCodecInfo())).first;
+                    info = c2i->second.get();
+                    info->setName(nodeName.c_str());
+                    info->setOwner(node->owner.c_str());
+                    info->setEncoder(isEncoder);
+                } else {
+                    // The node has been seen before. Simply retrieve the
+                    // existing MediaCodecInfoWriter.
+                    info = c2i->second.get();
+                }
+                std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
+                        info->addMime(typeName.c_str());
+                if (queryCapabilities(
+                        *node, typeName.c_str(), isEncoder, caps.get()) != OK) {
+                    ALOGW("Fail to add mime %s to codec %s "
+                          "after software codecs",
+                          typeName.c_str(), nodeName.c_str());
+                    info->removeMime(typeName.c_str());
+                }
+            }
+        }
+    }
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp
index 1daaf49..21c00a1 100644
--- a/media/libstagefright/codecs/aacdec/Android.bp
+++ b/media/libstagefright/codecs/aacdec/Android.bp
@@ -37,4 +37,5 @@
         "libcutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 4b478ea..fb368c2 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -145,6 +145,7 @@
         "libutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
 
 cc_library_shared {
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index a61fb57..d266dc2 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -104,6 +104,7 @@
         "liblog",
         "libstagefright_amrnb_common",
     ],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index 04ed07f..6dc2dc1 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -115,6 +115,7 @@
         "liblog",
         "libstagefright_amrnb_common",
     ],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index d337cde..8968991 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -178,6 +178,7 @@
         "liblog",
         "libstagefright_enc_common",
     ],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 3fa8d7f..1f43803 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -34,4 +34,5 @@
     },
 
     ldflags: ["-Wl,-Bsymbolic"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp
index 6c4311b..66507a7 100644
--- a/media/libstagefright/codecs/avcenc/Android.bp
+++ b/media/libstagefright/codecs/avcenc/Android.bp
@@ -34,4 +34,5 @@
     },
 
     ldflags: ["-Wl,-Bsymbolic"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 5652594..595cfdb 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -37,4 +37,5 @@
         "libstagefright_foundation",
         "libutils",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index 6197157..066917b 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -37,4 +37,5 @@
         enabled: true,
     },
 
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
index caceda9..56d2d69 100644
--- a/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
+++ b/media/libstagefright/codecs/flac/enc/SoftFlacEncoder.cpp
@@ -154,6 +154,30 @@
     ALOGV("SoftFlacEncoder::internalGetParameter(index=0x%x)", index);
 
     switch (index) {
+        case OMX_IndexParamAudioPortFormat:
+        {
+            OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+                (OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+            if (!isValidOMXParam(formatParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if (formatParams->nIndex > 0) {
+                return OMX_ErrorNoMore;
+            }
+
+            formatParams->eEncoding =
+                (formatParams->nPortIndex == 0)
+                    ? OMX_AUDIO_CodingPCM : OMX_AUDIO_CodingFLAC;
+
+            return OMX_ErrorNone;
+        }
+
         case OMX_IndexParamAudioPcm:
         {
             OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
@@ -163,7 +187,7 @@
                 return OMX_ErrorBadParameter;
             }
 
-            if (pcmParams->nPortIndex > 1) {
+            if (pcmParams->nPortIndex != 0) {
                 return OMX_ErrorUndefined;
             }
 
@@ -189,6 +213,10 @@
                 return OMX_ErrorBadParameter;
             }
 
+            if (flacParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
             flacParams->nCompressionLevel = mCompressionLevel;
             flacParams->nChannels = mNumChannels;
             flacParams->nSampleRate = mSampleRate;
@@ -203,6 +231,29 @@
 OMX_ERRORTYPE SoftFlacEncoder::internalSetParameter(
         OMX_INDEXTYPE index, const OMX_PTR params) {
     switch (index) {
+        case OMX_IndexParamAudioPortFormat:
+        {
+            const OMX_AUDIO_PARAM_PORTFORMATTYPE *formatParams =
+                (const OMX_AUDIO_PARAM_PORTFORMATTYPE *)params;
+
+            if (!isValidOMXParam(formatParams)) {
+                return OMX_ErrorBadParameter;
+            }
+
+            if (formatParams->nPortIndex > 1) {
+                return OMX_ErrorUndefined;
+            }
+
+            if ((formatParams->nPortIndex == 0
+                        && formatParams->eEncoding != OMX_AUDIO_CodingPCM)
+                || (formatParams->nPortIndex == 1
+                        && formatParams->eEncoding != OMX_AUDIO_CodingFLAC)) {
+                return OMX_ErrorUndefined;
+            }
+
+            return OMX_ErrorNone;
+        }
+
         case OMX_IndexParamAudioPcm:
         {
             ALOGV("SoftFlacEncoder::internalSetParameter(OMX_IndexParamAudioPcm)");
@@ -212,7 +263,7 @@
                 return OMX_ErrorBadParameter;
             }
 
-            if (pcmParams->nPortIndex != 0 && pcmParams->nPortIndex != 1) {
+            if (pcmParams->nPortIndex != 0) {
                 ALOGE("SoftFlacEncoder::internalSetParameter() Error #1");
                 return OMX_ErrorUndefined;
             }
@@ -258,6 +309,10 @@
                 return OMX_ErrorBadParameter;
             }
 
+            if (flacParams->nPortIndex != 1) {
+                return OMX_ErrorUndefined;
+            }
+
             mCompressionLevel = flacParams->nCompressionLevel; // range clamping done inside encoder
             return OMX_ErrorNone;
         }
diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp
index 0e6f468..fff72a8 100644
--- a/media/libstagefright/codecs/g711/dec/Android.bp
+++ b/media/libstagefright/codecs/g711/dec/Android.bp
@@ -31,4 +31,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp
index 7be86a4..753eeef 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.bp
+++ b/media/libstagefright/codecs/gsm/dec/Android.bp
@@ -34,4 +34,5 @@
     ],
 
     static_libs: ["libgsm"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp
index 3fd1652..7fa74d4 100644
--- a/media/libstagefright/codecs/hevcdec/Android.bp
+++ b/media/libstagefright/codecs/hevcdec/Android.bp
@@ -37,4 +37,5 @@
     // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC
     // Bug: 16853291
     ldflags: ["-Wl,-Bsymbolic"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 2619131..1216ae5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -109,4 +109,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index 919b9d4..640718d 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -95,6 +95,7 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index b2e8f9b..273fa31 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -115,6 +115,7 @@
     ],
 
     static_libs: ["libstagefright_mp3dec"],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index ed51797..15fdde7 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -34,4 +34,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index 249ab92..59c1f5d 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -34,4 +34,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index 0284719..741774c 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -36,4 +36,5 @@
         "libutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index d7569a9..88d6ec4 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -33,4 +33,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp
index 1bd75c6..f21d46f 100644
--- a/media/libstagefright/codecs/raw/Android.bp
+++ b/media/libstagefright/codecs/raw/Android.bp
@@ -31,4 +31,5 @@
         "libutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp
index fedfb67..628b36c 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.bp
+++ b/media/libstagefright/codecs/vorbis/dec/Android.bp
@@ -29,4 +29,5 @@
             "unsigned-integer-overflow",
         ],
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index cc89064..8f32582 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -23,6 +23,7 @@
 
 sp<ABuffer> decodeBase64(const AString &s) {
     size_t n = s.size();
+
     if ((n % 4) != 0) {
         return NULL;
     }
@@ -45,7 +46,6 @@
     size_t outLen = (n / 4) * 3 - padding;
 
     sp<ABuffer> buffer = new ABuffer(outLen);
-
     uint8_t *out = buffer->data();
     if (out == NULL || buffer->size() < outLen) {
         return NULL;
@@ -61,9 +61,9 @@
             value = 26 + c - 'a';
         } else if (c >= '0' && c <= '9') {
             value = 52 + c - '0';
-        } else if (c == '+') {
+        } else if (c == '+' || c == '-') {
             value = 62;
-        } else if (c == '/') {
+        } else if (c == '/' || c == '_') {
             value = 63;
         } else if (c != '=') {
             return NULL;
@@ -144,4 +144,26 @@
     }
 }
 
+void encodeBase64Url(
+        const void *_data, size_t size, AString *out) {
+    encodeBase64(_data, size, out);
+
+    if ((-1 != out->find("+")) || (-1 != out->find("/"))) {
+        size_t outLen = out->size();
+        char *base64url = new char[outLen];
+        for (size_t i = 0; i < outLen; ++i) {
+            if (out->c_str()[i] == '+')
+                base64url[i] = '-';
+            else if (out->c_str()[i] == '/')
+                base64url[i] = '_';
+            else
+                base64url[i] = out->c_str()[i];
+        }
+
+        out->setTo(base64url, outLen);
+        delete[] base64url;
+    }
+}
+
+
 }  // namespace android
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h b/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
index e340b89..abc95e0 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
@@ -28,6 +28,8 @@
 sp<ABuffer> decodeBase64(const AString &s);
 void encodeBase64(const void *data, size_t size, AString *out);
 
+void encodeBase64Url(const void *data, size_t size, AString *out);
+
 }  // namespace android
 
 #endif  // BASE_64_H_
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
index d741c6f..a9e3c76 100644
--- a/media/libstagefright/foundation/tests/Android.mk
+++ b/media/libstagefright/foundation/tests/Android.mk
@@ -9,11 +9,13 @@
 
 LOCAL_SRC_FILES := \
 	AData_test.cpp \
+	Base64_test.cpp \
 	Flagged_test.cpp \
 	TypeTraits_test.cpp \
 	Utils_test.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
+	liblog \
 	libstagefright_foundation \
 	libutils \
 
diff --git a/media/libstagefright/foundation/tests/Base64_test.cpp b/media/libstagefright/foundation/tests/Base64_test.cpp
new file mode 100644
index 0000000..7a4289e
--- /dev/null
+++ b/media/libstagefright/foundation/tests/Base64_test.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 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 <utils/Log.h>
+
+#include "gtest/gtest.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/AStringUtils.h>
+#include <media/stagefright/foundation/base64.h>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace {
+const android::String8 kBase64Padding("=");
+};
+
+namespace android {
+
+class Base64Test : public ::testing::Test {
+};
+
+void verifyDecode(const AString* expected, const AString* in) {
+    size_t numTests = 0;
+    while (!expected[numTests].empty())
+        ++numTests;
+
+    for (size_t i = 0; i < numTests; ++i) {
+        // Since android::decodeBase64() requires padding characters,
+        // add them so length of encoded text is exactly a multiple of 4.
+        int remainder = in[i].size() % 4;
+        String8 paddedText(in[i].c_str());
+        if (remainder > 0) {
+            for (int i = 0; i < 4 - remainder; ++i) {
+                paddedText.append(kBase64Padding);
+            }
+        }
+        sp<ABuffer> result = decodeBase64(AString(paddedText.string()));
+
+        ASSERT_EQ(AStringUtils::Compare(expected[i].c_str(),
+                reinterpret_cast<char*>(result->data()),
+                expected[i].size(), false), 0);
+    }
+}
+
+void verifyEncode(const AString* expected, const AString* in) {
+    size_t numTests = 0;
+    while (!expected[numTests].empty())
+        ++numTests;
+
+    AString out = AString("");
+    for (size_t i = 0; i < numTests; ++i) {
+        encodeBase64Url(in[i].c_str(), in[i].size(), &out);
+
+        ASSERT_EQ(AStringUtils::Compare(expected[i].c_str(), out.c_str(),
+                expected[i].size(), false), 0);
+    }
+}
+
+TEST_F(Base64Test, TestDecodeBase64) {
+    const AString base64[] = {
+        AString("SGVsbG8gRnJpZW5kIQ"),
+        AString("R29vZCBkYXkh"),
+        AString("")  // string to signal end of array
+    };
+
+    const AString clearText[] = {
+        AString("Hello Friend!"),
+        AString("Good day!"),
+        AString("")
+    };
+
+    verifyDecode(clearText, base64);
+}
+
+TEST_F(Base64Test, TestDecodeBase64Url) {
+    const AString base64Url[] = {
+        AString("SGVsbG8gRnJpZW5kICE-Pw"),
+        AString("SGVsbG8gRnJpZW5kICE_"),
+        AString("SGVsbG8gPz4-IEZyaWVuZCA_Pg"),
+        AString("")
+    };
+
+    const AString clearText[] = {
+        AString("Hello Friend !>?"),
+        AString("Hello Friend !?"),
+        AString("Hello ?>> Friend ?>"),
+        AString("")
+    };
+
+    verifyDecode(clearText, base64Url);
+}
+
+TEST_F(Base64Test, TestDecodeMalformedBase64) {
+    const AString base64Url[] = {
+        AString("1?GawgguFyGrWKav7AX4VKUg"),  // fail on parsing
+        AString("GawgguFyGrWKav7AX4V???"),    // fail on length not multiple of 4
+        AString("GawgguFyGrWKav7AX4VKUg"),    // ditto
+    };
+
+    for (size_t i = 0; i < 3; ++i) {
+        sp<ABuffer> result = decodeBase64(AString(base64Url[i]));
+        EXPECT_TRUE(result == nullptr);
+    }
+}
+
+TEST_F(Base64Test, TestEncodeBase64) {
+    const AString clearText[] = {
+        AString("Hello Friend!"),
+        AString("Good day!"),
+        AString("")
+    };
+
+    const AString base64[] = {
+        AString("SGVsbG8gRnJpZW5kIQ=="),
+        AString("R29vZCBkYXkh"),
+        AString("")
+    };
+
+    verifyEncode(base64, clearText);
+}
+
+TEST_F(Base64Test, TestEncodeBase64Url) {
+    const AString clearText[] = {
+        AString("Hello Friend !>?"),
+        AString("Hello Friend !?"),
+        AString("Hello ?>> Friend ?>"),
+        AString("")
+    };
+
+    const AString base64Url[] = {
+        AString("SGVsbG8gRnJpZW5kICE-Pw=="),
+        AString("SGVsbG8gRnJpZW5kICE_"),
+        AString("SGVsbG8gPz4-IEZyaWVuZCA_Pg"),
+        AString("")
+    };
+
+    verifyEncode(base64Url, clearText);
+}
+
+} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index b9f48c4..424246d 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <android/native_window.h>
 #include <media/hardware/MetadataBufferType.h>
+#include <media/MediaCodecInfo.h>
 #include <media/IOMX.h>
 #include <media/stagefright/foundation/AHierarchicalStateMachine.h>
 #include <media/stagefright/CodecBase.h>
@@ -30,6 +31,8 @@
 #include <OMX_Audio.h>
 #include <hardware/gralloc.h>
 #include <nativebase/nativebase.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
 
 #define TRACK_BUFFER_TIMING     0
 
@@ -42,20 +45,6 @@
 struct DescribeColorFormat2Params;
 struct DataConverter;
 
-// Treble shared memory
-namespace hidl {
-namespace allocator {
-namespace V1_0 {
-struct IAllocator;
-} // V1_0
-} // allocator
-namespace memory {
-namespace V1_0 {
-struct IMemory;
-} // V1_0
-} // memory
-} // hidl
-
 typedef hidl::allocator::V1_0::IAllocator TAllocator;
 typedef hidl::memory::V1_0::IMemory TMemory;
 
@@ -72,9 +61,10 @@
     virtual void initiateStart();
     virtual void initiateShutdown(bool keepComponentAllocated = false);
 
-    virtual status_t queryCapabilities(
-            const AString &name, const AString &mime, bool isEncoder,
-            sp<MediaCodecInfo::Capabilities> *caps);
+    status_t queryCapabilities(
+            const char* owner, const char* name,
+            const char* mime, bool isEncoder,
+            MediaCodecInfo::CapabilitiesWriter* caps);
 
     virtual status_t setSurface(const sp<Surface> &surface);
 
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 6245ccb..9197f7b 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -213,10 +213,6 @@
     // require an explicit message handler
     virtual void onMessageReceived(const sp<AMessage> &msg) = 0;
 
-    virtual status_t queryCapabilities(
-            const AString& /*name*/, const AString& /*mime*/, bool /*isEncoder*/,
-            sp<MediaCodecInfo::Capabilities>* /*caps*/ /* nonnull */) { return INVALID_OPERATION; }
-
     virtual status_t setSurface(const sp<Surface>& /*surface*/) { return INVALID_OPERATION; }
 
     virtual void signalFlush() = 0;
diff --git a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
index 044699c..c14755a 100644
--- a/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
+++ b/media/libstagefright/include/media/stagefright/FrameRenderTracker.h
@@ -23,6 +23,8 @@
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
 
 #include <list>
 
@@ -30,9 +32,6 @@
 
 namespace android {
 
-class Fence;
-class GraphicBuffer;
-
 // Tracks the render information about a frame. Frames go through several states while
 // the render information is tracked:
 //
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 209fe12..836534d 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -87,11 +87,6 @@
 
     static sp<PersistentSurface> CreatePersistentInputSurface();
 
-    // utility method to query capabilities
-    static status_t QueryCapabilities(
-            const AString &name, const AString &mime, bool isEncoder,
-            sp<MediaCodecInfo::Capabilities> *caps /* nonnull */);
-
     status_t configure(
             const sp<AMessage> &format,
             const sp<Surface> &nativeWindow,
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecList.h b/media/libstagefright/include/media/stagefright/MediaCodecList.h
index 430bc16..f2bd496 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecList.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecList.h
@@ -21,7 +21,6 @@
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AString.h>
 #include <media/IMediaCodecList.h>
-#include <media/IOMX.h>
 #include <media/MediaCodecInfo.h>
 
 #include <sys/types.h>
@@ -36,6 +35,8 @@
 
 struct AMessage;
 
+struct MediaCodecListBuilderBase;
+
 struct MediaCodecList : public BnMediaCodecList {
     static sp<IMediaCodecList> getInstance();
 
@@ -51,7 +52,7 @@
             ALOGE("b/24445127");
             return NULL;
         }
-        return mCodecInfos.itemAt(index);
+        return mCodecInfos[index];
     }
 
     virtual const sp<AMessage> getGlobalSettings() const;
@@ -62,9 +63,6 @@
     // only to be used by getLocalInstance
     static void *profilerThreadWrapper(void * /*arg*/);
 
-    // only to be used by MediaPlayerService
-    void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
-
     enum Flags {
         kPreferSoftwareCodecs   = 1,
         kHardwareCodecsOnly     = 2,
@@ -74,13 +72,11 @@
             const char *mime,
             bool createEncoder,
             uint32_t flags,
-            Vector<AString> *matching);
-
-    static uint32_t getQuirksFor(const char *mComponentName);
+            Vector<AString> *matchingCodecs,
+            Vector<AString> *owners = nullptr);
 
     static bool isSoftwareCodec(const AString &componentName);
 
-
 private:
     class BinderDeathObserver : public IBinder::DeathRecipient {
         void binderDied(const wp<IBinder> &the_late_who __unused);
@@ -88,64 +84,86 @@
 
     static sp<BinderDeathObserver> sBinderDeathObserver;
 
-    enum Section {
-        SECTION_TOPLEVEL,
-        SECTION_SETTINGS,
-        SECTION_DECODERS,
-        SECTION_DECODER,
-        SECTION_DECODER_TYPE,
-        SECTION_ENCODERS,
-        SECTION_ENCODER,
-        SECTION_ENCODER_TYPE,
-        SECTION_INCLUDE,
-    };
-
     static sp<IMediaCodecList> sCodecList;
     static sp<IMediaCodecList> sRemoteList;
 
     status_t mInitCheck;
-    Section mCurrentSection;
-    bool mUpdate;
-    Vector<Section> mPastSections;
-    int32_t mDepth;
-    AString mHrefBase;
 
     sp<AMessage> mGlobalSettings;
-    KeyedVector<AString, CodecSettings> mOverrides;
+    std::vector<sp<MediaCodecInfo> > mCodecInfos;
 
-    Vector<sp<MediaCodecInfo> > mCodecInfos;
-    sp<MediaCodecInfo> mCurrentInfo;
+    /**
+     * This constructor will call `buildMediaCodecList()` from the given
+     * `MediaCodecListBuilderBase` object.
+     */
+    MediaCodecList(MediaCodecListBuilderBase* builder);
 
-    MediaCodecList();
     ~MediaCodecList();
 
     status_t initCheck() const;
-    void parseXMLFile(const char *path);
 
-    static void StartElementHandlerWrapper(
-            void *me, const char *name, const char **attrs);
+    MediaCodecList(const MediaCodecList&) = delete;
+    MediaCodecList& operator=(const MediaCodecList&) = delete;
 
-    static void EndElementHandlerWrapper(void *me, const char *name);
+    friend MediaCodecListWriter;
+};
 
-    void startElementHandler(const char *name, const char **attrs);
-    void endElementHandler(const char *name);
+/**
+ * This class is to be used by a `MediaCodecListBuilderBase` instance to add
+ * information to the associated `MediaCodecList` object.
+ */
+struct MediaCodecListWriter {
+    /**
+     * Add a key-value pair to a `MediaCodecList`'s global settings.
+     *
+     * @param key Key.
+     * @param value Value.
+     */
+    void addGlobalSetting(const char* key, const char* value);
+    /**
+     * Create an add a new `MediaCodecInfo` object to a `MediaCodecList`, and
+     * return a `MediaCodecInfoWriter` object associated with the newly added
+     * `MediaCodecInfo`.
+     *
+     * @return The `MediaCodecInfoWriter` object associated with the newly
+     * added `MediaCodecInfo` object.
+     */
+    std::unique_ptr<MediaCodecInfoWriter> addMediaCodecInfo();
+private:
+    /**
+     * The associated `MediaCodecList` object.
+     */
+    MediaCodecList* mList;
 
-    status_t includeXMLFile(const char **attrs);
-    status_t addSettingFromAttributes(const char **attrs);
-    status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
-    void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+    /**
+     * Construct this writer object associated with the given `MediaCodecList`
+     * object.
+     *
+     * @param list The "base" `MediaCodecList` object.
+     */
+    MediaCodecListWriter(MediaCodecList* list);
 
-    void setCurrentCodecInfo(bool encoder, const char *name, const char *type);
+    friend MediaCodecList;
+};
 
-    status_t addQuirk(const char **attrs);
-    status_t addTypeFromAttributes(const char **attrs);
-    status_t addLimit(const char **attrs);
-    status_t addFeature(const char **attrs);
-    void addType(const char *name);
+/**
+ * This interface is to be used by `MediaCodecList` to fill its members with
+ * appropriate information. `buildMediaCodecList()` will be called from a
+ * `MediaCodecList` object during its construction.
+ */
+struct MediaCodecListBuilderBase {
+    /**
+     * Build the `MediaCodecList` via the given `MediaCodecListWriter` interface.
+     *
+     * @param writer The writer interface.
+     * @return The status of the construction. `NO_ERROR` means success.
+     */
+    virtual status_t buildMediaCodecList(MediaCodecListWriter* writer) = 0;
 
-    status_t initializeCapabilities(const char *type);
-
-    DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
+    /**
+     * The default destructor does nothing.
+     */
+    virtual ~MediaCodecListBuilderBase();
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/OMXClient.h b/media/libstagefright/include/media/stagefright/OMXClient.h
index 203a181..2f159b0 100644
--- a/media/libstagefright/include/media/stagefright/OMXClient.h
+++ b/media/libstagefright/include/media/stagefright/OMXClient.h
@@ -28,9 +28,10 @@
 
     status_t connect();
     status_t connect(bool* trebleFlag);
+    status_t connect(const char* name, bool* trebleFlag = nullptr);
 
     status_t connectLegacy();
-    status_t connectTreble();
+    status_t connectTreble(const char* name = "default");
     void disconnect();
 
     sp<IOMX> interface() {
@@ -40,8 +41,8 @@
 private:
     sp<IOMX> mOMX;
 
-    OMXClient(const OMXClient &);
-    OMXClient &operator=(const OMXClient &);
+    OMXClient(const OMXClient &) = delete;
+    OMXClient &operator=(const OMXClient &) = delete;
 };
 
 }  // namespace android
diff --git a/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
new file mode 100644
index 0000000..1b4d873
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/OmxInfoBuilder.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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 OMX_INFO_BUILDER_H_
+#define OMX_INFO_BUILDER_H_
+
+#include <media/stagefright/MediaCodecList.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class OmxInfoBuilder : public MediaCodecListBuilderBase {
+public:
+    OmxInfoBuilder();
+    status_t buildMediaCodecList(MediaCodecListWriter* writer) override;
+};
+
+}  // namespace android
+
+#endif  // OMX_INFO_BUILDER_H_
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index dfab3b0..fe50656 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -114,15 +114,23 @@
             return Void();
         }
         instance->setHandle(handle);
-        std::vector<AString> quirkVector;
-        if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
+
+        // Find quirks from mParser
+        const auto& codec = mParser.getCodecMap().find(name.c_str());
+        if (codec == mParser.getCodecMap().cend()) {
+            LOG(WARNING) << "Failed to obtain quirks for omx component "
+                    "'" << name.c_str() << "' "
+                    "from XML files";
+        } else {
             uint32_t quirks = 0;
-            for (const AString quirk : quirkVector) {
+            for (const auto& quirk : codec->second.quirkSet) {
                 if (quirk == "requires-allocate-on-input-ports") {
-                    quirks |= kRequiresAllocateBufferOnInputPorts;
+                    quirks |= OMXNodeInstance::
+                            kRequiresAllocateBufferOnInputPorts;
                 }
                 if (quirk == "requires-allocate-on-output-ports") {
-                    quirks |= kRequiresAllocateBufferOnOutputPorts;
+                    quirks |= OMXNodeInstance::
+                            kRequiresAllocateBufferOnOutputPorts;
                 }
             }
             instance->setQuirks(quirks);
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
index a82625a..447af6f 100644
--- a/media/libstagefright/omx/1.0/OmxStore.cpp
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -21,6 +21,7 @@
 
 #include <media/stagefright/omx/1.0/Conversion.h>
 #include <media/stagefright/omx/1.0/OmxStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 
 namespace android {
 namespace hardware {
@@ -29,24 +30,87 @@
 namespace V1_0 {
 namespace implementation {
 
-OmxStore::OmxStore() {
+OmxStore::OmxStore(
+        const char* owner,
+        const char* const* searchDirs,
+        const char* mainXmlName,
+        const char* performanceXmlName,
+        const char* profilingResultsXmlPath) {
+    MediaCodecsXmlParser parser(searchDirs,
+            mainXmlName,
+            performanceXmlName,
+            profilingResultsXmlPath);
+    mParsingStatus = toStatus(parser.getParsingStatus());
+
+    const auto& serviceAttributeMap = parser.getServiceAttributeMap();
+    mServiceAttributeList.resize(serviceAttributeMap.size());
+    size_t i = 0;
+    for (const auto& attributePair : serviceAttributeMap) {
+        ServiceAttribute attribute;
+        attribute.key = attributePair.first;
+        attribute.value = attributePair.second;
+        mServiceAttributeList[i] = std::move(attribute);
+        ++i;
+    }
+
+    const auto& roleMap = parser.getRoleMap();
+    mRoleList.resize(roleMap.size());
+    i = 0;
+    for (const auto& rolePair : roleMap) {
+        RoleInfo role;
+        role.role = rolePair.first;
+        role.type = rolePair.second.type;
+        role.isEncoder = rolePair.second.isEncoder;
+        // TODO: Currently, preferPlatformNodes information is not available in
+        // the xml file. Once we have a way to provide this information, it
+        // should be parsed properly.
+        role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+        hidl_vec<NodeInfo>& nodeList = role.nodes;
+        nodeList.resize(rolePair.second.nodeList.size());
+        size_t j = 0;
+        for (const auto& nodePair : rolePair.second.nodeList) {
+            NodeInfo node;
+            node.name = nodePair.second.name;
+            node.owner = owner;
+            hidl_vec<NodeAttribute>& attributeList = node.attributes;
+            attributeList.resize(nodePair.second.attributeList.size());
+            size_t k = 0;
+            for (const auto& attributePair : nodePair.second.attributeList) {
+                NodeAttribute attribute;
+                attribute.key = attributePair.first;
+                attribute.value = attributePair.second;
+                attributeList[k] = std::move(attribute);
+                ++k;
+            }
+            nodeList[j] = std::move(node);
+            ++j;
+        }
+        mRoleList[i] = std::move(role);
+        ++i;
+    }
+
+    mPrefix = parser.getCommonPrefix();
 }
 
 OmxStore::~OmxStore() {
 }
 
 Return<void> OmxStore::listServiceAttributes(listServiceAttributes_cb _hidl_cb) {
-    _hidl_cb(toStatus(NO_ERROR), hidl_vec<ServiceAttribute>());
+    if (mParsingStatus == Status::NO_ERROR) {
+        _hidl_cb(Status::NO_ERROR, mServiceAttributeList);
+    } else {
+        _hidl_cb(mParsingStatus, hidl_vec<ServiceAttribute>());
+    }
     return Void();
 }
 
 Return<void> OmxStore::getNodePrefix(getNodePrefix_cb _hidl_cb) {
-    _hidl_cb(hidl_string());
+    _hidl_cb(mPrefix);
     return Void();
 }
 
 Return<void> OmxStore::listRoles(listRoles_cb _hidl_cb) {
-    _hidl_cb(hidl_vec<RoleInfo>());
+    _hidl_cb(mRoleList);
     return Void();
 }
 
@@ -54,12 +118,6 @@
     return IOmx::tryGetService(omxName);
 }
 
-// Methods from ::android::hidl::base::V1_0::IBase follow.
-
-IOmxStore* HIDL_FETCH_IOmxStore(const char* /* name */) {
-    return new OmxStore();
-}
-
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace omx
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index fcf1092..c4499dc 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -41,7 +41,9 @@
     sp<GraphicBuffer> buf;
     status_t status = mBase->requestBuffer(slot, &buf);
     AnwBuffer anwBuffer;
-    wrapAs(&anwBuffer, *buf);
+    if (buf != nullptr) {
+        wrapAs(&anwBuffer, *buf);
+    }
     _hidl_cb(static_cast<int32_t>(status), anwBuffer);
     return Void();
 }
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index bb05740..d4cdf69 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -10,6 +10,7 @@
         "GraphicBufferSource.cpp",
         "BWGraphicBufferSource.cpp",
         "OMX.cpp",
+        "OMXStore.cpp",
         "OMXMaster.cpp",
         "OMXNodeInstance.cpp",
         "OMXUtils.cpp",
@@ -32,15 +33,6 @@
         "include",
     ],
 
-    include_dirs: [
-        "frameworks/av/include/media/",
-        "frameworks/av/media/libstagefright",
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include", // for media/hardware/MetadataBufferType.h
-        "frameworks/native/include/media/hardware",
-        "frameworks/native/include/media/openmax",
-    ],
-
     header_libs: [
         "media_plugin_headers",
     ],
@@ -99,8 +91,12 @@
     },
 }
 
-cc_library_static {
+cc_library_shared {
     name: "libstagefright_omx_utils",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
     srcs: ["OMXUtils.cpp"],
     export_include_dirs: [
         "include",
@@ -112,10 +108,11 @@
         "media_plugin_headers",
     ],
     shared_libs: [
-        "libmedia",
+        "libmedia_omx",
+        "liblog",
     ],
     export_shared_lib_headers: [
-        "libmedia",
+        "libmedia_omx",
     ],
     sanitize: {
         misc_undefined: [
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 93b4dbe..09c4019 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -115,15 +115,22 @@
         return StatusFromOMXError(err);
     }
     instance->setHandle(handle);
-    std::vector<AString> quirkVector;
-    if (mParser.getQuirks(name, &quirkVector) == OK) {
+
+    // Find quirks from mParser
+    const auto& codec = mParser.getCodecMap().find(name);
+    if (codec == mParser.getCodecMap().cend()) {
+        ALOGW("Failed to obtain quirks for omx component '%s' from XML files",
+                name);
+    } else {
         uint32_t quirks = 0;
-        for (const AString quirk : quirkVector) {
+        for (const auto& quirk : codec->second.quirkSet) {
             if (quirk == "requires-allocate-on-input-ports") {
-                quirks |= kRequiresAllocateBufferOnInputPorts;
+                quirks |= OMXNodeInstance::
+                        kRequiresAllocateBufferOnInputPorts;
             }
             if (quirk == "requires-allocate-on-output-ports") {
-                quirks |= kRequiresAllocateBufferOnOutputPorts;
+                quirks |= OMXNodeInstance::
+                        kRequiresAllocateBufferOnOutputPorts;
             }
         }
         instance->setQuirks(quirks);
diff --git a/media/libstagefright/omx/OMXStore.cpp b/media/libstagefright/omx/OMXStore.cpp
new file mode 100644
index 0000000..345336d
--- /dev/null
+++ b/media/libstagefright/omx/OMXStore.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXStore"
+#include <utils/Log.h>
+
+#include <media/stagefright/omx/OMXUtils.h>
+#include <media/stagefright/omx/OMX.h>
+#include <media/stagefright/omx/OMXStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include <map>
+#include <string>
+
+namespace android {
+
+namespace {
+    struct RoleProperties {
+        std::string type;
+        bool isEncoder;
+        bool preferPlatformNodes;
+        std::multimap<size_t, IOMXStore::NodeInfo> nodeList;
+    };
+}  // Unnamed namespace
+
+OMXStore::OMXStore(
+        const char* owner,
+        const char* const* searchDirs,
+        const char* mainXmlName,
+        const char* performanceXmlName,
+        const char* profilingResultsXmlPath) {
+    MediaCodecsXmlParser parser(
+            searchDirs,
+            mainXmlName,
+            performanceXmlName,
+            profilingResultsXmlPath);
+    mParsingStatus = parser.getParsingStatus();
+
+    const auto& serviceAttributeMap = parser.getServiceAttributeMap();
+    mServiceAttributeList.reserve(serviceAttributeMap.size());
+    for (const auto& attributePair : serviceAttributeMap) {
+        Attribute attribute;
+        attribute.key = attributePair.first;
+        attribute.value = attributePair.second;
+        mServiceAttributeList.push_back(std::move(attribute));
+    }
+
+    const auto& roleMap = parser.getRoleMap();
+    mRoleList.reserve(roleMap.size());
+    for (const auto& rolePair : roleMap) {
+        RoleInfo role;
+        role.role = rolePair.first;
+        role.type = rolePair.second.type;
+        role.isEncoder = rolePair.second.isEncoder;
+        // TODO: Currently, preferPlatformNodes information is not available in
+        // the xml file. Once we have a way to provide this information, it
+        // should be parsed properly.
+        role.preferPlatformNodes = rolePair.first.compare(0, 5, "audio") == 0;
+        std::vector<NodeInfo>& nodeList = role.nodes;
+        nodeList.reserve(rolePair.second.nodeList.size());
+        for (const auto& nodePair : rolePair.second.nodeList) {
+            NodeInfo node;
+            node.name = nodePair.second.name;
+            node.owner = owner;
+            std::vector<Attribute>& attributeList = node.attributes;
+            attributeList.reserve(nodePair.second.attributeList.size());
+            for (const auto& attributePair : nodePair.second.attributeList) {
+                Attribute attribute;
+                attribute.key = attributePair.first;
+                attribute.value = attributePair.second;
+                attributeList.push_back(std::move(attribute));
+            }
+            nodeList.push_back(std::move(node));
+        }
+        mRoleList.push_back(std::move(role));
+    }
+
+    mPrefix = parser.getCommonPrefix();
+}
+
+status_t OMXStore::listServiceAttributes(std::vector<Attribute>* attributes) {
+    *attributes = mServiceAttributeList;
+    return mParsingStatus;
+}
+
+status_t OMXStore::getNodePrefix(std::string* prefix) {
+    *prefix = mPrefix;
+    return mParsingStatus;
+}
+
+status_t OMXStore::listRoles(std::vector<RoleInfo>* roleList) {
+    *roleList = mRoleList;
+    return mParsingStatus;
+}
+
+status_t OMXStore::getOmx(const std::string& name, sp<IOMX>* omx) {
+    *omx = new OMX();
+    return NO_ERROR;
+}
+
+OMXStore::~OMXStore() {
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
index f319bdc..8d8a2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/Conversion.h
@@ -2064,8 +2064,10 @@
     int const* constFds = static_cast<int const*>(baseFds.get());
     numFds = baseNumFds;
     if (l->unflatten(constBuffer, size, constFds, numFds) != NO_ERROR) {
-        native_handle_close(nh);
-        native_handle_delete(nh);
+        if (nh != nullptr) {
+            native_handle_close(nh);
+            native_handle_delete(nh);
+        }
         return false;
     }
 
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
index f377f5a..006d2d9 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/1.0/OmxStore.h
@@ -21,6 +21,7 @@
 #include <hidl/Status.h>
 
 #include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 
 namespace android {
 namespace hardware {
@@ -41,17 +42,31 @@
 using ::android::wp;
 
 struct OmxStore : public IOmxStore {
-    OmxStore();
+    OmxStore(
+            const char* owner = "default",
+            const char* const* searchDirs
+                = MediaCodecsXmlParser::defaultSearchDirs,
+            const char* mainXmlName
+                = MediaCodecsXmlParser::defaultMainXmlName,
+            const char* performanceXmlName
+                = MediaCodecsXmlParser::defaultPerformanceXmlName,
+            const char* profilingResultsXmlPath
+                = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+
     virtual ~OmxStore();
 
-    // Methods from IOmx
+    // Methods from IOmxStore
     Return<void> listServiceAttributes(listServiceAttributes_cb) override;
     Return<void> getNodePrefix(getNodePrefix_cb) override;
     Return<void> listRoles(listRoles_cb) override;
     Return<sp<IOmx>> getOmx(hidl_string const&) override;
-};
 
-extern "C" IOmxStore* HIDL_FETCH_IOmxStore(const char* name);
+protected:
+    Status mParsingStatus;
+    hidl_string mPrefix;
+    hidl_vec<ServiceAttribute> mServiceAttributeList;
+    hidl_vec<RoleInfo> mRoleList;
+};
 
 }  // namespace implementation
 }  // namespace V1_0
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
index 29b51a8..0f2d9a6 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/GraphicBufferSource.h
@@ -22,7 +22,7 @@
 #include <gui/BufferQueue.h>
 #include <utils/RefBase.h>
 
-#include <VideoAPI.h>
+#include <media/hardware/VideoAPI.h>
 #include <media/IOMX.h>
 #include <media/OMXFenceParcelable.h>
 #include <media/stagefright/foundation/ABase.h>
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
index 3f9c0ca..897f287 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXMaster.h
@@ -18,7 +18,7 @@
 
 #define OMX_MASTER_H_
 
-#include <OMXPluginBase.h>
+#include <media/hardware/OMXPluginBase.h>
 
 #include <utils/threads.h>
 #include <utils/KeyedVector.h>
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
index 8e08d15..19114e2 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXNodeInstance.h
@@ -92,6 +92,15 @@
     status_t getExtensionIndex(
             const char *parameterName, OMX_INDEXTYPE *index);
 
+    // Quirk still supported, even though deprecated
+    enum Quirks {
+        kRequiresAllocateBufferOnInputPorts   = 1,
+        kRequiresAllocateBufferOnOutputPorts  = 2,
+
+        kQuirksMask = kRequiresAllocateBufferOnInputPorts
+                    | kRequiresAllocateBufferOnOutputPorts,
+    };
+
     status_t setQuirks(OMX_U32 quirks);
 
     bool isSecure() const {
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
new file mode 100644
index 0000000..e00d713
--- /dev/null
+++ b/media/libstagefright/omx/include/media/stagefright/omx/OMXStore.h
@@ -0,0 +1,62 @@
+/*
+ * 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 ANDROID_OMXSTORE_H_
+#define ANDROID_OMXSTORE_H_
+
+#include <media/IOMXStore.h>
+#include <media/IOMX.h>
+#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+
+#include <vector>
+#include <string>
+
+namespace android {
+
+class OMXStore : public BnOMXStore {
+public:
+    OMXStore(
+            const char* owner = "default",
+            const char* const* searchDirs
+                = MediaCodecsXmlParser::defaultSearchDirs,
+            const char* mainXmlName
+                = MediaCodecsXmlParser::defaultMainXmlName,
+            const char* performanceXmlName
+                = MediaCodecsXmlParser::defaultPerformanceXmlName,
+            const char* profilingResultsXmlPath
+                = MediaCodecsXmlParser::defaultProfilingResultsXmlPath);
+
+    status_t listServiceAttributes(
+            std::vector<Attribute>* attributes) override;
+
+    status_t getNodePrefix(std::string* prefix) override;
+
+    status_t listRoles(std::vector<RoleInfo>* roleList) override;
+
+    status_t getOmx(const std::string& name, sp<IOMX>* omx) override;
+
+    ~OMXStore() override;
+
+protected:
+    status_t mParsingStatus;
+    std::string mPrefix;
+    std::vector<Attribute> mServiceAttributeList;
+    std::vector<RoleInfo> mRoleList;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_OMXSTORE_H_
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
index c89cd87..8ec717e 100644
--- a/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
+++ b/media/libstagefright/omx/include/media/stagefright/omx/SoftOMXPlugin.h
@@ -19,7 +19,7 @@
 #define SOFT_OMX_PLUGIN_H_
 
 #include <media/stagefright/foundation/ABase.h>
-#include <OMXPluginBase.h>
+#include <media/hardware/OMXPluginBase.h>
 
 namespace android {
 
diff --git a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
index 2599608..0c22a42 100644
--- a/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
+++ b/media/libstagefright/tests/MediaCodecListOverrides_test.cpp
@@ -26,6 +26,8 @@
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/MediaCodecList.h>
 
+#include <vector>
+
 namespace android {
 
 static const char kTestOverridesStr[] =
@@ -117,7 +119,7 @@
 // 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;
+    std::vector<sp<MediaCodecInfo>> infos;
     for (size_t i = 0; i < list->countCodecs(); ++i) {
         infos.push_back(list->getCodecInfo(i));
     }
diff --git a/media/libstagefright/xmlparser/Android.bp b/media/libstagefright/xmlparser/Android.bp
index ab893de..3507284 100644
--- a/media/libstagefright/xmlparser/Android.bp
+++ b/media/libstagefright/xmlparser/Android.bp
@@ -9,11 +9,6 @@
         "MediaCodecsXmlParser.cpp",
     ],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright",
-        "frameworks/av/include",
-    ],
-
     export_include_dirs: [
         "include",
     ],
@@ -24,6 +19,7 @@
         "liblog",
         "libcutils",
         "libstagefright_foundation",
+        "libstagefright_omx_utils",
     ],
 
     cflags: [
@@ -38,6 +34,10 @@
             "unsigned-integer-overflow",
             "signed-integer-overflow",
         ],
+        cfi: true,
+        diag: {
+            cfi: true,
+        },
     },
 
 }
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 4fdd107..bbd1807 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -16,157 +16,190 @@
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaCodecsXmlParser"
-#include <utils/Log.h>
 
 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
 
-#include <media/MediaCodecInfo.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/foundation/AUtils.h>
+#include <utils/Log.h>
 #include <media/stagefright/MediaErrors.h>
-
+#include <media/stagefright/omx/OMXUtils.h>
 #include <sys/stat.h>
-
 #include <expat.h>
-#include <string>
 
-#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
+#include <cctype>
+#include <algorithm>
 
 namespace android {
 
-namespace { // Local variables and functions
+namespace {
 
-const char *kProfilingResults =
-        "/data/misc/media/media_codecs_profiling_results.xml";
-
-// Treblized media codec list will be located in /odm/etc or /vendor/etc.
-const char *kConfigLocationList[] =
-        {"/odm/etc", "/vendor/etc", "/etc"};
-constexpr int kConfigLocationListSize =
-        (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
-bool findMediaCodecListFileFullPath(
-        const char *file_name, std::string *out_path) {
-    for (int i = 0; i < kConfigLocationListSize; i++) {
-        *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
-        struct stat file_stat;
-        if (stat(out_path->c_str(), &file_stat) == 0 &&
-                S_ISREG(file_stat.st_mode)) {
+/**
+ * Search for a file in a list of search directories.
+ *
+ * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
+ * tested whether it is a valid file name or not. If it is a valid file name,
+ * the concatenated name (`searchDir/fileName`) will be stored in the output
+ * variable `outPath`, and the function will return `true`. Otherwise, the
+ * search continues until the `nullptr` element in `searchDirs` is reached, at
+ * which point the function returns `false`.
+ *
+ * \param[in] searchDirs Null-terminated array of search paths.
+ * \param[in] fileName Name of the file to search.
+ * \param[out] outPath Full path of the file. `outPath` will hold a valid file
+ * name if the return value of this function is `true`.
+ * \return `true` if some element in `searchDirs` combined with `fileName` is a
+ * valid file name; `false` otherwise.
+ */
+bool findFileInDirs(
+        const char* const* searchDirs,
+        const char *fileName,
+        std::string *outPath) {
+    for (; *searchDirs != nullptr; ++searchDirs) {
+        *outPath = std::string(*searchDirs) + "/" + fileName;
+        struct stat fileStat;
+        if (stat(outPath->c_str(), &fileStat) == 0 &&
+                S_ISREG(fileStat.st_mode)) {
             return true;
         }
     }
     return false;
 }
 
-// Find TypeInfo by name.
-std::vector<TypeInfo>::iterator findTypeInfo(
-        CodecInfo &codecInfo, const AString &typeName) {
-    return std::find_if(
-            codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
-            [typeName](const auto &typeInfo) {
-                return typeInfo.mName == typeName;
-            });
+bool strnEq(const char* s1, const char* s2, size_t count) {
+    return strncmp(s1, s2, count) == 0;
 }
 
-// Convert a string into a boolean value.
-bool ParseBoolean(const char *s) {
-    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
-        return true;
-    }
-    char *end;
-    unsigned long res = strtoul(s, &end, 10);
-    return *s != '\0' && *end == '\0' && res > 0;
+bool strEq(const char* s1, const char* s2) {
+    return strcmp(s1, s2) == 0;
 }
 
-} // unnamed namespace
+bool striEq(const char* s1, const char* s2) {
+    return strcasecmp(s1, s2) == 0;
+}
 
-MediaCodecsXmlParser::MediaCodecsXmlParser() :
-    mInitCheck(NO_INIT),
-    mUpdate(false) {
-    std::string config_file_path;
-    if (findMediaCodecListFileFullPath(
-            "media_codecs.xml", &config_file_path)) {
-        parseTopLevelXMLFile(config_file_path.c_str(), false);
+bool strHasPrefix(const char* test, const char* prefix) {
+    return strnEq(test, prefix, strlen(prefix));
+}
+
+bool parseBoolean(const char* s) {
+    return striEq(s, "y") ||
+            striEq(s, "yes") ||
+            striEq(s, "t") ||
+            striEq(s, "true") ||
+            striEq(s, "1");
+}
+
+status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
+    ALOGE("limit '%s' with %s'%s' attribute", name,
+            (found ? "" : "no "), attr);
+    return -EINVAL;
+}
+
+status_t limitError(const char* name, const char *msg) {
+    ALOGE("limit '%s' %s", name, msg);
+    return -EINVAL;
+}
+
+status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
+    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
+            attr, value);
+    return -EINVAL;
+}
+
+}; // unnamed namespace
+
+constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
+constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
+constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
+constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
+
+MediaCodecsXmlParser::MediaCodecsXmlParser(
+        const char* const* searchDirs,
+        const char* mainXmlName,
+        const char* performanceXmlName,
+        const char* profilingResultsXmlPath) :
+    mParsingStatus(NO_INIT),
+    mUpdate(false),
+    mCodecCounter(0) {
+    std::string path;
+    if (findFileInDirs(searchDirs, mainXmlName, &path)) {
+        parseTopLevelXMLFile(path.c_str(), false);
     } else {
-        mInitCheck = NAME_NOT_FOUND;
+        ALOGE("Cannot find %s", mainXmlName);
+        mParsingStatus = NAME_NOT_FOUND;
     }
-    if (findMediaCodecListFileFullPath(
-            "media_codecs_performance.xml", &config_file_path)) {
-        parseTopLevelXMLFile(config_file_path.c_str(), true);
+    if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
+        parseTopLevelXMLFile(path.c_str(), true);
     }
-    parseTopLevelXMLFile(kProfilingResults, true);
+    if (profilingResultsXmlPath != nullptr) {
+        parseTopLevelXMLFile(profilingResultsXmlPath, true);
+    }
 }
 
-void MediaCodecsXmlParser::parseTopLevelXMLFile(
-        const char *codecs_xml, bool ignore_errors) {
+bool MediaCodecsXmlParser::parseTopLevelXMLFile(
+        const char *codecs_xml,
+        bool ignore_errors) {
     // get href_base
     const char *href_base_end = strrchr(codecs_xml, '/');
-    if (href_base_end != NULL) {
-        mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
+    if (href_base_end != nullptr) {
+        mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
     }
 
-    mInitCheck = OK; // keeping this here for safety
+    mParsingStatus = OK; // keeping this here for safety
     mCurrentSection = SECTION_TOPLEVEL;
-    mDepth = 0;
 
     parseXMLFile(codecs_xml);
 
-    if (mInitCheck != OK) {
+    if (mParsingStatus != OK) {
+        ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
         if (ignore_errors) {
-            mInitCheck = OK;
-            return;
+            mParsingStatus = OK;
+            return false;
         }
-        mCodecInfos.clear();
-        return;
+        mCodecMap.clear();
+        return false;
     }
+    return true;
 }
 
 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
 }
 
-status_t MediaCodecsXmlParser::initCheck() const {
-    return mInitCheck;
-}
-
 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
     FILE *file = fopen(path, "r");
 
-    if (file == NULL) {
+    if (file == nullptr) {
         ALOGW("unable to open media codecs configuration xml file: %s", path);
-        mInitCheck = NAME_NOT_FOUND;
+        mParsingStatus = NAME_NOT_FOUND;
         return;
     }
 
-    ALOGV("Start parsing %s", path);
-    XML_Parser parser = ::XML_ParserCreate(NULL);
-    CHECK(parser != NULL);
+    XML_Parser parser = ::XML_ParserCreate(nullptr);
+    LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
 
     ::XML_SetUserData(parser, this);
     ::XML_SetElementHandler(
             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
 
-    const int BUFF_SIZE = 512;
-    while (mInitCheck == OK) {
+    static constexpr int BUFF_SIZE = 512;
+    while (mParsingStatus == OK) {
         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
-        if (buff == NULL) {
+        if (buff == nullptr) {
             ALOGE("failed in call to XML_GetBuffer()");
-            mInitCheck = UNKNOWN_ERROR;
+            mParsingStatus = UNKNOWN_ERROR;
             break;
         }
 
         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
         if (bytes_read < 0) {
             ALOGE("failed in call to read");
-            mInitCheck = ERROR_IO;
+            mParsingStatus = ERROR_IO;
             break;
         }
 
         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
         if (status != XML_STATUS_OK) {
             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
-            mInitCheck = ERROR_MALFORMED;
+            mParsingStatus = ERROR_MALFORMED;
             break;
         }
 
@@ -178,30 +211,29 @@
     ::XML_ParserFree(parser);
 
     fclose(file);
-    file = NULL;
+    file = nullptr;
 }
 
 // static
 void MediaCodecsXmlParser::StartElementHandlerWrapper(
         void *me, const char *name, const char **attrs) {
-    static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
+    static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
 }
 
 // static
 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
-    static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
+    static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
 }
 
 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
-    const char *href = NULL;
+    const char *href = nullptr;
     size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "href")) {
-            if (attrs[i + 1] == NULL) {
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "href")) {
+            if (attrs[++i] == nullptr) {
                 return -EINVAL;
             }
-            href = attrs[i + 1];
-            ++i;
+            href = attrs[i];
         } else {
             ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
             return -EINVAL;
@@ -222,44 +254,43 @@
         return -EINVAL;
     }
 
-    AString filename = href;
-    if (!filename.startsWith("media_codecs_") ||
-        !filename.endsWith(".xml")) {
+    std::string filename = href;
+    if (filename.compare(0, 13, "media_codecs_") != 0 ||
+            filename.compare(filename.size() - 4, 4, ".xml") != 0) {
         ALOGE("invalid include file name: %s", href);
         return -EINVAL;
     }
-    filename.insert(mHrefBase, 0);
+    filename.insert(0, mHrefBase);
 
     parseXMLFile(filename.c_str());
-    return mInitCheck;
+    return mParsingStatus;
 }
 
 void MediaCodecsXmlParser::startElementHandler(
         const char *name, const char **attrs) {
-    if (mInitCheck != OK) {
+    if (mParsingStatus != OK) {
         return;
     }
 
     bool inType = true;
 
-    if (!strcmp(name, "Include")) {
-        mInitCheck = includeXMLFile(attrs);
-        if (mInitCheck == OK) {
-            mPastSections.push(mCurrentSection);
+    if (strEq(name, "Include")) {
+        mParsingStatus = includeXMLFile(attrs);
+        if (mParsingStatus == OK) {
+            mSectionStack.push_back(mCurrentSection);
             mCurrentSection = SECTION_INCLUDE;
         }
-        ++mDepth;
         return;
     }
 
     switch (mCurrentSection) {
         case SECTION_TOPLEVEL:
         {
-            if (!strcmp(name, "Decoders")) {
+            if (strEq(name, "Decoders")) {
                 mCurrentSection = SECTION_DECODERS;
-            } else if (!strcmp(name, "Encoders")) {
+            } else if (strEq(name, "Encoders")) {
                 mCurrentSection = SECTION_ENCODERS;
-            } else if (!strcmp(name, "Settings")) {
+            } else if (strEq(name, "Settings")) {
                 mCurrentSection = SECTION_SETTINGS;
             }
             break;
@@ -267,16 +298,16 @@
 
         case SECTION_SETTINGS:
         {
-            if (!strcmp(name, "Setting")) {
-                mInitCheck = addSettingFromAttributes(attrs);
+            if (strEq(name, "Setting")) {
+                mParsingStatus = addSettingFromAttributes(attrs);
             }
             break;
         }
 
         case SECTION_DECODERS:
         {
-            if (!strcmp(name, "MediaCodec")) {
-                mInitCheck =
+            if (strEq(name, "MediaCodec")) {
+                mParsingStatus =
                     addMediaCodecFromAttributes(false /* encoder */, attrs);
 
                 mCurrentSection = SECTION_DECODER;
@@ -286,8 +317,8 @@
 
         case SECTION_ENCODERS:
         {
-            if (!strcmp(name, "MediaCodec")) {
-                mInitCheck =
+            if (strEq(name, "MediaCodec")) {
+                mParsingStatus =
                     addMediaCodecFromAttributes(true /* encoder */, attrs);
 
                 mCurrentSection = SECTION_ENCODER;
@@ -298,13 +329,14 @@
         case SECTION_DECODER:
         case SECTION_ENCODER:
         {
-            if (!strcmp(name, "Quirk")) {
-                mInitCheck = addQuirk(attrs);
-            } else if (!strcmp(name, "Type")) {
-                mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
+            if (strEq(name, "Quirk")) {
+                mParsingStatus = addQuirk(attrs);
+            } else if (strEq(name, "Type")) {
+                mParsingStatus = addTypeFromAttributes(attrs,
+                        (mCurrentSection == SECTION_ENCODER));
                 mCurrentSection =
-                    (mCurrentSection == SECTION_DECODER
-                            ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
+                        (mCurrentSection == SECTION_DECODER ?
+                        SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
             }
         }
         inType = false;
@@ -314,13 +346,15 @@
         case SECTION_ENCODER_TYPE:
         {
             // ignore limits and features specified outside of type
-            bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
-            if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
+            bool outside = !inType &&
+                    mCurrentType == mCurrentCodec->second.typeMap.end();
+            if (outside &&
+                    (strEq(name, "Limit") || strEq(name, "Feature"))) {
                 ALOGW("ignoring %s specified outside of a Type", name);
-            } else if (!strcmp(name, "Limit")) {
-                mInitCheck = addLimit(attrs);
-            } else if (!strcmp(name, "Feature")) {
-                mInitCheck = addFeature(attrs);
+            } else if (strEq(name, "Limit")) {
+                mParsingStatus = addLimit(attrs);
+            } else if (strEq(name, "Feature")) {
+                mParsingStatus = addFeature(attrs);
             }
             break;
         }
@@ -329,18 +363,17 @@
             break;
     }
 
-    ++mDepth;
 }
 
 void MediaCodecsXmlParser::endElementHandler(const char *name) {
-    if (mInitCheck != OK) {
+    if (mParsingStatus != OK) {
         return;
     }
 
     switch (mCurrentSection) {
         case SECTION_SETTINGS:
         {
-            if (!strcmp(name, "Settings")) {
+            if (strEq(name, "Settings")) {
                 mCurrentSection = SECTION_TOPLEVEL;
             }
             break;
@@ -348,7 +381,7 @@
 
         case SECTION_DECODERS:
         {
-            if (!strcmp(name, "Decoders")) {
+            if (strEq(name, "Decoders")) {
                 mCurrentSection = SECTION_TOPLEVEL;
             }
             break;
@@ -356,7 +389,7 @@
 
         case SECTION_ENCODERS:
         {
-            if (!strcmp(name, "Encoders")) {
+            if (strEq(name, "Encoders")) {
                 mCurrentSection = SECTION_TOPLEVEL;
             }
             break;
@@ -365,19 +398,19 @@
         case SECTION_DECODER_TYPE:
         case SECTION_ENCODER_TYPE:
         {
-            if (!strcmp(name, "Type")) {
+            if (strEq(name, "Type")) {
                 mCurrentSection =
-                    (mCurrentSection == SECTION_DECODER_TYPE
-                            ? SECTION_DECODER : SECTION_ENCODER);
+                        (mCurrentSection == SECTION_DECODER_TYPE ?
+                        SECTION_DECODER : SECTION_ENCODER);
 
-                mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
+                mCurrentType = mCurrentCodec->second.typeMap.end();
             }
             break;
         }
 
         case SECTION_DECODER:
         {
-            if (!strcmp(name, "MediaCodec")) {
+            if (strEq(name, "MediaCodec")) {
                 mCurrentSection = SECTION_DECODERS;
                 mCurrentName.clear();
             }
@@ -386,7 +419,7 @@
 
         case SECTION_ENCODER:
         {
-            if (!strcmp(name, "MediaCodec")) {
+            if (strEq(name, "MediaCodec")) {
                 mCurrentSection = SECTION_ENCODERS;
                 mCurrentName.clear();
             }
@@ -395,9 +428,9 @@
 
         case SECTION_INCLUDE:
         {
-            if (!strcmp(name, "Include") && mPastSections.size() > 0) {
-                mCurrentSection = mPastSections.top();
-                mPastSections.pop();
+            if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
+                mCurrentSection = mSectionStack.back();
+                mSectionStack.pop_back();
             }
             break;
         }
@@ -406,253 +439,282 @@
             break;
     }
 
-    --mDepth;
 }
 
 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
-    const char *name = NULL;
-    const char *value = NULL;
-    const char *update = NULL;
+    const char *name = nullptr;
+    const char *value = nullptr;
+    const char *update = nullptr;
 
     size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "name")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addSettingFromAttributes: name is null");
                 return -EINVAL;
             }
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "value")) {
-            if (attrs[i + 1] == NULL) {
+            name = attrs[i];
+        } else if (strEq(attrs[i], "value")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addSettingFromAttributes: value is null");
                 return -EINVAL;
             }
-            value = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "update")) {
-            if (attrs[i + 1] == NULL) {
+            value = attrs[i];
+        } else if (strEq(attrs[i], "update")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addSettingFromAttributes: update is null");
                 return -EINVAL;
             }
-            update = attrs[i + 1];
-            ++i;
+            update = attrs[i];
         } else {
             ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
             return -EINVAL;
         }
-
         ++i;
     }
 
-    if (name == NULL || value == NULL) {
+    if (name == nullptr || value == nullptr) {
         ALOGE("addSettingFromAttributes: name or value unspecified");
         return -EINVAL;
     }
 
-    mUpdate = (update != NULL) && ParseBoolean(update);
-    if (mUpdate != (mGlobalSettings.count(name) > 0)) {
-        ALOGE("addSettingFromAttributes: updating non-existing setting");
-        return -EINVAL;
+    // Boolean values are converted to "0" or "1".
+    if (strHasPrefix(name, "supports-")) {
+        value = parseBoolean(value) ? "1" : "0";
     }
-    mGlobalSettings[name] = value;
+
+    mUpdate = (update != nullptr) && parseBoolean(update);
+    auto attribute = mServiceAttributeMap.find(name);
+    if (attribute == mServiceAttributeMap.end()) { // New attribute name
+        if (mUpdate) {
+            ALOGE("addSettingFromAttributes: updating non-existing setting");
+            return -EINVAL;
+        }
+        mServiceAttributeMap.insert(Attribute(name, value));
+    } else { // Existing attribute name
+        if (!mUpdate) {
+            ALOGE("addSettingFromAttributes: adding existing setting");
+        }
+        attribute->second = value;
+    }
 
     return OK;
 }
 
 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
         bool encoder, const char **attrs) {
-    const char *name = NULL;
-    const char *type = NULL;
-    const char *update = NULL;
+    const char *name = nullptr;
+    const char *type = nullptr;
+    const char *update = nullptr;
 
     size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "name")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addMediaCodecFromAttributes: name is null");
                 return -EINVAL;
             }
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "type")) {
-            if (attrs[i + 1] == NULL) {
+            name = attrs[i];
+        } else if (strEq(attrs[i], "type")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addMediaCodecFromAttributes: type is null");
                 return -EINVAL;
             }
-            type = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "update")) {
-            if (attrs[i + 1] == NULL) {
+            type = attrs[i];
+        } else if (strEq(attrs[i], "update")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addMediaCodecFromAttributes: update is null");
                 return -EINVAL;
             }
-            update = attrs[i + 1];
-            ++i;
+            update = attrs[i];
         } else {
             ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
             return -EINVAL;
         }
-
         ++i;
     }
 
-    if (name == NULL) {
+    if (name == nullptr) {
         ALOGE("addMediaCodecFromAttributes: name not found");
         return -EINVAL;
     }
 
-    mUpdate = (update != NULL) && ParseBoolean(update);
-    if (mUpdate != (mCodecInfos.count(name) > 0)) {
-        ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
-        return -EINVAL;
-    }
-
-    CodecInfo *info = &mCodecInfos[name];
-    if (mUpdate) {
-        // existing codec
-        mCurrentName = name;
-        mCurrentType = info->mTypes.begin();
-        if (type != NULL) {
-            // existing type
-            mCurrentType = findTypeInfo(*info, type);
-            if (mCurrentType == info->mTypes.end()) {
+    mUpdate = (update != nullptr) && parseBoolean(update);
+    mCurrentCodec = mCodecMap.find(name);
+    if (mCurrentCodec == mCodecMap.end()) { // New codec name
+        if (mUpdate) {
+            ALOGE("addMediaCodecFromAttributes: updating non-existing codec");
+            return -EINVAL;
+        }
+        // Create a new codec in mCodecMap
+        mCurrentCodec = mCodecMap.insert(
+                Codec(name, CodecProperties())).first;
+        if (type != nullptr) {
+            mCurrentType = mCurrentCodec->second.typeMap.insert(
+                    Type(type, AttributeMap())).first;
+        } else {
+            mCurrentType = mCurrentCodec->second.typeMap.end();
+        }
+        mCurrentCodec->second.isEncoder = encoder;
+        mCurrentCodec->second.order = mCodecCounter++;
+    } else { // Existing codec name
+        if (!mUpdate) {
+            ALOGE("addMediaCodecFromAttributes: adding existing codec");
+            return -EINVAL;
+        }
+        if (type != nullptr) {
+            mCurrentType = mCurrentCodec->second.typeMap.find(type);
+            if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
                 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
                 return -EINVAL;
             }
+        } else {
+            // This should happen only when the codec has at most one type.
+            mCurrentType = mCurrentCodec->second.typeMap.begin();
         }
-    } else {
-        // new codec
-        mCurrentName = name;
-        mQuirks[name].clear();
-        info->mTypes.clear();
-        info->mTypes.emplace_back();
-        mCurrentType = --info->mTypes.end();
-        mCurrentType->mName = type;
-        info->mIsEncoder = encoder;
     }
 
     return OK;
 }
 
 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
-    const char *name = NULL;
+    const char *name = nullptr;
 
     size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "name")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addQuirk: name is null");
                 return -EINVAL;
             }
-            name = attrs[i + 1];
-            ++i;
+            name = attrs[i];
         } else {
             ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
             return -EINVAL;
         }
-
         ++i;
     }
 
-    if (name == NULL) {
+    if (name == nullptr) {
         ALOGE("addQuirk: name not found");
         return -EINVAL;
     }
 
-    mQuirks[mCurrentName].emplace_back(name);
+    mCurrentCodec->second.quirkSet.emplace(name);
     return OK;
 }
 
 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
-    const char *name = NULL;
-    const char *update = NULL;
+    const char *name = nullptr;
+    const char *update = nullptr;
 
     size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (!strcmp(attrs[i], "name")) {
-            if (attrs[i + 1] == NULL) {
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "name")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addTypeFromAttributes: name is null");
                 return -EINVAL;
             }
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "update")) {
-            if (attrs[i + 1] == NULL) {
+            name = attrs[i];
+        } else if (strEq(attrs[i], "update")) {
+            if (attrs[++i] == nullptr) {
                 ALOGE("addTypeFromAttributes: update is null");
                 return -EINVAL;
             }
-            update = attrs[i + 1];
-            ++i;
+            update = attrs[i];
         } else {
             ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
             return -EINVAL;
         }
-
         ++i;
     }
 
-    if (name == NULL) {
+    if (name == nullptr) {
         return -EINVAL;
     }
 
-    CodecInfo *info = &mCodecInfos[mCurrentName];
-    info->mIsEncoder = encoder;
-    mCurrentType = findTypeInfo(*info, name);
+    mCurrentCodec->second.isEncoder = encoder;
+    mCurrentType = mCurrentCodec->second.typeMap.find(name);
     if (!mUpdate) {
-        if (mCurrentType != info->mTypes.end()) {
+        if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
             ALOGE("addTypeFromAttributes: re-defining existing type without update");
             return -EINVAL;
         }
-        info->mTypes.emplace_back();
-        mCurrentType = --info->mTypes.end();
-    } else if (mCurrentType == info->mTypes.end()) {
+        mCurrentType = mCurrentCodec->second.typeMap.insert(
+                Type(name, AttributeMap())).first;
+    } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
         ALOGE("addTypeFromAttributes: updating non-existing type");
-        return -EINVAL;
     }
-
     return OK;
 }
 
-static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
-    ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
-            (found ? "" : "no "), attr);
-    return -EINVAL;
-}
-
-static status_t limitError(const AString &name, const char *msg) {
-    ALOGE("limit '%s' %s", name.c_str(), msg);
-    return -EINVAL;
-}
-
-static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
-    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
-            attr, value.c_str());
-    return -EINVAL;
-}
-
 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
-    sp<AMessage> msg = new AMessage();
+    const char* a_name = nullptr;
+    const char* a_default = nullptr;
+    const char* a_in = nullptr;
+    const char* a_max = nullptr;
+    const char* a_min = nullptr;
+    const char* a_range = nullptr;
+    const char* a_ranges = nullptr;
+    const char* a_scale = nullptr;
+    const char* a_value = nullptr;
 
     size_t i = 0;
-    while (attrs[i] != NULL) {
-        if (attrs[i + 1] == NULL) {
-            ALOGE("addLimit: limit is not given");
-            return -EINVAL;
-        }
-
-        // attributes with values
-        if (!strcmp(attrs[i], "name")
-                || !strcmp(attrs[i], "default")
-                || !strcmp(attrs[i], "in")
-                || !strcmp(attrs[i], "max")
-                || !strcmp(attrs[i], "min")
-                || !strcmp(attrs[i], "range")
-                || !strcmp(attrs[i], "ranges")
-                || !strcmp(attrs[i], "scale")
-                || !strcmp(attrs[i], "value")) {
-            msg->setString(attrs[i], attrs[i + 1]);
-            ++i;
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "name")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: name is null");
+                return -EINVAL;
+            }
+            a_name = attrs[i];
+        } else if (strEq(attrs[i], "default")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: default is null");
+                return -EINVAL;
+            }
+            a_default = attrs[i];
+        } else if (strEq(attrs[i], "in")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: in is null");
+                return -EINVAL;
+            }
+            a_in = attrs[i];
+        } else if (strEq(attrs[i], "max")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: max is null");
+                return -EINVAL;
+            }
+            a_max = attrs[i];
+        } else if (strEq(attrs[i], "min")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: min is null");
+                return -EINVAL;
+            }
+            a_min = attrs[i];
+        } else if (strEq(attrs[i], "range")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: range is null");
+                return -EINVAL;
+            }
+            a_range = attrs[i];
+        } else if (strEq(attrs[i], "ranges")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: ranges is null");
+                return -EINVAL;
+            }
+            a_ranges = attrs[i];
+        } else if (strEq(attrs[i], "scale")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: scale is null");
+                return -EINVAL;
+            }
+            a_scale = attrs[i];
+        } else if (strEq(attrs[i], "value")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addLimit: value is null");
+                return -EINVAL;
+            }
+            a_value = attrs[i];
         } else {
             ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
             return -EINVAL;
@@ -660,8 +722,7 @@
         ++i;
     }
 
-    AString name;
-    if (!msg->findString("name", &name)) {
+    if (a_name == nullptr) {
         ALOGE("limit with no 'name' attribute");
         return -EINVAL;
     }
@@ -670,109 +731,148 @@
     // measured-frame-rate, measured-blocks-per-second: range
     // quality: range + default + [scale]
     // complexity: range + default
-    bool found;
-    if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+    if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
         ALOGW("ignoring null type");
         return OK;
     }
 
-    if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
-            || name == "blocks-per-second" || name == "complexity"
-            || name == "frame-rate" || name == "quality" || name == "size"
-            || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
-        AString min, max;
-        if (msg->findString("min", &min) && msg->findString("max", &max)) {
-            min.append("-");
-            min.append(max);
-            if (msg->contains("range") || msg->contains("value")) {
-                return limitError(name, "has 'min' and 'max' as well as 'range' or "
+    std::string range;
+    if (strEq(a_name, "aspect-ratio") ||
+            strEq(a_name, "bitrate") ||
+            strEq(a_name, "block-count") ||
+            strEq(a_name, "blocks-per-second") ||
+            strEq(a_name, "complexity") ||
+            strEq(a_name, "frame-rate") ||
+            strEq(a_name, "quality") ||
+            strEq(a_name, "size") ||
+            strEq(a_name, "measured-blocks-per-second") ||
+            strHasPrefix(a_name, "measured-frame-rate-")) {
+        // "range" is specified in exactly one of the following forms:
+        // 1) min-max
+        // 2) value-value
+        // 3) range
+        if (a_min != nullptr && a_max != nullptr) {
+            // min-max
+            if (a_range != nullptr || a_value != nullptr) {
+                return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
                         "'value' attributes");
             }
-            msg->setString("range", min);
-        } else if (msg->contains("min") || msg->contains("max")) {
-            return limitError(name, "has only 'min' or 'max' attribute");
-        } else if (msg->findString("value", &max)) {
-            min = max;
-            min.append("-");
-            min.append(max);
-            if (msg->contains("range")) {
-                return limitError(name, "has both 'range' and 'value' attributes");
+            range = a_min;
+            range += '-';
+            range += a_max;
+        } else if (a_min != nullptr || a_max != nullptr) {
+            return limitError(a_name, "has only 'min' or 'max' attribute");
+        } else if (a_value != nullptr) {
+            // value-value
+            if (a_range != nullptr) {
+                return limitError(a_name, "has both 'range' and 'value' attributes");
             }
-            msg->setString("range", min);
-        }
-
-        AString range, scale = "linear", def, in_;
-        if (!msg->findString("range", &range)) {
-            return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
-        }
-
-        if ((name == "quality" || name == "complexity") ^
-                (found = msg->findString("default", &def))) {
-            return limitFoundMissingAttr(name, "default", found);
-        }
-        if (name != "quality" && msg->findString("scale", &scale)) {
-            return limitFoundMissingAttr(name, "scale");
-        }
-        if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
-            return limitFoundMissingAttr(name, "in", found);
-        }
-
-        if (name == "aspect-ratio") {
-            if (!(in_ == "pixels") && !(in_ == "blocks")) {
-                return limitInvalidAttr(name, "in", in_);
-            }
-            in_.erase(5, 1); // (pixel|block)-aspect-ratio
-            in_.append("-");
-            in_.append(name);
-            name = in_;
-        }
-        if (name == "quality") {
-            mCurrentType->mDetails["quality-scale"] = scale;
-        }
-        if (name == "quality" || name == "complexity") {
-            AString tag = name;
-            tag.append("-default");
-            mCurrentType->mDetails[tag] = def;
-        }
-        AString tag = name;
-        tag.append("-range");
-        mCurrentType->mDetails[tag] = range;
-    } else {
-        AString max, value, ranges;
-        if (msg->contains("default")) {
-            return limitFoundMissingAttr(name, "default");
-        } else if (msg->contains("in")) {
-            return limitFoundMissingAttr(name, "in");
-        } else if ((name == "channel-count" || name == "concurrent-instances") ^
-                (found = msg->findString("max", &max))) {
-            return limitFoundMissingAttr(name, "max", found);
-        } else if (msg->contains("min")) {
-            return limitFoundMissingAttr(name, "min");
-        } else if (msg->contains("range")) {
-            return limitFoundMissingAttr(name, "range");
-        } else if ((name == "sample-rate") ^
-                (found = msg->findString("ranges", &ranges))) {
-            return limitFoundMissingAttr(name, "ranges", found);
-        } else if (msg->contains("scale")) {
-            return limitFoundMissingAttr(name, "scale");
-        } else if ((name == "alignment" || name == "block-size") ^
-                (found = msg->findString("value", &value))) {
-            return limitFoundMissingAttr(name, "value", found);
-        }
-
-        if (max.size()) {
-            AString tag = "max-";
-            tag.append(name);
-            mCurrentType->mDetails[tag] = max;
-        } else if (value.size()) {
-            mCurrentType->mDetails[name] = value;
-        } else if (ranges.size()) {
-            AString tag = name;
-            tag.append("-ranges");
-            mCurrentType->mDetails[tag] = ranges;
+            range = a_value;
+            range += '-';
+            range += a_value;
+        } else if (a_range == nullptr) {
+            return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
         } else {
-            ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
+            // range
+            range = a_range;
         }
+
+        // "aspect-ratio" requires some special treatment.
+        if (strEq(a_name, "aspect-ratio")) {
+            // "aspect-ratio" must have "in".
+            if (a_in == nullptr) {
+                return limitFoundMissingAttr(a_name, "in", false);
+            }
+            // "in" must be either "pixels" or "blocks".
+            if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
+                return limitInvalidAttr(a_name, "in", a_in);
+            }
+            // name will be "pixel-aspect-ratio-range" or
+            // "block-aspect-ratio-range".
+            mCurrentType->second[
+                    std::string(a_in).substr(0, strlen(a_in) - 1) +
+                    "-aspect-ratio-range"] = range;
+        } else {
+            // For everything else (apart from "aspect-ratio"), simply append
+            // "-range" to the name for the range-type property.
+            mCurrentType->second[std::string(a_name) + "-range"] = range;
+
+            // Only "quality" may have "scale".
+            if (!strEq(a_name, "quality") && a_scale != nullptr) {
+                return limitFoundMissingAttr(a_name, "scale");
+            } else if (strEq(a_name, "quality")) {
+                // The default value of "quality-scale" is "linear".
+                mCurrentType->second["quality-scale"] = a_scale == nullptr ?
+                        "linear" : a_scale;
+            }
+
+            // "quality" and "complexity" must have "default".
+            // Other limits must not have "default".
+            if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
+                if (a_default == nullptr) {
+                    return limitFoundMissingAttr(a_name, "default", false);
+                }
+                // name will be "quality-default" or "complexity-default".
+                mCurrentType->second[std::string(a_name) + "-default"] = a_default;
+            } else if (a_default != nullptr) {
+                return limitFoundMissingAttr(a_name, "default", true);
+            }
+        }
+    } else {
+        if (a_default != nullptr) {
+            return limitFoundMissingAttr(a_name, "default");
+        }
+        if (a_in != nullptr) {
+            return limitFoundMissingAttr(a_name, "in");
+        }
+        if (a_scale != nullptr) {
+            return limitFoundMissingAttr(a_name, "scale");
+        }
+        if (a_range != nullptr) {
+            return limitFoundMissingAttr(a_name, "range");
+        }
+        if (a_min != nullptr) {
+            return limitFoundMissingAttr(a_name, "min");
+        }
+
+        if (a_max != nullptr) {
+            // "max" must exist if and only if name is "channel-count" or
+            // "concurrent-instances".
+            // "min" is not ncessary.
+            if (strEq(a_name, "channel-count") ||
+                    strEq(a_name, "concurrent-instances")) {
+                mCurrentType->second[std::string("max-") + a_name] = a_max;
+            } else {
+                return limitFoundMissingAttr(a_name, "max", false);
+            }
+        } else if (strEq(a_name, "channel-count") ||
+                strEq(a_name, "concurrent-instances")) {
+            return limitFoundMissingAttr(a_name, "max");
+        }
+
+        if (a_ranges != nullptr) {
+            // "ranges" must exist if and only if name is "sample-rate".
+            if (strEq(a_name, "sample-rate")) {
+                mCurrentType->second["sample-rate-ranges"] = a_ranges;
+            } else {
+                return limitFoundMissingAttr(a_name, "ranges", false);
+            }
+        } else if (strEq(a_name, "sample-rate")) {
+            return limitFoundMissingAttr(a_name, "ranges");
+        }
+
+        if (a_value != nullptr) {
+            // "value" must exist if and only if name is "alignment" or
+            // "block-size".
+            if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
+                mCurrentType->second[a_name] = a_value;
+            } else {
+                return limitFoundMissingAttr(a_name, "value", false);
+            }
+        } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
+            return limitFoundMissingAttr(a_name, "value", false);
+        }
+
     }
 
     return OK;
@@ -780,83 +880,169 @@
 
 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
     size_t i = 0;
-    const char *name = NULL;
+    const char *name = nullptr;
     int32_t optional = -1;
     int32_t required = -1;
-    const char *value = NULL;
+    const char *value = nullptr;
 
-    while (attrs[i] != NULL) {
-        if (attrs[i + 1] == NULL) {
-            ALOGE("addFeature: feature is not given");
-            return -EINVAL;
-        }
-
-        // attributes with values
-        if (!strcmp(attrs[i], "name")) {
-            name = attrs[i + 1];
-            ++i;
-        } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
-            int value = (int)ParseBoolean(attrs[i + 1]);
-            if (!strcmp(attrs[i], "optional")) {
-                optional = value;
-            } else {
-                required = value;
+    while (attrs[i] != nullptr) {
+        if (strEq(attrs[i], "name")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addFeature: name is null");
+                return -EINVAL;
             }
-            ++i;
-        } else if (!strcmp(attrs[i], "value")) {
-            value = attrs[i + 1];
-            ++i;
+            name = attrs[i];
+        } else if (strEq(attrs[i], "optional")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addFeature: optional is null");
+                return -EINVAL;
+            }
+            optional = parseBoolean(attrs[i]) ? 1 : 0;
+        } else if (strEq(attrs[i], "required")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addFeature: required is null");
+                return -EINVAL;
+            }
+            required = parseBoolean(attrs[i]) ? 1 : 0;
+        } else if (strEq(attrs[i], "value")) {
+            if (attrs[++i] == nullptr) {
+                ALOGE("addFeature: value is null");
+                return -EINVAL;
+            }
+            value = attrs[i];
         } else {
             ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
             return -EINVAL;
         }
         ++i;
     }
-    if (name == NULL) {
+
+    // Every feature must have a name.
+    if (name == nullptr) {
         ALOGE("feature with no 'name' attribute");
         return -EINVAL;
     }
 
-    if (optional == required && optional != -1) {
-        ALOGE("feature '%s' is both/neither optional and required", name);
-        return -EINVAL;
-    }
-
-    if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
+    if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
         ALOGW("ignoring null type");
         return OK;
     }
-    if (value != NULL) {
-        mCurrentType->mStringFeatures[name] = value;
-    } else {
-        mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
+
+    if ((optional != -1) || (required != -1)) {
+        if (optional == required) {
+            ALOGE("feature '%s' is both/neither optional and required", name);
+            return -EINVAL;
+        }
+        if ((optional == 1) || (required == 1)) {
+            if (value != nullptr) {
+                ALOGE("feature '%s' cannot have extra 'value'", name);
+                return -EINVAL;
+            }
+            mCurrentType->second[std::string("feature-") + name] =
+                    optional == 1 ? "0" : "1";
+            return OK;
+        }
     }
+    mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
+            "0" : value;
     return OK;
 }
 
-void MediaCodecsXmlParser::getGlobalSettings(
-        std::map<AString, AString> *settings) const {
-    settings->clear();
-    settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
+const MediaCodecsXmlParser::AttributeMap&
+        MediaCodecsXmlParser::getServiceAttributeMap() const {
+    return mServiceAttributeMap;
 }
 
-status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
-    if (mCodecInfos.count(name) == 0) {
-        ALOGE("Codec not found with name '%s'", name);
-        return NAME_NOT_FOUND;
+const MediaCodecsXmlParser::CodecMap&
+        MediaCodecsXmlParser::getCodecMap() const {
+    return mCodecMap;
+}
+
+const MediaCodecsXmlParser::RoleMap&
+        MediaCodecsXmlParser::getRoleMap() const {
+    if (mRoleMap.empty()) {
+        generateRoleMap();
     }
-    *info = mCodecInfos.at(name);
-    return OK;
+    return mRoleMap;
 }
 
-status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
-    if (mQuirks.count(name) == 0) {
-        ALOGE("Codec not found with name '%s'", name);
-        return NAME_NOT_FOUND;
+const char* MediaCodecsXmlParser::getCommonPrefix() const {
+    if (mCommonPrefix.empty()) {
+        generateCommonPrefix();
     }
-    quirks->clear();
-    quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
-    return OK;
+    return mCommonPrefix.data();
 }
 
-}  // namespace android
+status_t MediaCodecsXmlParser::getParsingStatus() const {
+    return mParsingStatus;
+}
+
+void MediaCodecsXmlParser::generateRoleMap() const {
+    for (const auto& codec : mCodecMap) {
+        const auto& codecName = codec.first;
+        bool isEncoder = codec.second.isEncoder;
+        size_t order = codec.second.order;
+        const auto& typeMap = codec.second.typeMap;
+        for (const auto& type : typeMap) {
+            const auto& typeName = type.first;
+            const char* roleName = GetComponentRole(isEncoder, typeName.data());
+            const auto& typeAttributeMap = type.second;
+
+            auto roleIterator = mRoleMap.find(roleName);
+            std::multimap<size_t, NodeInfo>* nodeList;
+            if (roleIterator == mRoleMap.end()) {
+                RoleProperties roleProperties;
+                roleProperties.type = typeName;
+                roleProperties.isEncoder = isEncoder;
+                auto insertResult = mRoleMap.insert(
+                        std::make_pair(roleName, roleProperties));
+                if (!insertResult.second) {
+                    ALOGE("Cannot add role %s", roleName);
+                    continue;
+                }
+                nodeList = &insertResult.first->second.nodeList;
+            } else {
+                if (roleIterator->second.type != typeName) {
+                    ALOGE("Role %s has mismatching types: %s and %s",
+                            roleName,
+                            roleIterator->second.type.data(),
+                            typeName.data());
+                    continue;
+                }
+                if (roleIterator->second.isEncoder != isEncoder) {
+                    ALOGE("Role %s cannot be both an encoder and a decoder",
+                            roleName);
+                    continue;
+                }
+                nodeList = &roleIterator->second.nodeList;
+            }
+
+            NodeInfo nodeInfo;
+            nodeInfo.name = codecName;
+            nodeInfo.attributeList.reserve(typeAttributeMap.size());
+            for (const auto& attribute : typeAttributeMap) {
+                nodeInfo.attributeList.push_back(
+                        Attribute{attribute.first, attribute.second});
+            }
+            nodeList->insert(std::make_pair(
+                    std::move(order), std::move(nodeInfo)));
+        }
+    }
+}
+
+void MediaCodecsXmlParser::generateCommonPrefix() const {
+    if (mCodecMap.empty()) {
+        return;
+    }
+    auto i = mCodecMap.cbegin();
+    auto first = i->first.cbegin();
+    auto last = i->first.cend();
+    for (++i; i != mCodecMap.cend(); ++i) {
+        last = std::mismatch(
+                first, last, i->first.cbegin(), i->first.cend()).first;
+    }
+    mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
index b324cd8..cc69e52 100644
--- a/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
+++ b/media/libstagefright/xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h
@@ -14,65 +14,107 @@
  * limitations under the License.
  */
 
-#ifndef MEDIA_CODECS_XML_PARSER_H_
-
-#define MEDIA_CODECS_XML_PARSER_H_
-
-#include <map>
-#include <vector>
-
-#include <media/stagefright/foundation/ABase.h>
-#include <media/stagefright/foundation/AString.h>
+#ifndef MEDIA_STAGEFRIGHT_XMLPARSER_H_
+#define MEDIA_STAGEFRIGHT_XMLPARSER_H_
 
 #include <sys/types.h>
 #include <utils/Errors.h>
 #include <utils/Vector.h>
 #include <utils/StrongPointer.h>
 
+#include <string>
+#include <set>
+#include <map>
+#include <vector>
+
 namespace android {
 
-struct AMessage;
-
-// Quirk still supported, even though deprecated
-enum Quirks {
-    kRequiresAllocateBufferOnInputPorts   = 1,
-    kRequiresAllocateBufferOnOutputPorts  = 2,
-
-    kQuirksMask = kRequiresAllocateBufferOnInputPorts
-                | kRequiresAllocateBufferOnOutputPorts,
-};
-
-// Lightweight struct for querying components.
-struct TypeInfo {
-    AString mName;
-    std::map<AString, AString> mStringFeatures;
-    std::map<AString, bool> mBoolFeatures;
-    std::map<AString, AString> mDetails;
-};
-
-struct ProfileLevel {
-    uint32_t mProfile;
-    uint32_t mLevel;
-};
-
-struct CodecInfo {
-    std::vector<TypeInfo> mTypes;
-    std::vector<ProfileLevel> mProfileLevels;
-    std::vector<uint32_t> mColorFormats;
-    uint32_t mFlags;
-    bool mIsEncoder;
-};
-
 class MediaCodecsXmlParser {
 public:
-    MediaCodecsXmlParser();
+
+    // Treblized media codec list will be located in /odm/etc or /vendor/etc.
+    static constexpr char const* defaultSearchDirs[] =
+            {"/odm/etc", "/vendor/etc", "/etc", nullptr};
+    static constexpr char const* defaultMainXmlName =
+            "media_codecs.xml";
+    static constexpr char const* defaultPerformanceXmlName =
+            "media_codecs_performance.xml";
+    static constexpr char const* defaultProfilingResultsXmlPath =
+            "/data/misc/media/media_codecs_profiling_results.xml";
+
+    MediaCodecsXmlParser(
+            const char* const* searchDirs = defaultSearchDirs,
+            const char* mainXmlName = defaultMainXmlName,
+            const char* performanceXmlName = defaultPerformanceXmlName,
+            const char* profilingResultsXmlPath = defaultProfilingResultsXmlPath);
     ~MediaCodecsXmlParser();
 
-    void getGlobalSettings(std::map<AString, AString> *settings) const;
+    typedef std::pair<std::string, std::string> Attribute;
+    typedef std::map<std::string, std::string> AttributeMap;
 
-    status_t getCodecInfo(const char *name, CodecInfo *info) const;
+    typedef std::pair<std::string, AttributeMap> Type;
+    typedef std::map<std::string, AttributeMap> TypeMap;
 
-    status_t getQuirks(const char *name, std::vector<AString> *quirks) const;
+    typedef std::set<std::string> QuirkSet;
+
+    /**
+     * Properties of a codec (node)
+     */
+    struct CodecProperties {
+        bool isEncoder;    ///< Whether this codec is an encoder or a decoder
+        size_t order;      ///< Order of appearance in the file (starting from 0)
+        QuirkSet quirkSet; ///< Set of quirks requested by this codec
+        TypeMap typeMap;   ///< Map of types supported by this codec
+    };
+
+    typedef std::pair<std::string, CodecProperties> Codec;
+    typedef std::map<std::string, CodecProperties> CodecMap;
+
+    /**
+     * Properties of a node (for IOmxStore)
+     */
+    struct NodeInfo {
+        std::string name;
+        std::vector<Attribute> attributeList;
+    };
+
+    /**
+     * Properties of a role (for IOmxStore)
+     */
+    struct RoleProperties {
+        std::string type;
+        bool isEncoder;
+        std::multimap<size_t, NodeInfo> nodeList;
+    };
+
+    typedef std::pair<std::string, RoleProperties> Role;
+    typedef std::map<std::string, RoleProperties> RoleMap;
+
+    /**
+     * Return a map for attributes that are service-specific.
+     */
+    const AttributeMap& getServiceAttributeMap() const;
+
+    /**
+     * Return a map for codecs and their properties.
+     */
+    const CodecMap& getCodecMap() const;
+
+    /**
+     * Return a map for roles and their properties.
+     * This map is generated from the CodecMap.
+     */
+    const RoleMap& getRoleMap() const;
+
+    /**
+     * Return a common prefix of all node names.
+     *
+     * The prefix is not provided in the xml, so it has to be computed by taking
+     * the longest common prefix of all node names.
+     */
+    const char* getCommonPrefix() const;
+
+    status_t getParsingStatus() const;
 
 private:
     enum Section {
@@ -87,23 +129,31 @@
         SECTION_INCLUDE,
     };
 
-    status_t mInitCheck;
+    status_t mParsingStatus;
     Section mCurrentSection;
     bool mUpdate;
-    Vector<Section> mPastSections;
-    int32_t mDepth;
-    AString mHrefBase;
+    std::vector<Section> mSectionStack;
+    std::string mHrefBase;
 
-    std::map<AString, AString> mGlobalSettings;
+    // Service attributes
+    AttributeMap mServiceAttributeMap;
 
-    // name -> CodecInfo
-    std::map<AString, CodecInfo> mCodecInfos;
-    std::map<AString, std::vector<AString>> mQuirks;
-    AString mCurrentName;
-    std::vector<TypeInfo>::iterator mCurrentType;
+    // Codec attributes
+    std::string mCurrentName;
+    std::set<std::string> mCodecSet;
+    Codec mCodecListTemp[2048];
+    CodecMap mCodecMap;
+    size_t mCodecCounter;
+    CodecMap::iterator mCurrentCodec;
+    TypeMap::iterator mCurrentType;
 
-    status_t initCheck() const;
-    void parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
+    // Role map
+    mutable RoleMap mRoleMap;
+
+    // Computed longest common prefix
+    mutable std::string mCommonPrefix;
+
+    bool parseTopLevelXMLFile(const char *path, bool ignore_errors = false);
 
     void parseXMLFile(const char *path);
 
@@ -118,7 +168,8 @@
     status_t includeXMLFile(const char **attrs);
     status_t addSettingFromAttributes(const char **attrs);
     status_t addMediaCodecFromAttributes(bool encoder, const char **attrs);
-    void addMediaCodec(bool encoder, const char *name, const char *type = NULL);
+    void addMediaCodec(bool encoder, const char *name,
+            const char *type = nullptr);
 
     status_t addQuirk(const char **attrs);
     status_t addTypeFromAttributes(const char **attrs, bool encoder);
@@ -126,10 +177,14 @@
     status_t addFeature(const char **attrs);
     void addType(const char *name);
 
-    DISALLOW_EVIL_CONSTRUCTORS(MediaCodecsXmlParser);
+    void generateRoleMap() const;
+    void generateCommonPrefix() const;
+
+    MediaCodecsXmlParser(const MediaCodecsXmlParser&) = delete;
+    MediaCodecsXmlParser& operator=(const MediaCodecsXmlParser&) = delete;
 };
 
-}  // namespace android
+} // namespace android
 
-#endif  // MEDIA_CODECS_XML_PARSER_H_
+#endif // MEDIA_STAGEFRIGHT_XMLPARSER_H_
 
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 51143ac..eecc858 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -421,7 +421,7 @@
 
     for (size_t i = 0; i < mObj->mQueryResults.size(); i++) {
         keyValuePairs[i].mKey = mObj->mQueryResults.keyAt(i).string();
-        keyValuePairs[i].mValue = mObj->mQueryResults.keyAt(i).string();
+        keyValuePairs[i].mValue = mObj->mQueryResults.valueAt(i).string();
     }
     *numPairs = mObj->mQueryResults.size();
     return AMEDIA_OK;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 0df9a39..13522f5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -326,7 +326,7 @@
     sp<MmapThread> thread = mMmapThreads.valueFor(io);
     if (thread != 0) {
         interface = new MmapThreadHandle(thread);
-        thread->configure(attr, streamType, sessionId, callback, portId);
+        thread->configure(attr, streamType, sessionId, callback, *deviceId, portId);
         *handle = portId;
     } else {
         ret = NO_INIT;
@@ -2603,7 +2603,7 @@
             while (ec->mEffects.size()) {
                 sp<EffectModule> effect = ec->mEffects[0];
                 effect->unPin();
-                t->removeEffect_l(effect);
+                t->removeEffect_l(effect, /*release*/ true);
                 if (effect->purgeHandles()) {
                     t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
                 }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index f2c1c4f..bd5f146 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
 #include <system/audio_effects/effect_ns.h>
 #include <system/audio_effects/effect_visualizer.h>
 #include <audio_utils/primitives.h>
+#include <media/AudioEffect.h>
 #include <media/audiohal/EffectHalInterface.h>
 #include <media/audiohal/EffectsFactoryHalInterface.h>
 
@@ -109,7 +110,10 @@
 {
     ALOGV("Destructor %p", this);
     if (mEffectInterface != 0) {
-        ALOGW("EffectModule %p destructor called with unreleased interface", this);
+        char uuidStr[64];
+        AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+        ALOGW("EffectModule %p destructor called with unreleased interface, effect %s",
+                this, uuidStr);
         release_l();
     }
 
@@ -1081,18 +1085,12 @@
     result.append(buffer);
 
     result.append("\t\tDescriptor:\n");
-    snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
-            mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion,
-            mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],
-                    mDescriptor.uuid.node[2],
-            mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]);
+    char uuidStr[64];
+    AudioEffect::guidToString(&mDescriptor.uuid, uuidStr, sizeof(uuidStr));
+    snprintf(buffer, SIZE, "\t\t- UUID: %s\n", uuidStr);
     result.append(buffer);
-    snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n",
-                mDescriptor.type.timeLow, mDescriptor.type.timeMid,
-                    mDescriptor.type.timeHiAndVersion,
-                mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],
-                    mDescriptor.type.node[2],
-                mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]);
+    AudioEffect::guidToString(&mDescriptor.type, uuidStr, sizeof(uuidStr));
+    snprintf(buffer, SIZE, "\t\t- TYPE: %s\n", uuidStr);
     result.append(buffer);
     snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n",
             mDescriptor.apiVersion,
@@ -1306,11 +1304,10 @@
     if (thread != 0) {
         thread->disconnectEffectHandle(this, unpinIfLast);
     } else {
-        ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
         // try to cleanup as much as we can
         sp<EffectModule> effect = mEffect.promote();
-        if (effect != 0) {
-            effect->disconnectHandle(this, unpinIfLast);
+        if (effect != 0 && effect->disconnectHandle(this, unpinIfLast) > 0) {
+            ALOGW("%s Effect handle %p disconnected after thread destruction", __FUNCTION__, this);
         }
     }
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8c4531a..297608c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7545,6 +7545,8 @@
         AudioHwDevice *hwDev, sp<StreamHalInterface> stream,
         audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady)
     : ThreadBase(audioFlinger, id, outDevice, inDevice, MMAP, systemReady),
+      mSessionId(AUDIO_SESSION_NONE),
+      mDeviceId(AUDIO_PORT_HANDLE_NONE), mPortId(AUDIO_PORT_HANDLE_NONE),
       mHalStream(stream), mHalDevice(hwDev->hwDevice()), mAudioHwDev(hwDev),
       mActiveTracks(&this->mLocalLog)
 {
@@ -7580,11 +7582,13 @@
                                                 audio_stream_type_t streamType __unused,
                                                 audio_session_t sessionId,
                                                 const sp<MmapStreamCallback>& callback,
+                                                audio_port_handle_t deviceId,
                                                 audio_port_handle_t portId)
 {
     mAttr = *attr;
     mSessionId = sessionId;
     mCallback = callback;
+    mDeviceId = deviceId;
     mPortId = portId;
 }
 
@@ -7629,6 +7633,10 @@
         return NO_ERROR;
     }
 
+    if (!isOutput() && !recordingAllowed(client.packageName, client.clientPid, client.clientUid)) {
+        return PERMISSION_DENIED;
+    }
+
     audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
 
     audio_io_handle_t io = mId;
@@ -7640,7 +7648,7 @@
         audio_stream_type_t stream = streamType();
         audio_output_flags_t flags =
                 (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
-        audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+        audio_port_handle_t deviceId = mDeviceId;
         ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                             mSessionId,
                                             &stream,
@@ -7654,7 +7662,7 @@
         config.sample_rate = mSampleRate;
         config.channel_mask = mChannelMask;
         config.format = mFormat;
-        audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+        audio_port_handle_t deviceId = mDeviceId;
         ret = AudioSystem::getInputForAttr(&mAttr, &io,
                                               mSessionId,
                                               client.clientPid,
@@ -7991,17 +7999,19 @@
         mPrevOutDevice = type;
         sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
         sp<MmapStreamCallback> callback = mCallback.promote();
-        if (callback != 0) {
+        if (mDeviceId != deviceId && callback != 0) {
             callback->onRoutingChanged(deviceId);
         }
+        mDeviceId = deviceId;
     }
     if (!isOutput() && mPrevInDevice != mInDevice) {
         mPrevInDevice = type;
         sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
         sp<MmapStreamCallback> callback = mCallback.promote();
-        if (callback != 0) {
+        if (mDeviceId != deviceId && callback != 0) {
             callback->onRoutingChanged(deviceId);
         }
+        mDeviceId = deviceId;
     }
     return status;
 }
@@ -8242,9 +8252,10 @@
                                                 audio_stream_type_t streamType,
                                                 audio_session_t sessionId,
                                                 const sp<MmapStreamCallback>& callback,
+                                                audio_port_handle_t deviceId,
                                                 audio_port_handle_t portId)
 {
-    MmapThread::configure(attr, streamType, sessionId, callback, portId);
+    MmapThread::configure(attr, streamType, sessionId, callback, deviceId, portId);
     mStreamType = streamType;
 }
 
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 32f2bc4..8d7bb1a 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1479,6 +1479,7 @@
                                       audio_stream_type_t streamType,
                                       audio_session_t sessionId,
                                       const sp<MmapStreamCallback>& callback,
+                                      audio_port_handle_t deviceId,
                                       audio_port_handle_t portId);
 
                 void        disconnect();
@@ -1540,6 +1541,7 @@
 
                 audio_attributes_t      mAttr;
                 audio_session_t         mSessionId;
+                audio_port_handle_t     mDeviceId;
                 audio_port_handle_t     mPortId;
 
                 wp<MmapStreamCallback>  mCallback;
@@ -1562,6 +1564,7 @@
                                       audio_stream_type_t streamType,
                                       audio_session_t sessionId,
                                       const sp<MmapStreamCallback>& callback,
+                                      audio_port_handle_t deviceId,
                                       audio_port_handle_t portId);
 
                 AudioStreamOut* clearOutput();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 78f195d..e6ee6c9 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1320,9 +1320,9 @@
 
         // apply volume rules for current stream and device if necessary
         checkAndSetVolume(stream,
-                          mVolumeCurves->getVolumeIndex(stream, device),
+                          mVolumeCurves->getVolumeIndex(stream, outputDesc->device()),
                           outputDesc,
-                          device);
+                          outputDesc->device());
 
         // update the outputs if starting an output with a stream that can affect notification
         // routing
@@ -4721,10 +4721,10 @@
     //      use device for strategy enforced audible
     // 2: we are in call or the strategy phone is active on the output:
     //      use device for strategy phone
-    // 3: the strategy for enforced audible is active but not enforced on the output:
-    //      use the device for strategy enforced audible
-    // 4: the strategy sonification is active on the output:
+    // 3: the strategy sonification is active on the output:
     //      use device for strategy sonification
+    // 4: the strategy for enforced audible is active but not enforced on the output:
+    //      use the device for strategy enforced audible
     // 5: the strategy accessibility is active on the output:
     //      use device for strategy accessibility
     // 6: the strategy "respectful" sonification is active on the output:
@@ -4741,10 +4741,10 @@
     } else if (isInCall() ||
                     isStrategyActive(outputDesc, STRATEGY_PHONE)) {
         device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
-    } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) {
-        device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
     } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION)) {
         device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
+    } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) {
+        device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
     } else if (isStrategyActive(outputDesc, STRATEGY_ACCESSIBILITY)) {
         device = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, fromCache);
     } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION_RESPECTFUL)) {
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index 3f4017f..0d2dba1 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -121,18 +121,17 @@
 
     if (mCallbackStreamId != NO_STREAM) {
         // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight, currentFormat;
-        res = device->getStreamInfo(mCallbackStreamId,
-                &currentWidth, &currentHeight, &currentFormat, 0);
+        CameraDeviceBase::StreamInfo streamInfo;
+        res = device->getStreamInfo(mCallbackStreamId, &streamInfo);
         if (res != OK) {
             ALOGE("%s: Camera %d: Error querying callback output stream info: "
                     "%s (%d)", __FUNCTION__, mId,
                     strerror(-res), res);
             return res;
         }
-        if (currentWidth != (uint32_t)params.previewWidth ||
-                currentHeight != (uint32_t)params.previewHeight ||
-                currentFormat != (uint32_t)callbackFormat) {
+        if (streamInfo.width != (uint32_t)params.previewWidth ||
+                streamInfo.height != (uint32_t)params.previewHeight ||
+                !streamInfo.matchFormat((uint32_t)callbackFormat)) {
             // Since size should only change while preview is not running,
             // assuming that all existing use of old callback stream is
             // completed.
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index d6d8dde..d8b7af2 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -136,17 +136,16 @@
 
     if (mCaptureStreamId != NO_STREAM) {
         // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight;
-        res = device->getStreamInfo(mCaptureStreamId,
-                &currentWidth, &currentHeight, 0, 0);
+        CameraDeviceBase::StreamInfo streamInfo;
+        res = device->getStreamInfo(mCaptureStreamId, &streamInfo);
         if (res != OK) {
             ALOGE("%s: Camera %d: Error querying capture output stream info: "
                     "%s (%d)", __FUNCTION__,
                     mId, strerror(-res), res);
             return res;
         }
-        if (currentWidth != (uint32_t)params.pictureWidth ||
-                currentHeight != (uint32_t)params.pictureHeight) {
+        if (streamInfo.width != (uint32_t)params.pictureWidth ||
+                streamInfo.height != (uint32_t)params.pictureHeight) {
             ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
                 __FUNCTION__, mId, mCaptureStreamId);
             res = device->deleteStream(mCaptureStreamId);
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index d79e430..b6f443a 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -161,18 +161,17 @@
 
     if (mPreviewStreamId != NO_STREAM) {
         // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight;
-        res = device->getStreamInfo(mPreviewStreamId,
-                &currentWidth, &currentHeight, 0, 0);
+        CameraDeviceBase::StreamInfo streamInfo;
+        res = device->getStreamInfo(mPreviewStreamId, &streamInfo);
         if (res != OK) {
             ALOGE("%s: Camera %d: Error querying preview stream info: "
                     "%s (%d)", __FUNCTION__, mId, strerror(-res), res);
             return res;
         }
-        if (currentWidth != (uint32_t)params.previewWidth ||
-                currentHeight != (uint32_t)params.previewHeight) {
+        if (streamInfo.width != (uint32_t)params.previewWidth ||
+                streamInfo.height != (uint32_t)params.previewHeight) {
             ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
-                    __FUNCTION__, mId, currentWidth, currentHeight,
+                    __FUNCTION__, mId, streamInfo.width, streamInfo.height,
                     params.previewWidth, params.previewHeight);
             res = device->waitUntilDrained();
             if (res != OK) {
@@ -312,10 +311,8 @@
         return INVALID_OPERATION;
     }
 
-    uint32_t currentWidth, currentHeight, currentFormat;
-    android_dataspace currentDataSpace;
-    res = device->getStreamInfo(mRecordingStreamId,
-            &currentWidth, &currentHeight, &currentFormat, &currentDataSpace);
+    CameraDeviceBase::StreamInfo streamInfo;
+    res = device->getStreamInfo(mRecordingStreamId, &streamInfo);
     if (res != OK) {
         ALOGE("%s: Camera %d: Error querying recording output stream info: "
                 "%s (%d)", __FUNCTION__, mId,
@@ -324,10 +321,10 @@
     }
 
     if (mRecordingWindow == nullptr ||
-            currentWidth != (uint32_t)params.videoWidth ||
-            currentHeight != (uint32_t)params.videoHeight ||
-            currentFormat != (uint32_t)params.videoFormat ||
-            currentDataSpace != params.videoDataSpace) {
+            streamInfo.width != (uint32_t)params.videoWidth ||
+            streamInfo.height != (uint32_t)params.videoHeight ||
+            !streamInfo.matchFormat((uint32_t)params.videoFormat) ||
+            streamInfo.dataSpace != params.videoDataSpace) {
         *needsUpdate = true;
         return res;
     }
@@ -348,22 +345,18 @@
 
     if (mRecordingStreamId != NO_STREAM) {
         // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight;
-        uint32_t currentFormat;
-        android_dataspace currentDataSpace;
-        res = device->getStreamInfo(mRecordingStreamId,
-                &currentWidth, &currentHeight,
-                &currentFormat, &currentDataSpace);
+        CameraDeviceBase::StreamInfo streamInfo;
+        res = device->getStreamInfo(mRecordingStreamId, &streamInfo);
         if (res != OK) {
             ALOGE("%s: Camera %d: Error querying recording output stream info: "
                     "%s (%d)", __FUNCTION__, mId,
                     strerror(-res), res);
             return res;
         }
-        if (currentWidth != (uint32_t)params.videoWidth ||
-                currentHeight != (uint32_t)params.videoHeight ||
-                currentFormat != (uint32_t)params.videoFormat ||
-                currentDataSpace != params.videoDataSpace) {
+        if (streamInfo.width != (uint32_t)params.videoWidth ||
+                streamInfo.height != (uint32_t)params.videoHeight ||
+                !streamInfo.matchFormat((uint32_t)params.videoFormat) ||
+                streamInfo.dataSpace != params.videoDataSpace) {
             // TODO: Should wait to be sure previous recording has finished
             res = device->deleteStream(mRecordingStreamId);
 
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 9bc31b9..b0607fb 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -233,17 +233,16 @@
 
     if ((mZslStreamId != NO_STREAM) || (mInputStreamId != NO_STREAM)) {
         // Check if stream parameters have to change
-        uint32_t currentWidth, currentHeight;
-        res = device->getStreamInfo(mZslStreamId,
-                &currentWidth, &currentHeight, 0, 0);
+        CameraDeviceBase::StreamInfo streamInfo;
+        res = device->getStreamInfo(mZslStreamId, &streamInfo);
         if (res != OK) {
             ALOGE("%s: Camera %d: Error querying capture output stream info: "
                     "%s (%d)", __FUNCTION__,
                     client->getCameraId(), strerror(-res), res);
             return res;
         }
-        if (currentWidth != (uint32_t)params.fastInfo.arrayWidth ||
-                currentHeight != (uint32_t)params.fastInfo.arrayHeight) {
+        if (streamInfo.width != (uint32_t)params.fastInfo.arrayWidth ||
+                streamInfo.height != (uint32_t)params.fastInfo.arrayHeight) {
             if (mZslStreamId != NO_STREAM) {
                 ALOGV("%s: Camera %d: Deleting stream %d since the buffer "
                       "dimensions changed",
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 54fcb0a..fe4c8d7 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -142,12 +142,32 @@
     virtual status_t createInputStream(uint32_t width, uint32_t height,
             int32_t format, /*out*/ int32_t *id) = 0;
 
+    struct StreamInfo {
+        uint32_t width;
+        uint32_t height;
+        uint32_t format;
+        bool formatOverridden;
+        uint32_t originalFormat;
+        android_dataspace dataSpace;
+        StreamInfo() : width(0), height(0), format(0), formatOverridden(false), originalFormat(0),
+                dataSpace(HAL_DATASPACE_UNKNOWN) {}
+        /**
+         * Check whether the format matches the current or the original one in case
+         * it got overridden.
+         */
+        bool matchFormat(uint32_t clientFormat) {
+            if ((formatOverridden && (originalFormat == clientFormat)) ||
+                    (format == clientFormat)) {
+                return true;
+            }
+            return false;
+        }
+    };
+
     /**
      * Get information about a given stream.
      */
-    virtual status_t getStreamInfo(int id,
-            uint32_t *width, uint32_t *height,
-            uint32_t *format, android_dataspace *dataSpace) = 0;
+    virtual status_t getStreamInfo(int id, StreamInfo *streamInfo) = 0;
 
     /**
      * Set stream gralloc buffer transform
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 94e8f3b..669f763 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1345,10 +1345,11 @@
     return OK;
 }
 
-status_t Camera3Device::getStreamInfo(int id,
-        uint32_t *width, uint32_t *height,
-        uint32_t *format, android_dataspace *dataSpace) {
+status_t Camera3Device::getStreamInfo(int id, StreamInfo *streamInfo) {
     ATRACE_CALL();
+    if (nullptr == streamInfo) {
+        return BAD_VALUE;
+    }
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
@@ -1375,10 +1376,12 @@
         return idx;
     }
 
-    if (width) *width  = mOutputStreams[idx]->getWidth();
-    if (height) *height = mOutputStreams[idx]->getHeight();
-    if (format) *format = mOutputStreams[idx]->getFormat();
-    if (dataSpace) *dataSpace = mOutputStreams[idx]->getDataSpace();
+    streamInfo->width  = mOutputStreams[idx]->getWidth();
+    streamInfo->height = mOutputStreams[idx]->getHeight();
+    streamInfo->format = mOutputStreams[idx]->getFormat();
+    streamInfo->dataSpace = mOutputStreams[idx]->getDataSpace();
+    streamInfo->formatOverridden = mOutputStreams[idx]->isFormatOverridden();
+    streamInfo->originalFormat = mOutputStreams[idx]->getOriginalFormat();
     return OK;
 }
 
@@ -3233,6 +3236,8 @@
         }
         HalStream &src = finalConfiguration.streams[realIdx];
 
+        Camera3Stream* dstStream = Camera3Stream::cast(dst);
+        dstStream->setFormatOverride(false);
         int overrideFormat = mapToFrameworkFormat(src.overrideFormat);
         if (dst->format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
             if (dst->format != overrideFormat) {
@@ -3240,6 +3245,8 @@
                         streamId, dst->format);
             }
         } else {
+            dstStream->setFormatOverride((dst->format != overrideFormat) ? true : false);
+            dstStream->setOriginalFormat(dst->format);
             // Override allowed with IMPLEMENTATION_DEFINED
             dst->format = overrideFormat;
         }
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 363bd88..298a3d8 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -128,9 +128,7 @@
             uint32_t width, uint32_t height, int format,
             int *id) override;
 
-    status_t getStreamInfo(int id,
-            uint32_t *width, uint32_t *height,
-            uint32_t *format, android_dataspace *dataSpace) override;
+    status_t getStreamInfo(int id, StreamInfo *streamInfo) override;
     status_t setStreamTransform(int id, int transform) override;
 
     status_t deleteStream(int id) override;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 25e44a5..c186208 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -62,7 +62,9 @@
     mPrepared(false),
     mPreparedBufferIdx(0),
     mLastMaxCount(Camera3StreamInterface::ALLOCATE_PIPELINE_MAX),
-    mBufferLimitLatency(kBufferLimitLatencyBinSize) {
+    mBufferLimitLatency(kBufferLimitLatencyBinSize),
+    mFormatOverridden(false),
+    mOriginalFormat(-1) {
 
     camera3_stream::stream_type = type;
     camera3_stream::width = width;
@@ -112,6 +114,22 @@
     mUsage = usage;
 }
 
+void Camera3Stream::setFormatOverride(bool formatOverridden) {
+    mFormatOverridden = formatOverridden;
+}
+
+bool Camera3Stream::isFormatOverridden() {
+    return mFormatOverridden;
+}
+
+void Camera3Stream::setOriginalFormat(int originalFormat) {
+    mOriginalFormat = originalFormat;
+}
+
+int Camera3Stream::getOriginalFormat() {
+    return mOriginalFormat;
+}
+
 camera3_stream* Camera3Stream::startConfiguration() {
     ATRACE_CALL();
     Mutex::Autolock l(mLock);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 9090f83..1843ae8 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -146,6 +146,10 @@
     android_dataspace getDataSpace() const;
     uint64_t          getUsage() const;
     void              setUsage(uint64_t usage);
+    void              setFormatOverride(bool formatOverriden);
+    bool              isFormatOverridden();
+    void              setOriginalFormat(int originalFormat);
+    int               getOriginalFormat();
 
     camera3_stream*   asHalStream() override {
         return this;
@@ -514,6 +518,10 @@
     // max_buffers.
     static const int32_t kBufferLimitLatencyBinSize = 33; //in ms
     CameraLatencyHistogram mBufferLimitLatency;
+
+    //Keep track of original format in case it gets overridden
+    bool mFormatOverridden;
+    int mOriginalFormat;
 }; // class Camera3Stream
 
 }; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 0544a1b..63456c4 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -71,6 +71,10 @@
     virtual uint32_t getHeight() const = 0;
     virtual int      getFormat() const = 0;
     virtual android_dataspace getDataSpace() const = 0;
+    virtual void setFormatOverride(bool formatOverriden) = 0;
+    virtual bool isFormatOverridden() = 0;
+    virtual void setOriginalFormat(int originalFormat) = 0;
+    virtual int getOriginalFormat() = 0;
 
     /**
      * Get a HAL3 handle for the stream, without starting stream configuration.
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index f3bb35c..2443301 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -29,12 +29,15 @@
 #include <unistd.h>
 
 #include <string.h>
+#include <pwd.h>
 
 #include <cutils/atomic.h>
 #include <cutils/properties.h> // for property_get
 
 #include <utils/misc.h>
 
+#include <android/content/pm/IPackageManagerNative.h>
+
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryHeapBase.h>
@@ -80,27 +83,26 @@
 
 namespace android {
 
+    using namespace android::base;
+    using namespace android::content::pm;
+
 
 
 // summarized records
-// up to 48 sets, each covering an hour -- at least 2 days of coverage
+// up to 36 sets, each covering an hour -- so at least 1.5 days
 // (will be longer if there are hours without any media action)
 static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll);
-static const int kMaxRecordSets = 48;
-// individual records kept in memory
-static const int kMaxRecords    = 100;
+static const int kMaxRecordSets = 36;
 
+// individual records kept in memory: age or count
+// age: <= 36 hours (1.5 days)
+// count: hard limit of # records
+// (0 for either of these disables that threshold)
+static const nsecs_t kMaxRecordAgeNs =  36 * 3600 * (1000*1000*1000ll);
+static const int kMaxRecords    = 0;
 
 static const char *kServiceName = "media.metrics";
 
-
-//using android::status_t;
-//using android::OK;
-//using android::BAD_VALUE;
-//using android::NOT_ENOUGH_DATA;
-//using android::Parcel;
-
-
 void MediaAnalyticsService::instantiate() {
     defaultServiceManager()->addService(
             String16(kServiceName), new MediaAnalyticsService());
@@ -110,6 +112,7 @@
 MediaAnalyticsService::SummarizerSet::SummarizerSet() {
     mSummarizers = new List<MetricsSummarizer *>();
 }
+
 MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
     // empty the list
     List<MetricsSummarizer *> *l = mSummarizers;
@@ -153,8 +156,10 @@
 
 MediaAnalyticsService::MediaAnalyticsService()
         : mMaxRecords(kMaxRecords),
+          mMaxRecordAgeNs(kMaxRecordAgeNs),
           mMaxRecordSets(kMaxRecordSets),
-          mNewSetInterval(kNewSetIntervalNs) {
+          mNewSetInterval(kNewSetIntervalNs),
+          mDumpProto(MediaAnalyticsItem::PROTO_V0) {
 
     ALOGD("MediaAnalyticsService created");
     // clear our queues
@@ -167,6 +172,8 @@
     mItemsSubmitted = 0;
     mItemsFinalized = 0;
     mItemsDiscarded = 0;
+    mItemsDiscardedExpire = 0;
+    mItemsDiscardedCount = 0;
 
     mLastSessionID = 0;
     // recover any persistency we set up
@@ -177,8 +184,23 @@
         ALOGD("MediaAnalyticsService destroyed");
 
     // clean out mOpen and mFinalized
+    while (mOpen->size() > 0) {
+        MediaAnalyticsItem * oitem = *(mOpen->begin());
+        mOpen->erase(mOpen->begin());
+        delete oitem;
+        mItemsDiscarded++;
+        mItemsDiscardedCount++;
+    }
     delete mOpen;
     mOpen = NULL;
+
+    while (mFinalized->size() > 0) {
+        MediaAnalyticsItem * oitem = *(mFinalized->begin());
+        mFinalized->erase(mFinalized->begin());
+        delete oitem;
+        mItemsDiscarded++;
+        mItemsDiscardedCount++;
+    }
     delete mFinalized;
     mFinalized = NULL;
 
@@ -212,13 +234,15 @@
     //
     bool isTrusted = false;
 
+    ALOGV("caller has uid=%d, embedded uid=%d", uid, uid_given);
+
     switch (uid)  {
         case AID_MEDIA:
         case AID_MEDIA_CODEC:
         case AID_MEDIA_EX:
         case AID_MEDIA_DRM:
             // trusted source, only override default values
-                isTrusted = true;
+            isTrusted = true;
             if (uid_given == (-1)) {
                 item->setUid(uid);
             }
@@ -233,6 +257,21 @@
             break;
     }
 
+    // Overwrite package name and version if the caller was untrusted.
+    if (!isTrusted) {
+      item->setPkgName(getPkgName(item->getUid(), true));
+      item->setPkgVersionCode(0);
+    } else if (item->getPkgName().empty()) {
+      // Only overwrite the package name if it was empty. Trust whatever
+      // version code was provided by the trusted caller.
+      item->setPkgName(getPkgName(uid, true));
+    }
+
+    ALOGV("given uid %d; sanitized uid: %d sanitized pkg: %s "
+          "sanitized pkg version: %d",
+          uid_given, item->getUid(),
+          item->getPkgName().c_str(),
+          item->getPkgVersionCode());
 
     mItemsSubmitted++;
 
@@ -316,6 +355,7 @@
     return id;
 }
 
+
 status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 512;
@@ -333,22 +373,41 @@
     }
 
     // crack any parameters
-    bool clear = false;
-    bool summary = false;
-    nsecs_t ts_since = 0;
     String16 summaryOption("-summary");
+    bool summary = false;
+    String16 protoOption("-proto");
     String16 clearOption("-clear");
+    bool clear = false;
     String16 sinceOption("-since");
+    nsecs_t ts_since = 0;
     String16 helpOption("-help");
     String16 onlyOption("-only");
-    const char *only = NULL;
+    AString only;
     int n = args.size();
+
     for (int i = 0; i < n; i++) {
         String8 myarg(args[i]);
         if (args[i] == clearOption) {
             clear = true;
         } else if (args[i] == summaryOption) {
             summary = true;
+        } else if (args[i] == protoOption) {
+            i++;
+            if (i < n) {
+                String8 value(args[i]);
+                int proto = MediaAnalyticsItem::PROTO_V0;       // default to original
+                char *endp;
+                const char *p = value.string();
+                proto = strtol(p, &endp, 10);
+                if (endp != p || *endp == '\0') {
+                    if (proto < MediaAnalyticsItem::PROTO_FIRST) {
+                        proto = MediaAnalyticsItem::PROTO_FIRST;
+                    } else if (proto > MediaAnalyticsItem::PROTO_LAST) {
+                        proto = MediaAnalyticsItem::PROTO_LAST;
+                    }
+                    mDumpProto = proto;
+                }
+            }
         } else if (args[i] == sinceOption) {
             i++;
             if (i < n) {
@@ -368,18 +427,12 @@
             i++;
             if (i < n) {
                 String8 value(args[i]);
-                const char *p = value.string();
-                char *q = strdup(p);
-                if (q != NULL) {
-                    if (only != NULL) {
-                        free((void*)only);
-                    }
-                only = q;
-                }
+                only = value.string();
             }
         } else if (args[i] == helpOption) {
             result.append("Recognized parameters:\n");
             result.append("-help        this help message\n");
+            result.append("-proto X     dump using protocol X (defaults to 1)");
             result.append("-summary     show summary info\n");
             result.append("-clear       clears out saved records\n");
             result.append("-only X      process records for component X\n");
@@ -398,14 +451,12 @@
 
     dumpHeaders(result, ts_since);
 
-    // only want 1, to avoid confusing folks that parse the output
+    // want exactly 1, to avoid confusing folks that parse the output
     if (summary) {
-        dumpSummaries(result, ts_since, only);
+        dumpSummaries(result, ts_since, only.c_str());
     } else {
-        dumpRecent(result, ts_since, only);
+        dumpRecent(result, ts_since, only.c_str());
     }
-    free((void*)only);
-    only=NULL;
 
 
     if (clear) {
@@ -430,6 +481,9 @@
     const size_t SIZE = 512;
     char buffer[SIZE];
 
+    snprintf(buffer, SIZE, "Protocol Version: %d\n", mDumpProto);
+    result.append(buffer);
+
     int enabled = MediaAnalyticsItem::isEnabled();
     if (enabled) {
         snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
@@ -439,10 +493,14 @@
     result.append(buffer);
 
     snprintf(buffer, SIZE,
-        "Since Boot: Submissions: %" PRId64
-            " Finalizations: %" PRId64
-        " Discarded: %" PRId64 "\n",
-        mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
+        "Since Boot: Submissions: %8" PRId64
+            " Finalizations: %8" PRId64 "\n",
+        mItemsSubmitted, mItemsFinalized);
+    result.append(buffer);
+    snprintf(buffer, SIZE,
+        "Records Discarded: %8" PRId64
+            " (by Count: %" PRId64 " by Expiration: %" PRId64 ")\n",
+         mItemsDiscarded, mItemsDiscardedCount, mItemsDiscardedExpire);
     result.append(buffer);
     snprintf(buffer, SIZE,
         "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
@@ -464,6 +522,10 @@
     snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
     result.append(buffer);
 
+    if (only != NULL && *only == '\0') {
+        only = NULL;
+    }
+
     // have each of the distillers dump records
     if (mSummarizerSets != NULL) {
         List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
@@ -490,6 +552,10 @@
     const size_t SIZE = 512;
     char buffer[SIZE];
 
+    if (only != NULL && *only == '\0') {
+        only = NULL;
+    }
+
     // show the recently recorded records
     snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
     result.append(buffer);
@@ -526,7 +592,7 @@
                 ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
                 continue;
             }
-            AString entry = (*it)->toString();
+            AString entry = (*it)->toString(mDumpProto);
             result.appendFormat("%5d: %s\n", slot, entry.c_str());
             slot++;
         }
@@ -551,13 +617,32 @@
         l->push_back(item);
     }
 
-    // keep removing old records the front until we're in-bounds
+    // keep removing old records the front until we're in-bounds (count)
     if (mMaxRecords > 0) {
         while (l->size() > (size_t) mMaxRecords) {
             MediaAnalyticsItem * oitem = *(l->begin());
             l->erase(l->begin());
             delete oitem;
             mItemsDiscarded++;
+            mItemsDiscardedCount++;
+        }
+    }
+
+    // keep removing old records the front until we're in-bounds (count)
+    if (mMaxRecordAgeNs > 0) {
+        nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+        while (l->size() > 0) {
+            MediaAnalyticsItem * oitem = *(l->begin());
+            nsecs_t when = oitem->getTimestamp();
+            // careful about timejumps too
+            if ((now > when) && (now-when) <= mMaxRecordAgeNs) {
+                // this (and the rest) are recent enough to keep
+                break;
+            }
+            l->erase(l->begin());
+            delete oitem;
+            mItemsDiscarded++;
+            mItemsDiscardedExpire++;
         }
     }
 }
@@ -565,11 +650,6 @@
 // are they alike enough that nitem can be folded into oitem?
 static bool compatibleItems(MediaAnalyticsItem * oitem, MediaAnalyticsItem * nitem) {
 
-    if (0) {
-        ALOGD("Compare: o %s n %s",
-              oitem->toString().c_str(), nitem->toString().c_str());
-    }
-
     // general safety
     if (nitem->getUid() != oitem->getUid()) {
         return false;
@@ -720,4 +800,85 @@
 
 }
 
+// mapping uids to package names
+
+// give me the package name, perhaps going to find it
+AString MediaAnalyticsService::getPkgName(uid_t uid, bool addIfMissing) {
+    ssize_t i = mPkgMappings.indexOfKey(uid);
+    if (i >= 0) {
+        AString pkg = mPkgMappings.valueAt(i);
+        ALOGV("returning pkg '%s' for uid %d", pkg.c_str(), uid);
+        return pkg;
+    }
+
+    AString pkg;
+
+    if (addIfMissing == false) {
+        return pkg;
+    }
+
+    struct passwd *pw = getpwuid(uid);
+    if (pw) {
+        pkg = pw->pw_name;
+    } else {
+        pkg = "-";
+    }
+
+    // find the proper value
+
+    sp<IBinder> binder = NULL;
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) {
+        ALOGE("defaultServiceManager failed");
+    } else {
+        binder = sm->getService(String16("package_native"));
+        if (binder == NULL) {
+            ALOGE("getService package_native failed");
+        }
+    }
+
+    if (binder != NULL) {
+        sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);
+
+        std::vector<int> uids;
+        std::vector<std::string> names;
+
+        uids.push_back(uid);
+
+        binder::Status status = package_mgr->getNamesForUids(uids, &names);
+        if (!status.isOk()) {
+            ALOGE("package_native::getNamesForUids failed: %s",
+                  status.exceptionMessage().c_str());
+        } else {
+            if (!names[0].empty()) {
+                pkg = names[0].c_str();
+            }
+        }
+    }
+
+    // XXX determine whether package was side-loaded or from playstore.
+    // for privacy, we only list apps loaded from playstore.
+
+    // Sanitize the package name for ":"
+    // as an example, we get "shared:android.uid.systemui"
+    // replace : with something benign (I'm going to use !)
+    if (!pkg.empty()) {
+        int n = pkg.size();
+        char *p = (char *) pkg.c_str();
+        for (int i = 0 ; i < n; i++) {
+            if (p[i] == ':') {
+                p[i] = '!';
+            }
+        }
+    }
+
+    // add it to the map, to save a subsequent lookup
+    if (!pkg.empty()) {
+        ALOGV("Adding uid %d pkg '%s'", uid, pkg.c_str());
+        mPkgMappings.add(uid, pkg);
+    }
+
+    return pkg;
+}
+
 } // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index 6685967..4fe2fb2 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -54,6 +54,8 @@
     int64_t mItemsSubmitted;
     int64_t mItemsFinalized;
     int64_t mItemsDiscarded;
+    int64_t mItemsDiscardedExpire;
+    int64_t mItemsDiscardedCount;
     int64_t mSetsDiscarded;
     MediaAnalyticsItem::SessionID_t mLastSessionID;
 
@@ -61,9 +63,12 @@
     mutable Mutex           mLock;
     mutable Mutex           mLock_ids;
 
-    // the most we hold in memory
-    // up to this many in each queue (open, finalized)
+    // limit how many records we'll retain
+    // by count (in each queue (open, finalized))
     int32_t mMaxRecords;
+    // by time (none older than this long agan
+    nsecs_t mMaxRecordAgeNs;
+    //
     // # of sets of summaries
     int32_t mMaxRecordSets;
     // nsecs until we start a new record set
@@ -118,6 +123,7 @@
     void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
 
     // support for generating output
+    int mDumpProto;
     String8 dumpQueue(List<MediaAnalyticsItem*> *);
     String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
 
@@ -125,6 +131,15 @@
     void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
     void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
 
+    // mapping uids to package names
+    struct UidToPkgMap {
+        uid_t uid;
+        AString pkg;
+    };
+
+    KeyedVector<uid_t,AString>  mPkgMappings;
+    AString getPkgName(uid_t uid, bool addIfMissing);
+
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
index 3477f1f..93fe0ec 100644
--- a/services/mediaanalytics/MetricsSummarizer.cpp
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -141,23 +141,23 @@
     List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
     for (; it != mSummaries->end(); it++) {
         bool good = sameAttributes((*it), item, getIgnorables());
-        ALOGV("Match against %s says %d",
-              (*it)->toString().c_str(), good);
+        ALOGV("Match against %s says %d", (*it)->toString().c_str(), good);
         if (good)
             break;
     }
     if (it == mSummaries->end()) {
             ALOGV("save new record");
-            item = item->dup();
-            if (item == NULL) {
+            MediaAnalyticsItem *nitem = item->dup();
+            if (nitem == NULL) {
                 ALOGE("unable to save MediaMetrics record");
             }
-            sortProps(item);
-            item->setInt32("count",1);
-            mSummaries->push_back(item);
+            sortProps(nitem);
+            nitem->setInt32("aggregated",1);
+            mergeRecord(*nitem, *item);
+            mSummaries->push_back(nitem);
     } else {
             ALOGV("increment existing record");
-            (*it)->addInt32("count",1);
+            (*it)->addInt32("aggregated",1);
             mergeRecord(*(*it), *item);
     }
 }
@@ -168,6 +168,71 @@
     return;
 }
 
+// keep some stats for things: sums, counts, standard deviation
+// the integer version -- all of these pieces are in 64 bits
+void MetricsSummarizer::minMaxVar64(MediaAnalyticsItem &summation, const char *key, int64_t value) {
+    if (key == NULL)
+        return;
+    int len = strlen(key) + 32;
+    char *tmpKey = (char *)malloc(len);
+
+    if (tmpKey == NULL) {
+        return;
+    }
+
+    // N - count of samples
+    snprintf(tmpKey, len, "%s.n", key);
+    summation.addInt64(tmpKey, 1);
+
+    // zero - count of samples that are zero
+    if (value == 0) {
+        snprintf(tmpKey, len, "%s.zero", key);
+        int64_t zero = 0;
+        (void) summation.getInt64(tmpKey,&zero);
+        zero++;
+        summation.setInt64(tmpKey, zero);
+    }
+
+    // min
+    snprintf(tmpKey, len, "%s.min", key);
+    int64_t min = value;
+    if (summation.getInt64(tmpKey,&min)) {
+        if (min > value) {
+            summation.setInt64(tmpKey, value);
+        }
+    } else {
+        summation.setInt64(tmpKey, value);
+    }
+
+    // max
+    snprintf(tmpKey, len, "%s.max", key);
+    int64_t max = value;
+    if (summation.getInt64(tmpKey,&max)) {
+        if (max < value) {
+            summation.setInt64(tmpKey, value);
+        }
+    } else {
+        summation.setInt64(tmpKey, value);
+    }
+
+    // components for mean, stddev;
+    // stddev = sqrt(1/4*(sumx2 - (2*sumx*sumx/n) + ((sumx/n)^2)))
+    // sum x
+    snprintf(tmpKey, len, "%s.sumX", key);
+    summation.addInt64(tmpKey, value);
+    // sum x^2
+    snprintf(tmpKey, len, "%s.sumX2", key);
+    summation.addInt64(tmpKey, value*value);
+
+
+    // last thing we do -- remove the base key from the summation
+    // record so we won't get confused about it having both individual
+    // and summary information in there.
+    summation.removeProp(key);
+
+    free(tmpKey);
+}
+
 
 //
 // Comparators
@@ -186,20 +251,23 @@
     ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
     ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
 
+    // keep different sources/users separate
+    if (single->mUid != summ->mUid) {
+        return false;
+    }
+
     // this can be made better.
     for(size_t i=0;i<single->mPropCount;i++) {
         MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
         const char *attrName = prop1->mName;
-        ALOGV("compare on attr '%s'", attrName);
 
         // is it something we should ignore
         if (ignorable != NULL) {
             const char **ig = ignorable;
-            while (*ig) {
+            for (;*ig; ig++) {
                 if (strcmp(*ig, attrName) == 0) {
                     break;
                 }
-                ig++;
             }
             if (*ig) {
                 ALOGV("we don't mind that it has attr '%s'", attrName);
@@ -218,29 +286,42 @@
         }
         switch (prop1->mType) {
             case MediaAnalyticsItem::kTypeInt32:
-                if (prop1->u.int32Value != prop2->u.int32Value)
+                if (prop1->u.int32Value != prop2->u.int32Value) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeInt64:
-                if (prop1->u.int64Value != prop2->u.int64Value)
+                if (prop1->u.int64Value != prop2->u.int64Value) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeDouble:
                 // XXX: watch out for floating point comparisons!
-                if (prop1->u.doubleValue != prop2->u.doubleValue)
+                if (prop1->u.doubleValue != prop2->u.doubleValue) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeCString:
-                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0)
+                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             case MediaAnalyticsItem::kTypeRate:
-                if (prop1->u.rate.count != prop2->u.rate.count)
+                if (prop1->u.rate.count != prop2->u.rate.count) {
+                    ALOGV("mismatch values");
                     return false;
-                if (prop1->u.rate.duration != prop2->u.rate.duration)
+                }
+                if (prop1->u.rate.duration != prop2->u.rate.duration) {
+                    ALOGV("mismatch values");
                     return false;
+                }
                 break;
             default:
+                ALOGV("mismatch values in default type");
                 return false;
         }
     }
@@ -248,15 +329,6 @@
     return true;
 }
 
-bool MetricsSummarizer::sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
-
-    // verify same user
-    if (summ->mPid != single->mPid)
-        return false;
-
-    // and finally do the more expensive validation of the attributes
-    return sameAttributes(summ, single, ignorable);
-}
 
 int MetricsSummarizer::PropSorter(const void *a, const void *b) {
     MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
@@ -267,14 +339,8 @@
 // we sort in the summaries so that it looks pretty in the dumpsys
 void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
     if (item->mPropCount != 0) {
-        if (DEBUG_SORT) {
-            ALOGD("sortProps(pre): %s", item->toString().c_str());
-        }
         qsort(item->mProps, item->mPropCount,
               sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
-        if (DEBUG_SORT) {
-            ALOGD("sortProps(pst): %s", item->toString().c_str());
-        }
     }
 }
 
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
index 0b64eac..a9f0786 100644
--- a/services/mediaanalytics/MetricsSummarizer.h
+++ b/services/mediaanalytics/MetricsSummarizer.h
@@ -59,10 +59,9 @@
 
     // various comparators
     // "do these records have same attributes and values in those attrs"
-    // ditto, but watch for "error" fields
     bool sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
-    // attributes + from the same app/userid
-    bool sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+
+    void minMaxVar64(MediaAnalyticsItem &summ, const char *key, int64_t value);
 
     static int PropSorter(const void *a, const void *b);
     void sortProps(MediaAnalyticsItem *item);
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
index 5162059..f882cb9 100644
--- a/services/mediaanalytics/MetricsSummarizerPlayer.cpp
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
@@ -51,37 +51,43 @@
     setIgnorables(player_ignorable);
 }
 
+// NB: this is also called for the first time -- so summation == item
+// Not sure if we need a flag for that or not.
+// In this particular mergeRecord() code -- we're' ok for this.
 void MetricsSummarizerPlayer::mergeRecord(MediaAnalyticsItem &summation, MediaAnalyticsItem &item) {
 
     ALOGV("MetricsSummarizerPlayer::mergeRecord()");
 
-    //
-    // we sum time & frames.
-    // be careful about our special "-1" values that indicate 'unknown'
-    // treat those as 0 [basically, not summing them into the totals].
+
     int64_t duration = 0;
     if (item.getInt64("android.media.mediaplayer.durationMs", &duration)) {
         ALOGV("found durationMs of %" PRId64, duration);
-        summation.addInt64("android.media.mediaplayer.durationMs",duration);
+        minMaxVar64(summation, "android.media.mediaplayer.durationMs", duration);
     }
+
     int64_t playing = 0;
-    if (item.getInt64("android.media.mediaplayer.playingMs", &playing))
+    if (item.getInt64("android.media.mediaplayer.playingMs", &playing)) {
         ALOGV("found playingMs of %" PRId64, playing);
-        if (playing >= 0) {
-            summation.addInt64("android.media.mediaplayer.playingMs",playing);
-        }
+    }
+    if (playing >= 0) {
+        minMaxVar64(summation,"android.media.mediaplayer.playingMs",playing);
+    }
+
     int64_t frames = 0;
-    if (item.getInt64("android.media.mediaplayer.frames", &frames))
+    if (item.getInt64("android.media.mediaplayer.frames", &frames)) {
         ALOGV("found framess of %" PRId64, frames);
-        if (frames >= 0) {
-            summation.addInt64("android.media.mediaplayer.frames",frames);
-        }
+    }
+    if (frames >= 0) {
+        minMaxVar64(summation,"android.media.mediaplayer.frames",frames);
+    }
+
     int64_t dropped = 0;
-    if (item.getInt64("android.media.mediaplayer.dropped", &dropped))
+    if (item.getInt64("android.media.mediaplayer.dropped", &dropped)) {
         ALOGV("found dropped of %" PRId64, dropped);
-        if (dropped >= 0) {
-            summation.addInt64("android.media.mediaplayer.dropped",dropped);
-        }
+    }
+    if (dropped >= 0) {
+        minMaxVar64(summation,"android.media.mediaplayer.dropped",dropped);
+    }
 }
 
 } // namespace android
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 6997b5a..faeb0a7 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -9,13 +9,8 @@
     libgui \
     libutils \
     liblog \
-    libstagefright_omx
-LOCAL_C_INCLUDES := \
-    frameworks/av/include \
-    frameworks/av/media/libstagefright \
-    frameworks/av/media/libstagefright/include \
-    frameworks/native/include \
-    frameworks/native/include/media/openmax
+    libstagefright_omx \
+    libstagefright_xmlparser
 LOCAL_MODULE:= libmediacodecservice
 LOCAL_VENDOR_MODULE := true
 LOCAL_32_BIT_ONLY := true
@@ -38,15 +33,10 @@
     libhwbinder \
     libhidltransport \
     libstagefright_omx \
+    libstagefright_xmlparser \
     android.hardware.media.omx@1.0 \
     android.hidl.memory@1.0
 
-LOCAL_C_INCLUDES := \
-    frameworks/av/include \
-    frameworks/av/media/libstagefright \
-    frameworks/av/media/libstagefright/include \
-    frameworks/native/include \
-    frameworks/native/include/media/openmax
 LOCAL_MODULE := android.hardware.media.omx@1.0-service
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_VENDOR_MODULE := true
diff --git a/services/mediacodec/MediaCodecService.cpp b/services/mediacodec/MediaCodecService.cpp
index fc1e5d9..6b510c6 100644
--- a/services/mediacodec/MediaCodecService.cpp
+++ b/services/mediacodec/MediaCodecService.cpp
@@ -24,15 +24,25 @@
 
 sp<IOMX> MediaCodecService::getOMX() {
 
-    Mutex::Autolock autoLock(mLock);
+    Mutex::Autolock autoLock(mOMXLock);
 
     if (mOMX.get() == NULL) {
-        mOMX = new OMX;
+        mOMX = new OMX();
     }
 
     return mOMX;
 }
 
+sp<IOMXStore> MediaCodecService::getOMXStore() {
+
+    Mutex::Autolock autoLock(mOMXStoreLock);
+
+    if (mOMXStore.get() == NULL) {
+        mOMXStore = new OMXStore();
+    }
+
+    return mOMXStore;
+}
 
 status_t MediaCodecService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
         uint32_t flags)
diff --git a/services/mediacodec/MediaCodecService.h b/services/mediacodec/MediaCodecService.h
index 0d2c9d8..9301135 100644
--- a/services/mediacodec/MediaCodecService.h
+++ b/services/mediacodec/MediaCodecService.h
@@ -20,10 +20,12 @@
 #include <binder/BinderService.h>
 #include <media/IMediaCodecService.h>
 #include <media/stagefright/omx/OMX.h>
+#include <media/stagefright/omx/OMXStore.h>
 
 namespace android {
 
-class MediaCodecService : public BinderService<MediaCodecService>, public BnMediaCodecService
+class MediaCodecService : public BinderService<MediaCodecService>,
+        public BnMediaCodecService
 {
     friend class BinderService<MediaCodecService>;    // for MediaCodecService()
 public:
@@ -31,16 +33,20 @@
     virtual ~MediaCodecService() { }
     virtual void onFirstRef() { }
 
-    static const char*  getServiceName() { return "media.codec"; }
+    static const char*    getServiceName() { return "media.codec"; }
 
-    virtual sp<IOMX>    getOMX();
+    virtual sp<IOMX>      getOMX();
 
-    virtual status_t    onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                uint32_t flags);
+    virtual sp<IOMXStore> getOMXStore();
+
+    virtual status_t      onTransact(uint32_t code, const Parcel& data,
+                                     Parcel* reply, uint32_t flags);
 
 private:
-    Mutex               mLock;
-    sp<IOMX>            mOMX;
+    Mutex                 mOMXLock;
+    sp<IOMX>              mOMX;
+    Mutex                 mOMXStoreLock;
+    sp<IOMXStore>         mOMXStore;
 };
 
 }   // namespace android
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 855ae69..5b34895 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -215,8 +215,7 @@
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
 
-    aaudio_result_t result = serviceStream->start();
-    return result;
+    return serviceStream->start();
 }
 
 aaudio_result_t AAudioService::pauseStream(aaudio_handle_t streamHandle) {
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 58213f8..4be25c8 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -250,6 +250,8 @@
 
 aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
                                                    audio_port_handle_t *clientHandle) {
+    // Start the client on behalf of the AAudio service.
+    // Use the port handle that was provided by openMmapStream().
     return startClient(mMmapClient, &mPortHandle);
 }
 
@@ -262,11 +264,12 @@
 aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
                                                        audio_port_handle_t *clientHandle) {
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+    ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d))",
+          &client, client.clientUid, client.clientPid);
     audio_port_handle_t originalHandle =  *clientHandle;
-    aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->start(client,
-                                                                                    clientHandle));
-    ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d), %d => %d) returns %d",
-          &client, client.clientUid, client.clientPid,
+    status_t status = mMmapStream->start(client, clientHandle);
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+    ALOGD("AAudioServiceEndpointMMAP::startClient() , %d => %d returns %d",
           originalHandle, *clientHandle, result);
     return result;
 }
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 43d73b7..18dc12f 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -122,7 +122,6 @@
         startSharingThread_l();
     }
     if (result == AAUDIO_OK) {
-        ALOGD("AAudioServiceEndpointShared::startStream() use shared stream client.");
         result = getStreamInternal()->startClient(sharedStream->getAudioClient(), clientHandle);
     }
     return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 2dc62a0..ca7b528 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -42,7 +42,7 @@
 
 AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
         : mUpMessageQueue(nullptr)
-        , mAAudioThread()
+        , mTimestampThread()
         , mAtomicTimestamp()
         , mAudioService(audioService) {
     mMmapClient.clientUid = -1;
@@ -54,10 +54,10 @@
     ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroying %p", this);
     // If the stream is deleted when OPEN or in use then audio resources will leak.
     // This would indicate an internal error. So we want to find this ASAP.
-    LOG_ALWAYS_FATAL_IF(!(mState == AAUDIO_STREAM_STATE_CLOSED
-                        || mState == AAUDIO_STREAM_STATE_UNINITIALIZED
-                        || mState == AAUDIO_STREAM_STATE_DISCONNECTED),
-                        "service stream still open, state = %d", mState);
+    LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
+                        || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
+                        || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
+                        "service stream still open, state = %d", getState());
 }
 
 std::string AAudioServiceStreamBase::dumpHeader() {
@@ -71,7 +71,7 @@
            << std::dec << std::setfill(' ') ;
     result << std::setw(6) << mMmapClient.clientUid;
     result << std::setw(4) << (isRunning() ? "yes" : " no");
-    result << std::setw(6) << mState;
+    result << std::setw(6) << getState();
     result << std::setw(7) << getFormat();
     result << std::setw(6) << mFramesPerBurst;
     result << std::setw(5) << getSamplesPerFrame();
@@ -124,7 +124,7 @@
 
 aaudio_result_t AAudioServiceStreamBase::close() {
     aaudio_result_t result = AAUDIO_OK;
-    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+    if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
         return AAUDIO_OK;
     }
 
@@ -146,37 +146,50 @@
         mUpMessageQueue = nullptr;
     }
 
-    mState = AAUDIO_STREAM_STATE_CLOSED;
+    setState(AAUDIO_STREAM_STATE_CLOSED);
     return result;
 }
 
+aaudio_result_t AAudioServiceStreamBase::startDevice() {
+    mClientHandle = AUDIO_PORT_HANDLE_NONE;
+    return mServiceEndpoint->startStream(this, &mClientHandle);
+}
+
 /**
  * Start the flow of audio data.
  *
  * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
  */
 aaudio_result_t AAudioServiceStreamBase::start() {
+    aaudio_result_t result = AAUDIO_OK;
     if (isRunning()) {
         return AAUDIO_OK;
     }
 
     if (mServiceEndpoint == nullptr) {
         ALOGE("AAudioServiceStreamBase::start() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
+        result = AAUDIO_ERROR_INVALID_STATE;
+        goto error;
     }
+
+    // Start with fresh presentation timestamps.
+    mAtomicTimestamp.clear();
+
     mClientHandle = AUDIO_PORT_HANDLE_NONE;
-    aaudio_result_t result = mServiceEndpoint->startStream(this, &mClientHandle);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamBase::start() mServiceEndpoint returned %d", result);
-        disconnect();
-    } else {
-        if (result == AAUDIO_OK) {
-            sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
-            mState = AAUDIO_STREAM_STATE_STARTED;
-            mThreadEnabled.store(true);
-            result = mAAudioThread.start(this);
-        }
-    }
+    result = startDevice();
+    if (result != AAUDIO_OK) goto error;
+
+    // This should happen at the end of the start.
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+    setState(AAUDIO_STREAM_STATE_STARTED);
+    mThreadEnabled.store(true);
+    result = mTimestampThread.start(this);
+    if (result != AAUDIO_OK) goto error;
+
+    return result;
+
+error:
+    disconnect();
     return result;
 }
 
@@ -197,13 +210,13 @@
 
     sendCurrentTimestamp();
     mThreadEnabled.store(false);
-    result = mAAudioThread.stop();
+    result = mTimestampThread.stop();
     if (result != AAUDIO_OK) {
         disconnect();
         return result;
     }
     sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
-    mState = AAUDIO_STREAM_STATE_PAUSED;
+    setState(AAUDIO_STREAM_STATE_PAUSED);
     return result;
 }
 
@@ -234,7 +247,7 @@
     }
 
     sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
-    mState = AAUDIO_STREAM_STATE_STOPPED;
+    setState(AAUDIO_STREAM_STATE_STOPPED);
     return result;
 }
 
@@ -242,20 +255,20 @@
     aaudio_result_t result = AAUDIO_OK;
     // clear flag that tells thread to loop
     if (mThreadEnabled.exchange(false)) {
-        result = mAAudioThread.stop();
+        result = mTimestampThread.stop();
     }
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush() {
-    if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+    if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
         ALOGE("AAudioServiceStreamBase::flush() stream not paused, state = %s",
               AAudio_convertStreamStateToText(mState));
         return AAUDIO_ERROR_INVALID_STATE;
     }
     // Data will get flushed when the client receives the FLUSHED event.
     sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
-    mState = AAUDIO_STREAM_STATE_FLUSHED;
+    setState(AAUDIO_STREAM_STATE_FLUSHED);
     return AAUDIO_OK;
 }
 
@@ -283,9 +296,9 @@
 }
 
 void AAudioServiceStreamBase::disconnect() {
-    if (mState != AAUDIO_STREAM_STATE_DISCONNECTED) {
+    if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
         sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
-        mState = AAUDIO_STREAM_STATE_DISCONNECTED;
+        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
     }
 }
 
@@ -321,6 +334,9 @@
     aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
                                                     &command.timestamp.timestamp);
     if (result == AAUDIO_OK) {
+        ALOGV("sendCurrentTimestamp() SERVICE  %8lld at %lld",
+              (long long) command.timestamp.position,
+              (long long) command.timestamp.timestamp);
         command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE;
         result = writeUpMessageQueue(&command);
 
@@ -329,13 +345,16 @@
             result = getHardwareTimestamp(&command.timestamp.position,
                                           &command.timestamp.timestamp);
             if (result == AAUDIO_OK) {
+                ALOGV("sendCurrentTimestamp() HARDWARE %8lld at %lld",
+                      (long long) command.timestamp.position,
+                      (long long) command.timestamp.timestamp);
                 command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE;
                 result = writeUpMessageQueue(&command);
             }
         }
     }
 
-    if (result == AAUDIO_ERROR_UNAVAILABLE) {
+    if (result == AAUDIO_ERROR_UNAVAILABLE) { // TODO review best error code
         result = AAUDIO_OK; // just not available yet, try again later
     }
     return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 301795d..6f61401 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -191,6 +191,12 @@
         mState = state;
     }
 
+    /**
+     * Device specific startup.
+     * @return AAUDIO_OK or negative error.
+     */
+    virtual aaudio_result_t startDevice();
+
     aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
 
     aaudio_result_t sendCurrentTimestamp();
@@ -213,7 +219,7 @@
     SharedRingBuffer*       mUpMessageQueue;
     std::mutex              mUpMessageQueueLock;
 
-    AAudioThread            mAAudioThread;
+    AAudioThread            mTimestampThread;
     // This is used by one thread to tell another thread to exit. So it must be atomic.
     std::atomic<bool>       mThreadEnabled{false};
 
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 43595a4..a629ed6 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -50,7 +50,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::close() {
-    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+    if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
         return AAUDIO_OK;
     }
 
@@ -67,7 +67,6 @@
     aaudio_result_t result = AAudioServiceStreamBase::open(request,
                                                            AAUDIO_SHARING_MODE_EXCLUSIVE);
     if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamBase open returned %d", result);
         return result;
     }
 
@@ -85,14 +84,11 @@
 /**
  * Start the flow of data.
  */
-aaudio_result_t AAudioServiceStreamMMAP::start() {
-    if (isRunning()) {
-        return AAUDIO_OK;
-    }
-
-    aaudio_result_t result = AAudioServiceStreamBase::start();
+aaudio_result_t AAudioServiceStreamMMAP::startDevice() {
+    aaudio_result_t result = AAudioServiceStreamBase::startDevice();
     if (!mInService && result == AAUDIO_OK) {
-        startClient(mMmapClient, &mClientHandle);
+        // Note that this can sometimes take 200 to 300 msec for a cold start!
+        result = startClient(mMmapClient, &mClientHandle);
     }
     return result;
 }
@@ -107,7 +103,7 @@
     aaudio_result_t result = AAudioServiceStreamBase::pause();
     // TODO put before base::pause()?
     if (!mInService) {
-        stopClient(mClientHandle);
+        (void) stopClient(mClientHandle);
     }
     return result;
 }
@@ -119,13 +115,14 @@
     aaudio_result_t result = AAudioServiceStreamBase::stop();
     // TODO put before base::stop()?
     if (!mInService) {
-        stopClient(mClientHandle);
+        (void) stopClient(mClientHandle);
     }
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
                                                        audio_port_handle_t *clientHandle) {
+    // Start the client on behalf of the application. Generate a new porthandle.
     aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
     return result;
 }
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index bf0aab3..83cd2ef 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -53,14 +53,6 @@
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     /**
-     * Start the flow of audio data.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
-     */
-    aaudio_result_t start() override;
-
-    /**
      * Stop the flow of data so that start() can resume without loss of data.
      *
      * This is not guaranteed to be synchronous but it currently is.
@@ -89,6 +81,12 @@
 
     aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
 
+    /**
+     * Device specific startup.
+     * @return AAUDIO_OK or negative error.
+     */
+    aaudio_result_t startDevice() override;
+
 private:
 
     bool                     mInService = false;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 834f39f..348d407 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -237,9 +237,15 @@
 aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
 
-    aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
+    int64_t position = 0;
+    aaudio_result_t result = mServiceEndpoint->getTimestamp(&position, timeNanos);
     if (result == AAUDIO_OK) {
-        *positionFrames -= mTimestampPositionOffset.load(); // Offset from shared MMAP stream
+        int64_t offset = mTimestampPositionOffset.load();
+        // TODO, do not go below starting value
+        position -= offset; // Offset from shared MMAP stream
+        ALOGV("getHardwareTimestamp() %8lld = %8lld - %8lld",
+              (long long) position, (long long) (position + offset), (long long) offset);
     }
+    *positionFrames = position;
     return result;
 }
diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp
index 8891aba..952acd7 100644
--- a/services/soundtrigger/SoundTriggerHwService.cpp
+++ b/services/soundtrigger/SoundTriggerHwService.cpp
@@ -840,7 +840,7 @@
         }
 
         const bool supports_stop_all =
-                (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() == ENOSYS);
+                (mHalInterface != 0) && (mHalInterface->stopAllRecognitions() != -ENOSYS);
 
         for (size_t i = 0; i < mModels.size(); i++) {
             sp<Model> model = mModels.valueAt(i);