Merge "Add new features to tuner service."
diff --git a/Android.bp b/Android.bp
index 60f0ff1..ee609e1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -57,7 +57,7 @@
             min_sdk_version: "29",
             apex_available: [
                 "//apex_available:platform",
-                "com.android.bluetooth.updatable",
+                "com.android.bluetooth",
                 "com.android.media",
                 "com.android.media.swcodec",
             ],
@@ -86,7 +86,7 @@
     min_sdk_version: "29",
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth.updatable",
+        "com.android.bluetooth",
         "com.android.media",
         "com.android.media.swcodec",
     ],
diff --git a/drm/mediadrm/plugins/clearkey/hidl/Android.bp b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
index 6c68532..02ac943 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/hidl/Android.bp
@@ -93,6 +93,11 @@
     srcs: ["protos/DeviceFiles.proto"],
 }
 
+cc_library {
+    name: "libclearkeyhidl",
+    defaults: ["clearkey_service_defaults"],
+}
+
 cc_binary {
     name: "android.hardware.drm@1.2-service.clearkey",
     defaults: ["clearkey_service_defaults"],
@@ -126,3 +131,37 @@
     init_rc: ["android.hardware.drm@1.4-service-lazy.clearkey.rc"],
     vintf_fragments: ["manifest_android.hardware.drm@1.4-service.clearkey.xml"],
 }
+
+cc_fuzz {
+    name: "clearkeyV1.4_fuzzer",
+    vendor: true,
+    srcs: [
+        "fuzzer/clearkeyV1.4_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libclearkeyhidl",
+        "libclearkeycommon",
+        "libclearkeydevicefiles-protos",
+        "libjsmn",
+        "libprotobuf-cpp-lite",
+        "libutils",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hardware.drm@1.0",
+        "android.hardware.drm@1.1",
+        "android.hardware.drm@1.2",
+        "android.hardware.drm@1.3",
+        "android.hardware.drm@1.4",
+        "libcrypto",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index 0cd9375..32d7723 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -187,7 +187,7 @@
         return Status_V1_2::ERROR_DRM_CANNOT_HANDLE;
     }
 
-    *defaultUrl = "";
+    *defaultUrl = "https://default.url";
     *keyRequestType = KeyRequestType_V1_1::UNKNOWN;
     *request = std::vector<uint8_t>();
 
diff --git a/drm/mediadrm/plugins/clearkey/hidl/fuzzer/README.md b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/README.md
new file mode 100644
index 0000000..cb45460
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/README.md
@@ -0,0 +1,52 @@
+# Fuzzer for android.hardware.drm@1.4-service.clearkey
+
+## Plugin Design Considerations
+The fuzzer plugin for android.hardware.drm@1.4-service.clearkey is designed based on the understanding of the
+source code and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+android.hardware.drm@1.4-service.clearkey supports the following parameters:
+1. Security Level (parameter name: `securityLevel`)
+2. Mime Type (parameter name: `mimeType`)
+3. Key Type (parameter name: `keyType`)
+4. Crypto Mode (parameter name: `cryptoMode`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `securityLevel` | 0.`SecurityLevel::UNKNOWN` 1.`SecurityLevel::SW_SECURE_CRYPTO` 2.`SecurityLevel::SW_SECURE_DECODE` 3.`SecurityLevel::HW_SECURE_CRYPTO`  4.`SecurityLevel::HW_SECURE_DECODE` 5.`SecurityLevel::HW_SECURE_ALL`| Value obtained from FuzzedDataProvider in the range 0 to 5|
+| `mimeType` | 0.`video/mp4` 1.`video/mpeg` 2.`video/x-flv` 3.`video/mj2` 4.`video/3gp2` 5.`video/3gpp` 6.`video/3gpp2` 7.`audio/mp4` 8.`audio/mpeg` 9.`audio/aac` 10.`audio/3gp2` 11.`audio/3gpp` 12.`audio/3gpp2` 13.`audio/webm` 14.`video/webm` 15.`webm` 16.`cenc` 17.`video/unknown` 18.`audio/unknown`| Value obtained from FuzzedDataProvider in the range 0 to 18|
+| `keyType` | 0.`KeyType::OFFLINE` 1.`KeyType::STREAMING` 2.`KeyType::RELEASE` | Value obtained from FuzzedDataProvider in the range 0 to 2|
+| `cryptoMode` | 0.`Mode::UNENCRYPTED` 1.`Mode::AES_CTR` 2.`Mode::AES_CBC_CTS` 3.`Mode::AES_CBC` | Value obtained from FuzzedDataProvider in the range 0 to 3|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build clearkeyV1.4_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) clearkeyV1.4_fuzzer
+```
+#### Steps to run
+To run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/${TARGET_ARCH}/clearkeyV1.4_fuzzer/vendor/hw/clearkeyV1.4_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/drm/mediadrm/plugins/clearkey/hidl/fuzzer/clearkeyV1.4_fuzzer.cpp b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/clearkeyV1.4_fuzzer.cpp
new file mode 100644
index 0000000..afe0e6c
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/hidl/fuzzer/clearkeyV1.4_fuzzer.cpp
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <include/CreatePluginFactories.h>
+
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <hidlmemory/mapping.h>
+#include <include/ClearKeyDrmProperties.h>
+#include <include/CryptoFactory.h>
+#include <include/CryptoPlugin.h>
+#include <include/DrmPlugin.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+namespace drm = ::android::hardware::drm;
+using namespace std;
+using namespace android;
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using drm::V1_0::BufferType;
+using drm::V1_0::DestinationBuffer;
+using drm::V1_0::EventType;
+using drm::V1_0::ICryptoPlugin;
+using drm::V1_0::IDrmPlugin;
+using drm::V1_0::IDrmPluginListener;
+using drm::V1_0::KeyedVector;
+using drm::V1_0::KeyStatus;
+using drm::V1_0::KeyStatusType;
+using drm::V1_0::KeyType;
+using drm::V1_0::Mode;
+using drm::V1_0::Pattern;
+using drm::V1_0::SecureStop;
+using drm::V1_0::SharedBuffer;
+using drm::V1_0::Status;
+using drm::V1_0::SubSample;
+using drm::V1_1::DrmMetricGroup;
+using drm::V1_1::HdcpLevel;
+using drm::V1_1::SecureStopRelease;
+using drm::V1_1::SecurityLevel;
+using drm::V1_2::KeySetId;
+using drm::V1_2::OfflineLicenseState;
+using drm::V1_4::clearkey::ICryptoFactory;
+using drm::V1_4::clearkey::IDrmFactory;
+using drm::V1_4::clearkey::kAlgorithmsKey;
+using drm::V1_4::clearkey::kClientIdKey;
+using drm::V1_4::clearkey::kDeviceIdKey;
+using drm::V1_4::clearkey::kDrmErrorTestKey;
+using drm::V1_4::clearkey::kListenerTestSupportKey;
+using drm::V1_4::clearkey::kMetricsKey;
+using drm::V1_4::clearkey::kPluginDescriptionKey;
+using drm::V1_4::clearkey::kVendorKey;
+using drm::V1_4::clearkey::kVersionKey;
+
+typedef ::android::hardware::hidl_vec<uint8_t> SessionId;
+typedef ::android::hardware::hidl_vec<uint8_t> SecureStopId;
+
+static const uint8_t kInvalidUUID[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60,
+                                       0x70, 0x80, 0x10, 0x20, 0x30, 0x40,
+                                       0x50, 0x60, 0x70, 0x80};
+
+static const uint8_t kClearKeyUUID[] = {0xE2, 0x71, 0x9D, 0x58, 0xA9, 0x85,
+                                        0xB3, 0xC9, 0x78, 0x1A, 0xB0, 0x30,
+                                        0xAF, 0x78, 0xD3, 0x0E};
+
+const SecurityLevel kSecurityLevel[] = {
+    SecurityLevel::UNKNOWN,          SecurityLevel::SW_SECURE_CRYPTO,
+    SecurityLevel::SW_SECURE_DECODE, SecurityLevel::HW_SECURE_CRYPTO,
+    SecurityLevel::HW_SECURE_DECODE, SecurityLevel::HW_SECURE_ALL};
+
+const char *kMimeType[] = {
+    "video/mp4",  "video/mpeg",  "video/x-flv",   "video/mj2",    "video/3gp2",
+    "video/3gpp", "video/3gpp2", "audio/mp4",     "audio/mpeg",   "audio/aac",
+    "audio/3gp2", "audio/3gpp",  "audio/3gpp2",   "audio/webm",   "video/webm",
+    "webm",       "cenc",        "video/unknown", "audio/unknown"};
+
+const char *kCipherAlgorithm[] = {"AES/CBC/NoPadding", ""};
+
+const char *kMacAlgorithm[] = {"HmacSHA256", ""};
+
+const char *kRSAAlgorithm[] = {"RSASSA-PSS-SHA1", ""};
+
+const std::string kProperty[] = {kVendorKey,
+                                 kVersionKey,
+                                 kPluginDescriptionKey,
+                                 kAlgorithmsKey,
+                                 kListenerTestSupportKey,
+                                 kDrmErrorTestKey,
+                                 kDeviceIdKey,
+                                 kClientIdKey,
+                                 kMetricsKey,
+                                 "placeholder"};
+
+const KeyType kKeyType[] = {KeyType::OFFLINE, KeyType::STREAMING,
+                            KeyType::RELEASE};
+
+const Mode kCryptoMode[] = {Mode::UNENCRYPTED, Mode::AES_CTR, Mode::AES_CBC_CTS,
+                            Mode::AES_CBC};
+
+const hidl_vec<uint8_t> validInitData = {
+    // BMFF box header (4 bytes size + 'pssh')
+    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+    // full box header (version = 1 flags = 0)
+    0x01, 0x00, 0x00, 0x00,
+    // system id
+    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
+    0x52, 0xe2, 0xfb, 0x4b,
+    // number of key ids
+    0x00, 0x00, 0x00, 0x01,
+    // key id
+    0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87, 0x7e, 0x57, 0xd0, 0x0d,
+    0x1e, 0xd0, 0x0d, 0x1e,
+    // size of data, must be zero
+    0x00, 0x00, 0x00, 0x00};
+
+const hidl_vec<uint8_t> validKeyResponse = {
+    0x7b, 0x22, 0x6b, 0x65, 0x79, 0x73, 0x22, 0x3a, 0x5b, 0x7b, 0x22,
+    0x6b, 0x74, 0x79, 0x22, 0x3a, 0x22, 0x6f, 0x63, 0x74, 0x22, 0x2c,
+    0x22, 0x6b, 0x69, 0x64, 0x22, 0x3a, 0x22, 0x59, 0x41, 0x59, 0x65,
+    0x41, 0x58, 0x35, 0x48, 0x66, 0x6f, 0x64, 0x2d, 0x56, 0x39, 0x41,
+    0x4e, 0x48, 0x74, 0x41, 0x4e, 0x48, 0x67, 0x22, 0x2c, 0x22, 0x6b,
+    0x22, 0x3a, 0x22, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54, 0x65,
+    0x73, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x61, 0x73, 0x65, 0x36, 0x34,
+    0x67, 0x67, 0x67, 0x22, 0x7d, 0x5d, 0x7d, 0x0a};
+
+const size_t kAESBlockSize = 16;
+const size_t kMaxStringLength = 100;
+const size_t kMaxSubSamples = 10;
+const size_t kMaxNumBytes = 1000;
+const size_t kSegmentIndex = 0;
+
+template <typename T, size_t size>
+T getValueFromArray(FuzzedDataProvider *fdp, const T (&arr)[size]) {
+  return arr[fdp->ConsumeIntegralInRange<int32_t>(0, size - 1)];
+}
+
+class TestDrmPluginListener : public IDrmPluginListener {
+public:
+  TestDrmPluginListener() {}
+  virtual ~TestDrmPluginListener() {}
+
+  virtual Return<void> sendEvent(EventType /*eventType*/,
+                                 const hidl_vec<uint8_t> & /*sessionId*/,
+                                 const hidl_vec<uint8_t> & /*data*/) override {
+    return Return<void>();
+  }
+
+  virtual Return<void>
+  sendExpirationUpdate(const hidl_vec<uint8_t> & /*sessionId*/,
+                       int64_t /*expiryTimeInMS*/) override {
+    return Return<void>();
+  }
+
+  virtual Return<void>
+  sendKeysChange(const hidl_vec<uint8_t> & /*sessionId*/,
+                 const hidl_vec<KeyStatus> & /*keyStatusList*/,
+                 bool /*hasNewUsableKey*/) override {
+    return Return<void>();
+  }
+};
+
+class ClearKeyFuzzer {
+public:
+  ~ClearKeyFuzzer() { deInit(); }
+  bool init();
+  void process(const uint8_t *data, size_t size);
+
+private:
+  void deInit();
+  void invokeDrmPlugin(const uint8_t *data, size_t size);
+  void invokeCryptoPlugin(const uint8_t *data);
+  void invokeDrm(const uint8_t *data, size_t size);
+  void invokeCrypto(const uint8_t *data);
+  void invokeDrmDecryptEncryptAPI(const uint8_t *data, size_t size);
+  bool invokeDrmFactory();
+  bool invokeCryptoFactory();
+  void invokeDrmV1_4API();
+  void invokeDrmSetAlgorithmAPI();
+  void invokeDrmPropertyAPI();
+  void invokeDrmSecureStopAPI();
+  void invokeDrmOfflineLicenseAPI(const uint8_t *data, size_t size);
+  SessionId getSessionId();
+  SecureStopRelease makeSecureRelease(const SecureStop &stop);
+  sp<IDrmFactory> mDrmFactory = nullptr;
+  sp<ICryptoFactory> mCryptoFactory = nullptr;
+  sp<IDrmPlugin> mDrmPlugin = nullptr;
+  sp<drm::V1_1::IDrmPlugin> mDrmPluginV1_1 = nullptr;
+  sp<drm::V1_2::IDrmPlugin> mDrmPluginV1_2 = nullptr;
+  sp<drm::V1_4::IDrmPlugin> mDrmPluginV1_4 = nullptr;
+  sp<drm::V1_4::ICryptoPlugin> mCryptoPluginV1_4 = nullptr;
+  sp<ICryptoPlugin> mCryptoPlugin = nullptr;
+  FuzzedDataProvider *mFDP = nullptr;
+  SessionId mSessionId = {};
+  SessionId mSessionIdV1 = {};
+};
+
+void ClearKeyFuzzer::deInit() {
+  if (mDrmPluginV1_1) {
+    mDrmPluginV1_1->closeSession(mSessionIdV1);
+  }
+  if (mDrmPluginV1_2) {
+    mDrmPluginV1_2->closeSession(mSessionId);
+  }
+  mDrmFactory.clear();
+  mCryptoFactory.clear();
+  mDrmPlugin.clear();
+  mDrmPluginV1_1.clear();
+  mDrmPluginV1_2.clear();
+  mDrmPluginV1_4.clear();
+  mCryptoPlugin.clear();
+  mCryptoPluginV1_4.clear();
+  mSessionId = {};
+  mSessionIdV1 = {};
+}
+
+void ClearKeyFuzzer::invokeDrmV1_4API() {
+  mDrmPluginV1_4->requiresSecureDecoderDefault(
+      getValueFromArray(mFDP, kMimeType));
+  mDrmPluginV1_4->requiresSecureDecoder(
+      getValueFromArray(mFDP, kMimeType),
+      getValueFromArray(mFDP, kSecurityLevel));
+  mDrmPluginV1_4->setPlaybackId(
+      mSessionId, mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str());
+  drm::V1_4::IDrmPlugin::getLogMessages_cb cb =
+      [&]([[maybe_unused]] drm::V1_4::Status status,
+          [[maybe_unused]] hidl_vec<drm::V1_4::LogMessage> logs) {};
+  mDrmPluginV1_4->getLogMessages(cb);
+}
+
+void ClearKeyFuzzer::invokeDrmSetAlgorithmAPI() {
+  const hidl_string cipherAlgo =
+      mFDP->ConsumeBool()
+          ? mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str()
+          : hidl_string(kCipherAlgorithm[mFDP->ConsumeBool()]);
+  mDrmPluginV1_2->setCipherAlgorithm(mSessionId, cipherAlgo);
+
+  const hidl_string macAlgo =
+      mFDP->ConsumeBool()
+          ? mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str()
+          : hidl_string(kMacAlgorithm[mFDP->ConsumeBool()]);
+  mDrmPluginV1_2->setMacAlgorithm(mSessionId, macAlgo);
+}
+
+void ClearKeyFuzzer::invokeDrmPropertyAPI() {
+  mDrmPluginV1_2->setPropertyString(
+      hidl_string(getValueFromArray(mFDP, kProperty)), hidl_string("value"));
+
+  hidl_string stringValue;
+  mDrmPluginV1_2->getPropertyString(
+      getValueFromArray(mFDP, kProperty),
+      [&](Status status, const hidl_string &hValue) {
+        if (status == Status::OK) {
+          stringValue = hValue;
+        }
+      });
+
+  hidl_vec<uint8_t> value = {};
+  mDrmPluginV1_2->setPropertyByteArray(
+      hidl_string(getValueFromArray(mFDP, kProperty)), value);
+
+  hidl_vec<uint8_t> byteValue;
+  mDrmPluginV1_2->getPropertyByteArray(
+      getValueFromArray(mFDP, kProperty),
+      [&](Status status, const hidl_vec<uint8_t> &hValue) {
+        if (status == Status::OK) {
+          byteValue = hValue;
+        }
+      });
+}
+
+SessionId ClearKeyFuzzer::getSessionId() {
+  SessionId emptySessionId = {};
+  return mFDP->ConsumeBool() ? mSessionId : emptySessionId;
+}
+
+void ClearKeyFuzzer::invokeDrmDecryptEncryptAPI(const uint8_t *data,
+                                                size_t size) {
+  uint32_t currSessions, maximumSessions;
+  mDrmPluginV1_2->getNumberOfSessions(
+      [&](Status status, uint32_t hCurrentSessions, uint32_t hMaxSessions) {
+        if (status == Status::OK) {
+          currSessions = hCurrentSessions;
+          maximumSessions = hMaxSessions;
+        }
+      });
+
+  HdcpLevel connected, maximum;
+  mDrmPluginV1_2->getHdcpLevels([&](Status status,
+                                    const HdcpLevel &hConnectedLevel,
+                                    const HdcpLevel &hMaxLevel) {
+    if (status == Status::OK) {
+      connected = hConnectedLevel;
+      maximum = hMaxLevel;
+    }
+  });
+
+  drm::V1_2::HdcpLevel connectedV1_2, maximumV1_2;
+  mDrmPluginV1_2->getHdcpLevels_1_2(
+      [&](drm::V1_2::Status status, const drm::V1_2::HdcpLevel &connectedLevel,
+          const drm::V1_2::HdcpLevel &maxLevel) {
+        if (status == drm::V1_2::Status::OK) {
+          connectedV1_2 = connectedLevel;
+          maximumV1_2 = maxLevel;
+        }
+      });
+
+  SecurityLevel securityLevel;
+  mDrmPluginV1_2->getSecurityLevel(mSessionId,
+                                   [&](Status status, SecurityLevel hLevel) {
+                                     if (status == Status::OK) {
+                                       securityLevel = hLevel;
+                                     }
+                                   });
+
+  hidl_vec<DrmMetricGroup> metrics;
+  mDrmPluginV1_2->getMetrics(
+      [&](Status status, hidl_vec<DrmMetricGroup> hMetricGroups) {
+        if (status == Status::OK) {
+          metrics = hMetricGroups;
+        }
+      });
+
+  hidl_string certificateType;
+  hidl_string certificateAuthority;
+  mDrmPluginV1_2->getProvisionRequest(certificateType, certificateAuthority,
+                                      [&]([[maybe_unused]] Status status,
+                                          const hidl_vec<uint8_t> &,
+                                          const hidl_string &) {});
+
+  mDrmPluginV1_2->getProvisionRequest_1_2(
+      certificateType, certificateAuthority,
+      [&]([[maybe_unused]] drm::V1_2::Status status, const hidl_vec<uint8_t> &,
+          const hidl_string &) {});
+
+  hidl_vec<uint8_t> response;
+  mDrmPluginV1_2->provideProvisionResponse(
+      response, [&]([[maybe_unused]] Status status, const hidl_vec<uint8_t> &,
+                    const hidl_vec<uint8_t> &) {});
+
+  hidl_vec<uint8_t> initData = {};
+  if (mFDP->ConsumeBool()) {
+    initData = validInitData;
+  } else {
+    initData.setToExternal(const_cast<uint8_t *>(data), kAESBlockSize);
+  }
+  hidl_string mimeType = getValueFromArray(mFDP, kMimeType);
+  KeyType keyType = mFDP->ConsumeBool()
+                        ? static_cast<KeyType>(mFDP->ConsumeIntegral<size_t>())
+                        : getValueFromArray(mFDP, kKeyType);
+  KeyedVector optionalParameters;
+  mDrmPluginV1_2->getKeyRequest_1_2(
+      mSessionId, initData, mimeType, keyType, optionalParameters,
+      [&]([[maybe_unused]] drm::V1_2::Status status, const hidl_vec<uint8_t> &,
+          drm::V1_1::KeyRequestType, const hidl_string &) {});
+  mDrmPluginV1_1->getKeyRequest_1_1(
+      mSessionIdV1, initData, mimeType, keyType, optionalParameters,
+      [&]([[maybe_unused]] drm::V1_0::Status status, const hidl_vec<uint8_t> &,
+          drm::V1_1::KeyRequestType, const hidl_string &) {});
+  hidl_vec<uint8_t> emptyInitData = {};
+  mDrmPlugin->getKeyRequest(
+      mSessionId, mFDP->ConsumeBool() ? initData : emptyInitData, mimeType,
+      keyType, optionalParameters,
+      [&]([[maybe_unused]] drm::V1_0::Status status, const hidl_vec<uint8_t> &,
+          drm::V1_0::KeyRequestType, const hidl_string &) {});
+
+  hidl_vec<uint8_t> keyResponse = {};
+  if (mFDP->ConsumeBool()) {
+    keyResponse = validKeyResponse;
+  } else {
+    keyResponse.setToExternal(const_cast<uint8_t *>(data), size);
+  }
+  hidl_vec<uint8_t> keySetId;
+  hidl_vec<uint8_t> emptyKeyResponse = {};
+  mDrmPluginV1_2->provideKeyResponse(
+      getSessionId(), mFDP->ConsumeBool() ? keyResponse : emptyKeyResponse,
+      [&](Status status, const hidl_vec<uint8_t> &hKeySetId) {
+        if (status == Status::OK) {
+          keySetId = hKeySetId;
+        }
+      });
+
+  mDrmPluginV1_2->restoreKeys(getSessionId(), keySetId);
+
+  mDrmPluginV1_2->queryKeyStatus(
+      getSessionId(),
+      [&]([[maybe_unused]] Status status, KeyedVector /* info */) {});
+
+  hidl_vec<uint8_t> keyId, input, iv;
+  keyId.setToExternal(const_cast<uint8_t *>(data), size);
+  input.setToExternal(const_cast<uint8_t *>(data), size);
+  iv.setToExternal(const_cast<uint8_t *>(data), size);
+  mDrmPluginV1_2->encrypt(
+      getSessionId(), keyId, input, iv,
+      [&]([[maybe_unused]] Status status, const hidl_vec<uint8_t> &) {});
+
+  mDrmPluginV1_2->decrypt(
+      getSessionId(), keyId, input, iv,
+      [&]([[maybe_unused]] Status status, const hidl_vec<uint8_t> &) {});
+
+  hidl_vec<uint8_t> message;
+  message.setToExternal(const_cast<uint8_t *>(data), size);
+  mDrmPluginV1_2->sign(
+      getSessionId(), keyId, message,
+      [&]([[maybe_unused]] Status status, const hidl_vec<uint8_t> &) {});
+
+  hidl_vec<uint8_t> signature;
+  signature.setToExternal(const_cast<uint8_t *>(data), size);
+  mDrmPluginV1_2->verify(getSessionId(), keyId, message, signature,
+                         [&]([[maybe_unused]] Status status, bool) {});
+
+  hidl_vec<uint8_t> wrappedKey;
+  signature.setToExternal(const_cast<uint8_t *>(data), size);
+  mDrmPluginV1_2->signRSA(
+      getSessionId(), kRSAAlgorithm[mFDP->ConsumeBool()], message, wrappedKey,
+      [&]([[maybe_unused]] Status status, const hidl_vec<uint8_t> &) {});
+
+  mDrmPluginV1_2->removeKeys(getSessionId());
+}
+
+/**
+ * Helper function to create a secure release message for
+ * a secure stop. The clearkey secure stop release format
+ * is just a count followed by the secure stop opaque data.
+ */
+SecureStopRelease ClearKeyFuzzer::makeSecureRelease(const SecureStop &stop) {
+  std::vector<uint8_t> stopData = stop.opaqueData;
+  std::vector<uint8_t> buffer;
+  std::string count = "0001";
+
+  auto it = buffer.insert(buffer.begin(), count.begin(), count.end());
+  buffer.insert(it + count.size(), stopData.begin(), stopData.end());
+  SecureStopRelease release = {.opaqueData = hidl_vec<uint8_t>(buffer)};
+  return release;
+}
+
+void ClearKeyFuzzer::invokeDrmSecureStopAPI() {
+  SecureStopId ssid;
+  mDrmPluginV1_2->getSecureStop(
+      ssid, [&]([[maybe_unused]] Status status, const SecureStop &) {});
+
+  mDrmPluginV1_2->getSecureStopIds(
+      [&]([[maybe_unused]] Status status,
+          [[maybe_unused]] const hidl_vec<SecureStopId> &secureStopIds) {});
+
+  SecureStopRelease release;
+  mDrmPluginV1_2->getSecureStops(
+      [&]([[maybe_unused]] Status status, const hidl_vec<SecureStop> &stops) {
+        if (stops.size() > 0) {
+          release = makeSecureRelease(
+              stops[mFDP->ConsumeIntegralInRange<size_t>(0, stops.size() - 1)]);
+        }
+      });
+
+  mDrmPluginV1_2->releaseSecureStops(release);
+
+  mDrmPluginV1_2->removeSecureStop(ssid);
+
+  mDrmPluginV1_2->removeAllSecureStops();
+
+  mDrmPluginV1_2->releaseSecureStop(ssid);
+
+  mDrmPluginV1_2->releaseAllSecureStops();
+}
+
+void ClearKeyFuzzer::invokeDrmOfflineLicenseAPI(const uint8_t *data,
+                                                size_t size) {
+  hidl_vec<KeySetId> keySetIds = {};
+  mDrmPluginV1_2->getOfflineLicenseKeySetIds(
+      [&](Status status, const hidl_vec<KeySetId> &hKeySetIds) {
+        if (status == Status::OK) {
+          keySetIds = hKeySetIds;
+        }
+      });
+
+  OfflineLicenseState licenseState;
+  KeySetId keySetId = {};
+  if (keySetIds.size() > 0) {
+    keySetId = keySetIds[mFDP->ConsumeIntegralInRange<size_t>(
+        0, keySetIds.size() - 1)];
+  } else {
+    keySetId.setToExternal(const_cast<uint8_t *>(data), size);
+  }
+  mDrmPluginV1_2->getOfflineLicenseState(
+      keySetId, [&](Status status, OfflineLicenseState hLicenseState) {
+        if (status == Status::OK) {
+          licenseState = hLicenseState;
+        }
+      });
+
+  mDrmPluginV1_2->removeOfflineLicense(keySetId);
+}
+
+void ClearKeyFuzzer::invokeDrmPlugin(const uint8_t *data, size_t size) {
+  SecurityLevel secLevel =
+      mFDP->ConsumeBool()
+          ? getValueFromArray(mFDP, kSecurityLevel)
+          : static_cast<SecurityLevel>(mFDP->ConsumeIntegral<uint32_t>());
+  mDrmPluginV1_1->openSession_1_1(
+      secLevel, [&]([[maybe_unused]] Status status, const SessionId &id) {
+        mSessionIdV1 = id;
+      });
+  mDrmPluginV1_2->openSession([&]([[maybe_unused]] Status status,
+                                  const SessionId &id) { mSessionId = id; });
+
+  sp<TestDrmPluginListener> listener = new TestDrmPluginListener();
+  mDrmPluginV1_2->setListener(listener);
+  const hidl_vec<KeyStatus> keyStatusList = {
+      {{1}, KeyStatusType::USABLE},
+      {{2}, KeyStatusType::EXPIRED},
+      {{3}, KeyStatusType::OUTPUTNOTALLOWED},
+      {{4}, KeyStatusType::STATUSPENDING},
+      {{5}, KeyStatusType::INTERNALERROR},
+  };
+  mDrmPluginV1_2->sendKeysChange(mSessionId, keyStatusList, true);
+
+  invokeDrmV1_4API();
+  invokeDrmSetAlgorithmAPI();
+  invokeDrmPropertyAPI();
+  invokeDrmDecryptEncryptAPI(data, size);
+  invokeDrmSecureStopAPI();
+  invokeDrmOfflineLicenseAPI(data, size);
+}
+
+void ClearKeyFuzzer::invokeCryptoPlugin(const uint8_t *data) {
+  mCryptoPlugin->requiresSecureDecoderComponent(
+      getValueFromArray(mFDP, kMimeType));
+
+  const uint32_t width = mFDP->ConsumeIntegral<uint32_t>();
+  const uint32_t height = mFDP->ConsumeIntegral<uint32_t>();
+  mCryptoPlugin->notifyResolution(width, height);
+
+  mCryptoPlugin->setMediaDrmSession(mSessionId);
+
+  size_t totalSize = 0;
+  const size_t numSubSamples =
+      mFDP->ConsumeIntegralInRange<size_t>(1, kMaxSubSamples);
+
+  const Pattern pattern = {0, 0};
+  hidl_vec<SubSample> subSamples;
+  subSamples.resize(numSubSamples);
+
+  for (size_t i = 0; i < numSubSamples; ++i) {
+    const uint32_t clearBytes =
+        mFDP->ConsumeIntegralInRange<uint32_t>(0, kMaxNumBytes);
+    const uint32_t encryptedBytes =
+        mFDP->ConsumeIntegralInRange<uint32_t>(0, kMaxNumBytes);
+    subSamples[i].numBytesOfClearData = clearBytes;
+    subSamples[i].numBytesOfEncryptedData = encryptedBytes;
+    totalSize += subSamples[i].numBytesOfClearData;
+    totalSize += subSamples[i].numBytesOfEncryptedData;
+  }
+
+  // The first totalSize bytes of shared memory is the encrypted
+  // input, the second totalSize bytes is the decrypted output.
+  size_t memoryBytes = totalSize * 2;
+
+  sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+  if (!ashmemAllocator.get()) {
+    return;
+  }
+
+  hidl_memory hidlMemory;
+  ashmemAllocator->allocate(memoryBytes, [&]([[maybe_unused]] bool success,
+                                             const hidl_memory &memory) {
+    mCryptoPlugin->setSharedBufferBase(memory, kSegmentIndex);
+    hidlMemory = memory;
+  });
+
+  sp<IMemory> mappedMemory = mapMemory(hidlMemory);
+  if (!mappedMemory.get()) {
+    return;
+  }
+  mCryptoPlugin->setSharedBufferBase(hidlMemory, kSegmentIndex);
+
+  uint32_t srcBufferId =
+      mFDP->ConsumeBool() ? kSegmentIndex : mFDP->ConsumeIntegral<uint32_t>();
+  const SharedBuffer sourceBuffer = {
+      .bufferId = srcBufferId, .offset = 0, .size = totalSize};
+
+  BufferType type = mFDP->ConsumeBool() ? BufferType::SHARED_MEMORY
+                                        : BufferType::NATIVE_HANDLE;
+  uint32_t destBufferId =
+      mFDP->ConsumeBool() ? kSegmentIndex : mFDP->ConsumeIntegral<uint32_t>();
+  const DestinationBuffer destBuffer = {
+      .type = type,
+      {.bufferId = destBufferId, .offset = totalSize, .size = totalSize},
+      .secureMemory = nullptr};
+
+  const uint64_t offset = 0;
+  uint32_t bytesWritten = 0;
+  hidl_array<uint8_t, kAESBlockSize> keyId =
+      hidl_array<uint8_t, kAESBlockSize>(data);
+  hidl_array<uint8_t, kAESBlockSize> iv =
+      hidl_array<uint8_t, kAESBlockSize>(data);
+  Mode mode = getValueFromArray(mFDP, kCryptoMode);
+  mCryptoPlugin->decrypt(
+      mFDP->ConsumeBool(), keyId, iv, mode, pattern, subSamples, sourceBuffer,
+      offset, destBuffer,
+      [&]([[maybe_unused]] Status status, uint32_t count,
+          [[maybe_unused]] string detailedError) { bytesWritten = count; });
+  drm::V1_4::IDrmPlugin::getLogMessages_cb cb =
+      [&]([[maybe_unused]] drm::V1_4::Status status,
+          [[maybe_unused]] hidl_vec<drm::V1_4::LogMessage> logs) {};
+  mCryptoPluginV1_4->getLogMessages(cb);
+}
+
+bool ClearKeyFuzzer::invokeDrmFactory() {
+  hidl_string packageName(
+      mFDP->ConsumeRandomLengthString(kMaxStringLength).c_str());
+  hidl_string mimeType(getValueFromArray(mFDP, kMimeType));
+  SecurityLevel securityLevel =
+      mFDP->ConsumeBool()
+          ? getValueFromArray(mFDP, kSecurityLevel)
+          : static_cast<SecurityLevel>(mFDP->ConsumeIntegral<uint32_t>());
+  const hidl_array<uint8_t, 16> uuid =
+      mFDP->ConsumeBool() ? kClearKeyUUID : kInvalidUUID;
+  mDrmFactory->isCryptoSchemeSupported_1_2(uuid, mimeType, securityLevel);
+  mDrmFactory->createPlugin(
+      uuid, packageName, [&](Status status, const sp<IDrmPlugin> &plugin) {
+        if (status == Status::OK) {
+          mDrmPlugin = plugin.get();
+          mDrmPluginV1_1 = drm::V1_1::IDrmPlugin::castFrom(mDrmPlugin);
+          mDrmPluginV1_2 = drm::V1_2::IDrmPlugin::castFrom(mDrmPlugin);
+          mDrmPluginV1_4 = drm::V1_4::IDrmPlugin::castFrom(mDrmPlugin);
+        }
+      });
+
+  std::vector<hidl_array<uint8_t, 16>> supportedSchemes;
+  mDrmFactory->getSupportedCryptoSchemes(
+      [&](const hidl_vec<hidl_array<uint8_t, 16>> &schemes) {
+        for (const auto &scheme : schemes) {
+          supportedSchemes.push_back(scheme);
+        }
+      });
+
+  if (!(mDrmPlugin && mDrmPluginV1_1 && mDrmPluginV1_2 && mDrmPluginV1_4)) {
+    return false;
+  }
+  return true;
+}
+
+bool ClearKeyFuzzer::invokeCryptoFactory() {
+  const hidl_array<uint8_t, 16> uuid =
+      mFDP->ConsumeBool() ? kClearKeyUUID : kInvalidUUID;
+  mCryptoFactory->createPlugin(
+      uuid, mSessionId, [this](Status status, const sp<ICryptoPlugin> &plugin) {
+        if (status == Status::OK) {
+          mCryptoPlugin = plugin;
+          mCryptoPluginV1_4 = drm::V1_4::ICryptoPlugin::castFrom(mCryptoPlugin);
+        }
+      });
+
+  if (!mCryptoPlugin && !mCryptoPluginV1_4) {
+    return false;
+  }
+  return true;
+}
+
+void ClearKeyFuzzer::invokeDrm(const uint8_t *data, size_t size) {
+  if (!invokeDrmFactory()) {
+    return;
+  }
+  invokeDrmPlugin(data, size);
+}
+
+void ClearKeyFuzzer::invokeCrypto(const uint8_t *data) {
+  if (!invokeCryptoFactory()) {
+    return;
+  }
+  invokeCryptoPlugin(data);
+}
+
+void ClearKeyFuzzer::process(const uint8_t *data, size_t size) {
+  mFDP = new FuzzedDataProvider(data, size);
+  invokeDrm(data, size);
+  invokeCrypto(data);
+  delete mFDP;
+}
+
+bool ClearKeyFuzzer::init() {
+  mCryptoFactory =
+      android::hardware::drm::V1_4::clearkey::createCryptoFactory();
+  mDrmFactory = android::hardware::drm::V1_4::clearkey::createDrmFactory();
+  if (!mDrmFactory && !mCryptoFactory) {
+    return false;
+  }
+  return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  if (size < kAESBlockSize) {
+    return 0;
+  }
+  ClearKeyFuzzer clearKeyFuzzer;
+  if (clearKeyFuzzer.init()) {
+    clearKeyFuzzer.process(data, size);
+  }
+  return 0;
+}
diff --git a/media/codec2/TEST_MAPPING b/media/codec2/TEST_MAPPING
index 16cb323..c6728c8 100644
--- a/media/codec2/TEST_MAPPING
+++ b/media/codec2/TEST_MAPPING
@@ -35,6 +35,17 @@
           "exclude-filter": "android.media.audio.cts.AudioRecordTest"
         }
       ]
+    },
+    {
+      "name": "CtsMediaPlayerTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+        }
+      ]
     }
   ]
 }
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index 7bd3358..475d863 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -20,6 +20,7 @@
 
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
 #include <Codec2Mapper.h>
 #include <SimpleC2Interface.h>
 #include <log/log.h>
@@ -338,6 +339,7 @@
           std::make_shared<SimpleInterface<IntfImpl>>(name, id, intfImpl)),
       mIntf(intfImpl),
       mCodecCtx(nullptr) {
+  mIsFormatR10G10B10A2Supported = IsFormatR10G10B10A2SupportedForLegacyRendering();
   gettimeofday(&mTimeStart, nullptr);
   gettimeofday(&mTimeEnd, nullptr);
 }
@@ -790,7 +792,14 @@
         work->workletsProcessed = 1u;
         return false;
       }
-      format = HAL_PIXEL_FORMAT_RGBA_1010102;
+      // TODO (b/201787956) For devices that do not support HAL_PIXEL_FORMAT_RGBA_1010102,
+      // HAL_PIXEL_FORMAT_YV12 is used as a temporary work around.
+      if (!mIsFormatR10G10B10A2Supported)  {
+        ALOGE("HAL_PIXEL_FORMAT_RGBA_1010102 isn't supported");
+        format = HAL_PIXEL_FORMAT_YV12;
+      } else {
+        format = HAL_PIXEL_FORMAT_RGBA_1010102;
+      }
     }
   }
   C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index 134fa0d..f82992d 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -82,6 +82,7 @@
 
   struct timeval mTimeStart;  // Time at the start of decode()
   struct timeval mTimeEnd;    // Time at the end of decode()
+  bool mIsFormatR10G10B10A2Supported;
 
   bool initDecoder();
   void getVuiParams(const libgav1::DecoderBuffer *buffer);
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index 45e2ca8..2da9d5b 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -25,6 +25,7 @@
 
 #include <C2Debug.h>
 #include <C2PlatformSupport.h>
+#include <Codec2BufferUtils.h>
 #include <SimpleC2Interface.h>
 
 #include "C2SoftVpxDec.h"
@@ -351,6 +352,7 @@
       mCodecCtx(nullptr),
       mCoreCount(1),
       mQueue(new Mutexed<ConversionQueue>) {
+      mIsFormatR10G10B10A2Supported = IsFormatR10G10B10A2SupportedForLegacyRendering();
 }
 
 C2SoftVpxDec::~C2SoftVpxDec() {
@@ -804,7 +806,14 @@
         if (defaultColorAspects->primaries == C2Color::PRIMARIES_BT2020 &&
             defaultColorAspects->matrix == C2Color::MATRIX_BT2020 &&
             defaultColorAspects->transfer == C2Color::TRANSFER_ST2084) {
-            format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            // TODO (b/201787956) For devices that do not support HAL_PIXEL_FORMAT_RGBA_1010102,
+            // HAL_PIXEL_FORMAT_YV12 is used as a temporary work around.
+            if (!mIsFormatR10G10B10A2Supported)  {
+                ALOGE("HAL_PIXEL_FORMAT_RGBA_1010102 isn't supported");
+                format = HAL_PIXEL_FORMAT_YV12;
+            } else {
+                format = HAL_PIXEL_FORMAT_RGBA_1010102;
+            }
         }
     }
     C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.h b/media/codec2/components/vpx/C2SoftVpxDec.h
index 2065165..ade162d 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.h
+++ b/media/codec2/components/vpx/C2SoftVpxDec.h
@@ -80,7 +80,7 @@
     };
     std::shared_ptr<Mutexed<ConversionQueue>> mQueue;
     std::vector<sp<ConverterThread>> mConverterThreads;
-
+    bool mIsFormatR10G10B10A2Supported;
     status_t initDecoder();
     status_t destroyDecoder();
     void finishWork(uint64_t index, const std::unique_ptr<C2Work> &work,
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
index 5f87c66..2213001 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
@@ -118,6 +118,22 @@
 
 }  // namespace
 
+bool IsFormatR10G10B10A2SupportedForLegacyRendering() {
+    const AHardwareBuffer_Desc desc = {
+        .width = 320,
+        .height = 240,
+        .format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
+        .layers = 1,
+        .usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
+                 AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE,
+        .stride = 0,
+        .rfu0 = 0,
+        .rfu1 = 0,
+    };
+
+    return AHardwareBuffer_isSupported(&desc);
+}
+
 status_t ImageCopy(uint8_t *imgBase, const MediaImage2 *img, const C2GraphicView &view) {
     if (view.crop().width != img->mWidth || view.crop().height != img->mHeight) {
         return BAD_VALUE;
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.h b/media/codec2/sfplugin/utils/Codec2BufferUtils.h
index 9fa642d..c4651a4 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.h
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.h
@@ -27,6 +27,11 @@
 namespace android {
 
 /**
+ * Check if R10G10B10A2 is supported in legacy rendering path that involves GPU
+ */
+bool IsFormatR10G10B10A2SupportedForLegacyRendering();
+
+/**
  * Converts an RGB view to planar YUV 420 media image.
  *
  * \param dstY       pointer to media image buffer
diff --git a/media/libaaudio/scripts/measure_device_power.py b/media/libaaudio/scripts/measure_device_power.py
new file mode 100755
index 0000000..9603f88
--- /dev/null
+++ b/media/libaaudio/scripts/measure_device_power.py
@@ -0,0 +1,272 @@
+#!/usr/bin/python3
+"""
+ * Copyright (C) 2021 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.
+"""
+
+'''
+Measure CPU related power on Pixel 6 or later devices using ODPM,
+the On Device Power Measurement tool.
+Generate a CSV report for putting in a spreadsheet
+'''
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+import time
+
+# defaults
+PRE_DELAY_SECONDS = 0.5 # time to sleep before command to avoid adb unroot error
+DEFAULT_NUM_ITERATIONS = 5
+DEFAULT_FILE_NAME = 'energy_commands.txt'
+
+'''
+Default rail assignments
+philburk-macbookpro3:expt philburk$ adb shell cat /sys/bus/iio/devices/iio\:device0/energy_value
+t=349894
+CH0(T=349894)[S10M_VDD_TPU], 5578756
+CH1(T=349894)[VSYS_PWR_MODEM], 29110940
+CH2(T=349894)[VSYS_PWR_RFFE], 3166046
+CH3(T=349894)[S2M_VDD_CPUCL2], 30203502
+CH4(T=349894)[S3M_VDD_CPUCL1], 23377533
+CH5(T=349894)[S4M_VDD_CPUCL0], 46356942
+CH6(T=349894)[S5M_VDD_INT], 10771876
+CH7(T=349894)[S1M_VDD_MIF], 21091363
+philburk-macbookpro3:expt philburk$ adb shell cat /sys/bus/iio/devices/iio\:device1/energy_value
+t=359458
+CH0(T=359458)[VSYS_PWR_WLAN_BT], 45993209
+CH1(T=359458)[L2S_VDD_AOC_RET], 2822928
+CH2(T=359458)[S9S_VDD_AOC], 6923706
+CH3(T=359458)[S5S_VDDQ_MEM], 4658202
+CH4(T=359458)[S10S_VDD2L], 5506273
+CH5(T=359458)[S4S_VDD2H_MEM], 14254574
+CH6(T=359458)[S2S_VDD_G3D], 5315420
+CH7(T=359458)[VSYS_PWR_DISPLAY], 81221665
+'''
+
+'''
+LDO2M(L2M_ALIVE):DDR  -> DRAM Array Core Power
+BUCK4S(S4S_VDD2H_MEM):DDR -> Normal operation data and control path logic circuits
+BUCK5S(S5S_VDDQ_MEM):DDR -> LPDDR I/O interface
+BUCK10S(S10S_VDD2L):DDR  -> DVFSC (1600Mbps or lower) operation data and control path logic circuits
+BUCK1M (S1M_VDD_MIF):  SoC side Memory InterFace and Controller
+'''
+
+# Map between rail name and human readable name.
+ENERGY_DICTIONARY = { \
+        'S4M_VDD_CPUCL0': 'CPU0', \
+        'S3M_VDD_CPUCL1': 'CPU1', \
+        'S2M_VDD_CPUCL2': 'CPU2', \
+        'S1M_VDD_MIF': 'MIF', \
+        'L2M_ALIVE': 'DDRAC', \
+        'S4S_VDD2H_MEM': 'DDRNO', \
+        'S10S_VDD2L': 'DDR16', \
+        'S5S_VDDQ_MEM': 'DDRIO', \
+        'VSYS_PWR_DISPLAY': 'SCREEN'}
+
+SORTED_ENERGY_LIST = sorted(ENERGY_DICTIONARY, key=ENERGY_DICTIONARY.get)
+
+# Sometimes "adb unroot" returns 1!
+# So try several times.
+# @return 0 on success
+def adbUnroot():
+    returnCode = 1
+    count = 0
+    limit = 5
+    while count < limit and returnCode != 0:
+        print(('Try to adb unroot {} of {}'.format(count, limit)))
+        subprocess.call(["adb", "wait-for-device"])
+        time.sleep(PRE_DELAY_SECONDS)
+        returnCode = subprocess.call(["adb", "unroot"])
+        print(('returnCode = {}'.format(returnCode)))
+        count += 1
+    return returnCode
+
+# @param commandString String containing shell command
+# @return Both the stdout and stderr of the commands run
+def runCommand(commandString):
+    print(commandString)
+    if commandString == "adb unroot":
+        result = adbUnroot()
+    else:
+        commandArray = commandString.split(' ')
+        result = subprocess.run(commandArray, check=True, capture_output=True).stdout
+    return result
+
+# @param commandString String containing ADB command
+# @return Both the stdout and stderr of the commands run
+def adbCommand(commandString):
+    if commandString == "unroot":
+        result = adbUnroot()
+    else:
+        print(("adb " + commandString))
+        commandArray = ["adb"] + commandString.split(' ')
+        subprocess.call(["adb", "wait-for-device"])
+        result = subprocess.run(commandArray, check=True, capture_output=True).stdout
+    return result
+
+# Parse a line that looks like "CH3(T=10697635)[S2M_VDD_CPUCL2], 116655335"
+# Use S2M_VDD_CPUCL2 as the tag and set value to the number
+# in the report dictionary.
+def parseEnergyValue(string):
+    return tuple(re.split('\[|\], +', string)[1:])
+
+# Read accumulated energy into a dictionary.
+def measureEnergyForDevice(deviceIndex, report):
+    # print("measureEnergyForDevice " + str(deviceIndex))
+    tableBytes = adbCommand( \
+            'shell cat /sys/bus/iio/devices/iio\:device{}/energy_value'\
+            .format(deviceIndex))
+    table = tableBytes.decode("utf-8")
+    # print(table)
+    for count, line in enumerate(table.splitlines()):
+        if count > 0:
+            tagEnergy = parseEnergyValue(line)
+            report[tagEnergy[0]] = int(tagEnergy[1].strip())
+    # print(report)
+
+def measureEnergyOnce():
+    adbCommand("root")
+    report = {}
+    d0 = measureEnergyForDevice(0, report)
+    d1 = measureEnergyForDevice(1, report)
+    adbUnroot()
+    return report
+
+# Subtract numeric values for matching keys.
+def subtractReports(A, B):
+    return {x: A[x] - B[x] for x in A if x in B}
+
+# Add numeric values for matching keys.
+def addReports(A, B):
+    return {x: A[x] + B[x] for x in A if x in B}
+
+# Divide numeric values by divisor.
+# @return Modified copy of report.
+def divideReport(report, divisor):
+    return {key: val / divisor for key, val in list(report.items())}
+
+# Generate a dictionary that is the difference between two measurements over time.
+def measureEnergyOverTime(duration):
+    report1 = measureEnergyOnce()
+    print(("Measure energy for " + str(duration) + " seconds."))
+    time.sleep(duration)
+    report2 = measureEnergyOnce()
+    return subtractReports(report2, report1)
+
+# Generate a CSV string containing the human readable headers.
+def formatEnergyHeader():
+    header = ""
+    for tag in SORTED_ENERGY_LIST:
+        header += ENERGY_DICTIONARY[tag] + ", "
+    return header
+
+# Generate a CSV string containing the numeric values.
+def formatEnergyData(report):
+    data = ""
+    for tag in SORTED_ENERGY_LIST:
+        if tag in list(report.keys()):
+            data += str(report[tag]) + ", "
+        else:
+            data += "-1,"
+    return data
+
+def printEnergyReport(report):
+    s = "\n"
+    s += "Values are in microWattSeconds\n"
+    s += "Report below is CSV format for pasting into a spreadsheet:\n"
+    s += formatEnergyHeader() + "\n"
+    s += formatEnergyData(report) + "\n"
+    print(s)
+
+# Generate a dictionary that is the difference between two measurements
+# before and after executing the command.
+def measureEnergyForCommand(command):
+    report1 = measureEnergyOnce()
+    print(("Measure energy for:  " + command))
+    result = runCommand(command)
+    report2 = measureEnergyOnce()
+    # print(result)
+    return subtractReports(report2, report1)
+
+# Average the results of several measurements for one command.
+def averageEnergyForCommand(command, count):
+    print("=================== #0\n")
+    sumReport = measureEnergyForCommand(command)
+    for i in range(1, count):
+        print(("=================== #" + str(i) + "\n"))
+        report = measureEnergyForCommand(command)
+        sumReport = addReports(sumReport, report)
+    print(sumReport)
+    return divideReport(sumReport, count)
+
+# Parse a list of commands in a file.
+# Lines ending in "\" are continuation lines.
+# Lines beginning with "#" are comments.
+def measureEnergyForCommands(fileName):
+    finalReport = "------------------------------------\n"
+    finalReport += "comment, command, " + formatEnergyHeader() + "\n"
+    comment = ""
+    try:
+        fp = open(fileName)
+        line = fp.readline()
+        while line:
+            command = line.strip()
+            if command.endswith('\\'):
+                command = command[:-1].strip() # remove \\:
+                runCommand(command)
+            elif command.startswith("#"):
+                # ignore comment
+                print((command + "\n"))
+                comment = command
+            elif command:
+                report = averageEnergyForCommand(command, DEFAULT_NUM_ITERATIONS)
+                finalReport += comment + ", " + command + ", " + formatEnergyData(report) + "\n"
+                print(finalReport)
+            line = fp.readline()
+    finally:
+        fp.close()
+    return finalReport
+
+def main():
+    # parse command line args
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-s', '--seconds',
+            help="Measure power for N seconds. Ignore scriptFile.",
+            type=float)
+    parser.add_argument("fileName",
+            nargs = '?',
+            help="Path to file containing commands to be measured."
+                    + " Default path = " + DEFAULT_FILE_NAME + "."
+                    + " Lines ending in '\' are continuation lines."
+                    + " Lines beginning with '#' are comments.",
+                    default=DEFAULT_FILE_NAME)
+    args=parser.parse_args();
+
+    print(("seconds  = " + str(args.seconds)))
+    print(("fileName = " + str(args.fileName)))
+    # Process command line
+    if args.seconds:
+        report = measureEnergyOverTime(args.seconds)
+        printEnergyReport(report)
+    else:
+        report = measureEnergyForCommands(args.fileName)
+        print(report)
+    print("Finished.\n")
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/media/libaaudio/scripts/setup_odpm_cpu_rails.sh b/media/libaaudio/scripts/setup_odpm_cpu_rails.sh
new file mode 100644
index 0000000..e9241b9
--- /dev/null
+++ b/media/libaaudio/scripts/setup_odpm_cpu_rails.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Configure ODPM rails to measure CPU specific power.
+# See go/odpm-p21-userguide
+
+adb root
+
+# LDO2M(L2M_ALIVE) - DRAM Array Core Power
+adb shell 'echo "CH0=LDO2M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+
+# These are the defaults.
+# BUCK2M(S2M_VDD_CPUCL2):CPU(BIG)
+# adb shell 'echo "CH3=BUCK2M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+# BUCK3M(S3M_VDD_CPUCL1):CPU(MID)
+# adb shell 'echo "CH4=BUCK3M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+# BUCK4M(S4M_VDD_CPUCL0):CPU(LITTLE)
+# adb shell 'echo "CH5=BUCK4M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+# BUCK1M(S1M_VDD_MIF):MIF
+# adb shell 'echo "CH7=BUCK1M" > /sys/bus/iio/devices/iio\:device0/enabled_rails'
+
+# These are default on device1.
+# BUCK5S(S5S_VDDQ_MEM):DDR
+# adb shell 'echo "CH3=BUCK5S" > /sys/bus/iio/devices/iio\:device1/enabled_rails'
+# BUCK10S(S10S_VDD2L):DDR
+# adb shell 'echo "CH4=BUCK10S" > /sys/bus/iio/devices/iio\:device1/enabled_rails'
+# BUCK4S(S4S_VDD2H_MEM):DDR
+# adb shell 'echo "CH5=BUCK4S" > /sys/bus/iio/devices/iio\:device1/enabled_rails'
+
+adb shell 'cat /sys/bus/iio/devices/iio\:device0/enabled_rails'
+adb shell 'cat /sys/bus/iio/devices/iio\:device1/enabled_rails'
+
+adb unroot
+
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 38bcb7c..f50b53a 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -214,6 +214,7 @@
         "flowgraph/MonoBlend.cpp",
         "flowgraph/MonoToMultiConverter.cpp",
         "flowgraph/MultiToMonoConverter.cpp",
+        "flowgraph/MultiToManyConverter.cpp",
         "flowgraph/RampLinear.cpp",
         "flowgraph/SampleRateConverter.cpp",
         "flowgraph/SinkFloat.cpp",
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index d3e2912..d0c3238 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -21,8 +21,10 @@
 #include "AAudioFlowGraph.h"
 
 #include <flowgraph/ClipToRange.h>
+#include <flowgraph/ManyToMultiConverter.h>
 #include <flowgraph/MonoBlend.h>
 #include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/MultiToManyConverter.h>
 #include <flowgraph/RampLinear.h>
 #include <flowgraph/SinkFloat.h>
 #include <flowgraph/SinkI16.h>
@@ -39,12 +41,16 @@
                           int32_t sourceChannelCount,
                           audio_format_t sinkFormat,
                           int32_t sinkChannelCount,
-                          bool useMonoBlend) {
+                          bool useMonoBlend,
+                          float audioBalance,
+                          bool isExclusive) {
     FlowGraphPortFloatOutput *lastOutput = nullptr;
 
     // TODO change back to ALOGD
-    ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d",
-          __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount);
+    ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d, "
+          "useMonoBlend = %d, audioBalance = %f, isExclusive %d",
+          __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount,
+          useMonoBlend, audioBalance, isExclusive);
 
     switch (sourceFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
@@ -65,10 +71,11 @@
     }
     lastOutput = &mSource->output;
 
-    // Apply volume as a ramp to avoid pops.
-    mVolumeRamp = std::make_unique<RampLinear>(sourceChannelCount);
-    lastOutput->connect(&mVolumeRamp->input);
-    lastOutput = &mVolumeRamp->output;
+    if (useMonoBlend) {
+        mMonoBlend = std::make_unique<MonoBlend>(sourceChannelCount);
+        lastOutput->connect(&mMonoBlend->input);
+        lastOutput = &mMonoBlend->output;
+    }
 
     // For a pure float graph, there is chance that the data range may be very large.
     // So we should clip to a reasonable value that allows a little headroom.
@@ -78,12 +85,6 @@
         lastOutput = &mClipper->output;
     }
 
-    if (useMonoBlend) {
-        mMonoBlend = std::make_unique<MonoBlend>(sourceChannelCount);
-        lastOutput->connect(&mMonoBlend->input);
-        lastOutput = &mMonoBlend->output;
-    }
-
     // Expand the number of channels if required.
     if (sourceChannelCount == 1 && sinkChannelCount > 1) {
         mChannelConverter = std::make_unique<MonoToMultiConverter>(sinkChannelCount);
@@ -94,6 +95,26 @@
         return AAUDIO_ERROR_UNIMPLEMENTED;
     }
 
+    // Apply volume ramps for only exclusive streams.
+    if (isExclusive) {
+        // Apply volume ramps to set the left/right audio balance and target volumes.
+        // The signals will be decoupled, volume ramps will be applied, before the signals are
+        // combined again.
+        mMultiToManyConverter = std::make_unique<MultiToManyConverter>(sinkChannelCount);
+        mManyToMultiConverter = std::make_unique<ManyToMultiConverter>(sinkChannelCount);
+        lastOutput->connect(&mMultiToManyConverter->input);
+        for (int i = 0; i < sinkChannelCount; i++) {
+            mVolumeRamps.emplace_back(std::make_unique<RampLinear>(1));
+            mPanningVolumes.emplace_back(1.0f);
+            lastOutput = mMultiToManyConverter->outputs[i].get();
+            lastOutput->connect(&(mVolumeRamps[i].get()->input));
+            lastOutput = &(mVolumeRamps[i].get()->output);
+            lastOutput->connect(mManyToMultiConverter->inputs[i].get());
+        }
+        lastOutput = &mManyToMultiConverter->output;
+        setAudioBalance(audioBalance);
+    }
+
     switch (sinkFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
             mSink = std::make_unique<SinkFloat>(sinkChannelCount);
@@ -125,9 +146,32 @@
  * @param volume between 0.0 and 1.0
  */
 void AAudioFlowGraph::setTargetVolume(float volume) {
-    mVolumeRamp->setTarget(volume);
+    for (int i = 0; i < mVolumeRamps.size(); i++) {
+        mVolumeRamps[i]->setTarget(volume * mPanningVolumes[i]);
+    }
+    mTargetVolume = volume;
 }
 
+/**
+ * @param audioBalance between -1.0 and 1.0
+ */
+void AAudioFlowGraph::setAudioBalance(float audioBalance) {
+    if (mPanningVolumes.size() >= 2) {
+        float leftMultiplier = 0;
+        float rightMultiplier = 0;
+        mBalance.computeStereoBalance(audioBalance, &leftMultiplier, &rightMultiplier);
+        mPanningVolumes[0] = leftMultiplier;
+        mPanningVolumes[1] = rightMultiplier;
+        mVolumeRamps[0]->setTarget(mTargetVolume * leftMultiplier);
+        mVolumeRamps[1]->setTarget(mTargetVolume * rightMultiplier);
+    }
+}
+
+/**
+ * @param numFrames to slowly adjust for volume changes
+ */
 void AAudioFlowGraph::setRampLengthInFrames(int32_t numFrames) {
-    mVolumeRamp->setLengthInFrames(numFrames);
+    for (auto& ramp : mVolumeRamps) {
+        ramp->setLengthInFrames(numFrames);
+    }
 }
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index e719d91..00b6575 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -23,9 +23,12 @@
 #include <system/audio.h>
 
 #include <aaudio/AAudio.h>
+#include <audio_utils/Balance.h>
 #include <flowgraph/ClipToRange.h>
+#include <flowgraph/ManyToMultiConverter.h>
 #include <flowgraph/MonoBlend.h>
 #include <flowgraph/MonoToMultiConverter.h>
+#include <flowgraph/MultiToManyConverter.h>
 #include <flowgraph/RampLinear.h>
 
 class AAudioFlowGraph {
@@ -37,13 +40,19 @@
      * @param sourceChannelCount
      * @param sinkFormat
      * @param sinkChannelCount
+     * @param useMonoBlend
+     * @param audioBalance
+     * @param channelMask
+     * @param isExclusive
      * @return
      */
     aaudio_result_t configure(audio_format_t sourceFormat,
                               int32_t sourceChannelCount,
                               audio_format_t sinkFormat,
                               int32_t sinkChannelCount,
-                              bool useMonoBlend);
+                              bool useMonoBlend,
+                              float audioBalance,
+                              bool isExclusive);
 
     void process(const void *source, void *destination, int32_t numFrames);
 
@@ -52,14 +61,27 @@
      */
     void setTargetVolume(float volume);
 
+    /**
+     * @param audioBalance between -1.0 and 1.0
+     */
+    void setAudioBalance(float audioBalance);
+
+    /**
+     * @param numFrames to slowly adjust for volume changes
+     */
     void setRampLengthInFrames(int32_t numFrames);
 
 private:
     std::unique_ptr<flowgraph::FlowGraphSourceBuffered>     mSource;
     std::unique_ptr<flowgraph::MonoBlend>                   mMonoBlend;
-    std::unique_ptr<flowgraph::RampLinear>                  mVolumeRamp;
     std::unique_ptr<flowgraph::ClipToRange>                 mClipper;
     std::unique_ptr<flowgraph::MonoToMultiConverter>        mChannelConverter;
+    std::unique_ptr<flowgraph::ManyToMultiConverter>        mManyToMultiConverter;
+    std::unique_ptr<flowgraph::MultiToManyConverter>        mMultiToManyConverter;
+    std::vector<std::unique_ptr<flowgraph::RampLinear>>     mVolumeRamps;
+    std::vector<float>                                      mPanningVolumes;
+    float                                                   mTargetVolume = 1.0f;
+    android::audio_utils::Balance                           mBalance;
     std::unique_ptr<flowgraph::FlowGraphSink>               mSink;
 };
 
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 1b8e224..afdc2ac 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -272,12 +272,15 @@
     }
 
     // Exclusive output streams should combine channels when mono audio adjustment
-    // is enabled.
+    // is enabled. They should also adjust for audio balance.
     if ((getDirection() == AAUDIO_DIRECTION_OUTPUT) &&
         (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE)) {
         bool isMasterMono = false;
         android::AudioSystem::getMasterMono(&isMasterMono);
         setRequireMonoBlend(isMasterMono);
+        float audioBalance = 0;
+        android::AudioSystem::getMasterBalance(&audioBalance);
+        setAudioBalance(audioBalance);
     }
 
     // For debugging and analyzing the distribution of MMAP timestamps.
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index c17c7a0..450d390 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -53,7 +53,9 @@
                              getSamplesPerFrame(),
                              getDeviceFormat(),
                              getDeviceChannelCount(),
-                             getRequireMonoBlend());
+                             getRequireMonoBlend(),
+                             getAudioBalance(),
+                             (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE));
 
         if (result != AAUDIO_OK) {
             safeReleaseClose();
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index a3af753..5fb4528 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -281,6 +281,10 @@
         return mRequireMonoBlend;
     }
 
+    float getAudioBalance() const {
+        return mAudioBalance;
+    }
+
     /**
      * This is only valid after setChannelMask() and setFormat()
      * have been called.
@@ -642,6 +646,13 @@
         mRequireMonoBlend = requireMonoBlend;
     }
 
+    /**
+     * This should not be called after the open() call.
+     */
+    void setAudioBalance(float audioBalance) {
+        mAudioBalance = audioBalance;
+    }
+
     std::string mMetricsId; // set once during open()
 
     std::mutex                 mStreamLock;
@@ -684,6 +695,7 @@
     aaudio_allowed_capture_policy_t mAllowedCapturePolicy = AAUDIO_ALLOW_CAPTURE_BY_ALL;
     bool                        mIsPrivacySensitive = false;
     bool                        mRequireMonoBlend = false;
+    float                       mAudioBalance = 0;
 
     int32_t                     mSessionId = AAUDIO_UNSPECIFIED;
 
diff --git a/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp b/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp
new file mode 100644
index 0000000..f074364
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MultiToManyConverter.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+#include "FlowGraphNode.h"
+#include "MultiToManyConverter.h"
+
+using namespace flowgraph;
+
+MultiToManyConverter::MultiToManyConverter(int32_t channelCount)
+        : outputs(channelCount)
+        , input(*this, channelCount) {
+    for (int i = 0; i < channelCount; i++) {
+        outputs[i] = std::make_unique<FlowGraphPortFloatOutput>(*this, 1);
+    }
+}
+
+MultiToManyConverter::~MultiToManyConverter() = default;
+
+int32_t MultiToManyConverter::onProcess(int32_t numFrames) {
+    int32_t channelCount = input.getSamplesPerFrame();
+
+    for (int ch = 0; ch < channelCount; ch++) {
+        const float *inputBuffer = input.getBuffer() + ch;
+        float *outputBuffer = outputs[ch]->getBuffer();
+
+        for (int i = 0; i < numFrames; i++) {
+            *outputBuffer++ = *inputBuffer;
+            inputBuffer += channelCount;
+        }
+    }
+
+    return numFrames;
+}
+
diff --git a/media/libaaudio/src/flowgraph/MultiToManyConverter.h b/media/libaaudio/src/flowgraph/MultiToManyConverter.h
new file mode 100644
index 0000000..de31475
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/MultiToManyConverter.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H
+#define FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "FlowGraphNode.h"
+
+namespace flowgraph {
+
+/**
+ * Convert a multi-channel interleaved stream to multiple mono-channel
+ * outputs
+ */
+    class MultiToManyConverter : public FlowGraphNode {
+    public:
+        explicit MultiToManyConverter(int32_t channelCount);
+
+        virtual ~MultiToManyConverter();
+
+        int32_t onProcess(int32_t numFrames) override;
+
+        const char *getName() override {
+            return "MultiToManyConverter";
+        }
+
+        std::vector<std::unique_ptr<flowgraph::FlowGraphPortFloatOutput>> outputs;
+        flowgraph::FlowGraphPortFloatInput input;
+    };
+
+} /* namespace flowgraph */
+
+#endif //FLOWGRAPH_MULTI_TO_MANY_CONVERTER_H
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 7e180a2..ab75c97 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -205,7 +205,7 @@
     ],
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth.updatable",
+        "com.android.bluetooth",
         "com.android.media",
         "com.android.media.swcodec",
     ],
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index ea4faa8..f191c49 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -1077,6 +1077,7 @@
         .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
         // the following are NOT immutable
         .set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
+        .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
         .set(AMEDIAMETRICS_PROP_SELECTEDMICDIRECTION, (int32_t)mSelectedMicDirection)
         .set(AMEDIAMETRICS_PROP_SELECTEDMICFIELDDIRECTION, (double)mSelectedMicFieldDimension)
         .record();
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 066a7ae..8be62ed 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -2121,6 +2121,7 @@
         .set(AMEDIAMETRICS_PROP_VOLUME_LEFT, (double)mVolume[AUDIO_INTERLEAVE_LEFT])
         .set(AMEDIAMETRICS_PROP_VOLUME_RIGHT, (double)mVolume[AUDIO_INTERLEAVE_RIGHT])
         .set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
+        .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)NO_ERROR)
         .set(AMEDIAMETRICS_PROP_AUXEFFECTID, (int32_t)mAuxEffectId)
         .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
         .set(AMEDIAMETRICS_PROP_PLAYBACK_SPEED, (double)mPlaybackRate.mSpeed)
@@ -2163,8 +2164,8 @@
     // Ensure these variables are initialized in set().
     mediametrics::LogItem(AMEDIAMETRICS_KEY_AUDIO_TRACK_ERROR)
         .set(AMEDIAMETRICS_PROP_EVENT, event)
-        .set(AMEDIAMETRICS_PROP_ERROR, mediametrics::statusToErrorString(status))
-        .set(AMEDIAMETRICS_PROP_ERRORMESSAGE, message)
+        .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
+        .set(AMEDIAMETRICS_PROP_STATUSMESSAGE, message)
         .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, toString(mOrigFlags).c_str())
         .set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
         .set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(mAttributes.content_type).c_str())
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index da27dc8..e3b79b2 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -490,6 +490,8 @@
 status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested)
 {
     struct timespec total;          // total elapsed time spent waiting
+    struct timespec before;
+    bool beforeIsValid = false;
     total.tv_sec = 0;
     total.tv_nsec = 0;
     audio_track_cblk_t* cblk = mCblk;
@@ -570,17 +572,38 @@
         }
         int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
         if (!(old & CBLK_FUTEX_WAKE)) {
+            if (!beforeIsValid) {
+                clock_gettime(CLOCK_MONOTONIC, &before);
+                beforeIsValid = true;
+            }
             errno = 0;
             (void) syscall(__NR_futex, &cblk->mFutex,
                     mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
-            switch (errno) {
+            status_t error = errno; // clock_gettime can affect errno
+            {
+                struct timespec after;
+                clock_gettime(CLOCK_MONOTONIC, &after);
+                total.tv_sec += after.tv_sec - before.tv_sec;
+                // Use auto instead of long to avoid the google-runtime-int warning.
+                auto deltaNs = after.tv_nsec - before.tv_nsec;
+                if (deltaNs < 0) {
+                    deltaNs += 1000000000;
+                    total.tv_sec--;
+                }
+                if ((total.tv_nsec += deltaNs) >= 1000000000) {
+                    total.tv_nsec -= 1000000000;
+                    total.tv_sec++;
+                }
+                before = after;
+            }
+            switch (error) {
             case 0:            // normal wakeup by server, or by binderDied()
             case EWOULDBLOCK:  // benign race condition with server
             case EINTR:        // wait was interrupted by signal or other spurious wakeup
             case ETIMEDOUT:    // time-out expired
                 break;
             default:
-                status = errno;
+                status = error;
                 ALOGE("%s unexpected error %s", __func__, strerror(status));
                 goto end;
             }
diff --git a/media/libaudiofoundation/AudioContainers.cpp b/media/libaudiofoundation/AudioContainers.cpp
index 117d188..553a319 100644
--- a/media/libaudiofoundation/AudioContainers.cpp
+++ b/media/libaudiofoundation/AudioContainers.cpp
@@ -89,6 +89,11 @@
     return ss.str();
 }
 
+bool deviceTypesToString(const DeviceTypeSet &deviceTypes, std::string &str) {
+    str = deviceTypesToString(deviceTypes);
+    return true;
+}
+
 std::string dumpDeviceTypes(const DeviceTypeSet &deviceTypes) {
     std::stringstream ss;
     for (auto it = deviceTypes.begin(); it != deviceTypes.end(); ++it) {
diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h
index 707ab68..d352a96 100644
--- a/media/libaudiofoundation/include/media/AudioContainers.h
+++ b/media/libaudiofoundation/include/media/AudioContainers.h
@@ -133,6 +133,8 @@
 
 std::string deviceTypesToString(const DeviceTypeSet& deviceTypes);
 
+bool deviceTypesToString(const DeviceTypeSet& deviceTypes, std::string &str);
+
 std::string dumpDeviceTypes(const DeviceTypeSet& deviceTypes);
 
 /**
diff --git a/media/libmediahelper/Android.bp b/media/libmediahelper/Android.bp
index 9b54199..a433fc6 100644
--- a/media/libmediahelper/Android.bp
+++ b/media/libmediahelper/Android.bp
@@ -20,7 +20,7 @@
     },
     apex_available: [
         "//apex_available:platform",
-        "com.android.bluetooth.updatable",
+        "com.android.bluetooth",
         "com.android.media",
         "com.android.media.swcodec",
     ],
diff --git a/media/libmediametrics/MediaMetricsItem.cpp b/media/libmediametrics/MediaMetricsItem.cpp
index a7ec975..57fc49d 100644
--- a/media/libmediametrics/MediaMetricsItem.cpp
+++ b/media/libmediametrics/MediaMetricsItem.cpp
@@ -57,18 +57,19 @@
     // This may be found in frameworks/av/media/libmediametrics/include/MediaMetricsConstants.h
     static std::unordered_map<std::string, int32_t> map{
         {"",                                      NO_ERROR},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_ARGUMENT, BAD_VALUE},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_IO,       DEAD_OBJECT},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_MEMORY,   NO_MEMORY},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_SECURITY, PERMISSION_DENIED},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_STATE,    INVALID_OPERATION},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_TIMEOUT,  WOULD_BLOCK},
-        {AMEDIAMETRICS_PROP_ERROR_VALUE_UNKNOWN,  UNKNOWN_ERROR},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_OK,       NO_ERROR},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT, BAD_VALUE},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_IO,       DEAD_OBJECT},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY,   NO_MEMORY},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY, PERMISSION_DENIED},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_STATE,    INVALID_OPERATION},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT,  WOULD_BLOCK},
+        {AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN,  UNKNOWN_ERROR},
     };
     return map;
 }
 
-status_t errorStringToStatus(const char *error) {
+status_t statusStringToStatus(const char *error) {
     const auto& map = getErrorStringMap();
     if (error == nullptr || error[0] == '\0') return NO_ERROR;
     auto it = map.find(error);
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index aeaa49c..2bf72a7 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -119,18 +119,6 @@
 #define AMEDIAMETRICS_PROP_DURATIONNS     "durationNs"     // int64 duration time span
 #define AMEDIAMETRICS_PROP_ENCODING       "encoding"       // string value of format
 
-// Error statistics
-#define AMEDIAMETRICS_PROP_ERROR          "error#"         // string, empty or one of
-                                                           // AMEDIAMETRICS_PROP_ERROR_VALUE_*
-                                                           // Used for error categorization.
-#define AMEDIAMETRICS_PROP_ERRORSUBCODE   "errorSubCode#"  // int32, specific code for error
-                                                           // used in conjunction with error#.
-#define AMEDIAMETRICS_PROP_ERRORMESSAGE   "errorMessage#"  // string, supplemental to error.
-                                                           // Arbitrary information treated as
-                                                           // informational, may be logcat msg,
-                                                           // or an exception with stack trace.
-                                                           // Treated as "debug" information.
-
 #define AMEDIAMETRICS_PROP_EVENT          "event#"         // string value (often func name)
 #define AMEDIAMETRICS_PROP_EXECUTIONTIMENS "executionTimeNs"  // time to execute the event
 
@@ -162,7 +150,17 @@
 #define AMEDIAMETRICS_PROP_STARTUPMS      "startupMs"      // double value
 // State is "ACTIVE" or "STOPPED" for AudioRecord
 #define AMEDIAMETRICS_PROP_STATE          "state"          // string
-#define AMEDIAMETRICS_PROP_STATUS         "status"         // int32 status_t
+#define AMEDIAMETRICS_PROP_STATUS         "status#"        // int32 status_t
+                                                           // AAudio uses their own status codes
+// Supplemental information to the status code.
+#define AMEDIAMETRICS_PROP_STATUSSUBCODE  "statusSubCode"  // int32, specific code
+                                                           // used in conjunction with status.
+#define AMEDIAMETRICS_PROP_STATUSMESSAGE  "statusMessage"  // string, supplemental info.
+                                                           // Arbitrary information treated as
+                                                           // informational, may be logcat msg,
+                                                           // or an exception with stack trace.
+                                                           // Treated as "debug" information.
+
 #define AMEDIAMETRICS_PROP_STREAMTYPE     "streamType"     // string (AudioTrack)
 #define AMEDIAMETRICS_PROP_THREADID       "threadId"       // int32 value io handle
 #define AMEDIAMETRICS_PROP_THROTTLEMS     "throttleMs"     // double
@@ -237,16 +235,20 @@
 // https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/Status.h;drc=88e25c0861499ee3ab885814dddc097ab234cb7b;l=57
 // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/media/java/android/media/AudioSystem.java;drc=3ac246c43294d7f7012bdcb0ccb7bae1aa695bd4;l=785
 // https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libaaudio/include/aaudio/AAudio.h;drc=cfd3a6fa3aaaf712a890dc02452b38ef401083b8;l=120
+// https://abseil.io/docs/cpp/guides/status-codes
 
-// Error category:
-// An empty error string indicates no error.
+// Status errors:
+// An empty status string or "ok" is interpreted as no error.
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_OK                "ok"
 
 // Error category: argument
 //   IllegalArgumentException
 //   NullPointerException
 //   BAD_VALUE
+//   absl::INVALID_ARGUMENT
+//   absl::OUT_OF_RANGE
 //   Out of range, out of bounds.
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_ARGUMENT           "argument"
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT          "argument"
 
 // Error category: io
 //   IOException
@@ -257,36 +259,48 @@
 //   file or ioctl failure
 //   Service, rpc, binder, or socket failure.
 //   Hardware or device failure.
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_IO                 "io"
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_IO                "io"
 
 // Error category: outOfMemory
 //   OutOfMemoryException
 //   NO_MEMORY
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_MEMORY             "memory"
+//   absl::RESOURCE_EXHAUSTED
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY            "memory"
 
 // Error category: security
 //   SecurityException
 //   PERMISSION_DENIED
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_SECURITY           "security"
+//   absl::PERMISSION_DENIED
+//   absl::UNAUTHENTICATED
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY          "security"
 
 // Error category: state
 //   IllegalStateException
 //   UnsupportedOperationException
 //   INVALID_OPERATION
 //   NO_INIT
+//   absl::NOT_FOUND
+//   absl::ALREADY_EXISTS
+//   absl::FAILED_PRECONDITION
+//   absl::UNAVAILABLE
+//   absl::UNIMPLEMENTED
 //   Functionality not implemented (argument may or may not be correct).
 //   Call unexpected or out of order.
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_STATE              "state"
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_STATE             "state"
 
 // Error category: timeout
 //   TimeoutException
 //   WOULD_BLOCK
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_TIMEOUT            "timeout"
+//   absl::DEADLINE_EXCEEDED
+//   absl::ABORTED
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT           "timeout"
 
 // Error category: unknown
 //   Exception (Java specified not listed above, or custom app/service)
 //   UNKNOWN_ERROR
+//   absl::INTERNAL
+//   absl::DATA_LOSS
 //   Catch-all bucket for errors not listed above.
-#define AMEDIAMETRICS_PROP_ERROR_VALUE_UNKNOWN            "unknown"
+#define AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN           "unknown"
 
 #endif // ANDROID_MEDIA_MEDIAMETRICSCONSTANTS_H
diff --git a/media/libmediametrics/include/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
index 87f608f..de56665 100644
--- a/media/libmediametrics/include/media/MediaMetricsItem.h
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -106,34 +106,34 @@
 };
 
 /*
- * Helper for error conversions
+ * Helper for status conversions
  */
 
-static inline constexpr const char* statusToErrorString(status_t status) {
+inline constexpr const char* statusToStatusString(status_t status) {
     switch (status) {
-    case NO_ERROR:
-        return "";
     case BAD_VALUE:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_ARGUMENT;
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT;
     case DEAD_OBJECT:
     case FAILED_TRANSACTION:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_IO;
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_IO;
     case NO_MEMORY:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_MEMORY;
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY;
     case PERMISSION_DENIED:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_SECURITY;
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY;
     case NO_INIT:
     case INVALID_OPERATION:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_STATE;
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_STATE;
     case WOULD_BLOCK:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_TIMEOUT;
-    case UNKNOWN_ERROR:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT;
     default:
-        return AMEDIAMETRICS_PROP_ERROR_VALUE_UNKNOWN;
+        if (status >= 0) return AMEDIAMETRICS_PROP_STATUS_VALUE_OK; // non-negative values "OK"
+        [[fallthrough]];            // negative values are error.
+    case UNKNOWN_ERROR:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN;
     }
 }
 
-status_t errorStringToStatus(const char *error);
+status_t statusStringToStatus(const char *error);
 
 /*
  * Time printing
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 0987a5b..7d4e168 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -40,6 +40,17 @@
           "exclude-filter": "android.media.audio.cts.AudioRecordTest"
         }
       ]
+    },
+    {
+      "name": "CtsMediaPlayerTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "android.platform.test.annotations.RequiresDevice"
+        }
+      ]
     }
   ],
   "presubmit": [
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index fb6c4e2..bb1cb0b 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -354,7 +354,7 @@
             }
 
             if (mpeg4type->eProfile != OMX_VIDEO_MPEG4ProfileCore ||
-                mpeg4type->eLevel != OMX_VIDEO_MPEG4Level2 ||
+                mpeg4type->eLevel > OMX_VIDEO_MPEG4Level2 ||
                 (mpeg4type->nAllowedPictureTypes & OMX_VIDEO_PictureTypeB) ||
                 mpeg4type->nBFrames != 0 ||
                 mpeg4type->nIDCVLCThreshold != 0 ||
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index c2114b3..5c99cc9 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -33,7 +33,7 @@
 
 #include <media/stagefright/foundation/hexdump.h>
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 #include <binder/Parcel.h>
 #endif
 
@@ -659,7 +659,7 @@
     return s;
 }
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 // static
 sp<AMessage> AMessage::FromParcel(const Parcel &parcel, size_t maxNestingLevel) {
     int32_t what = parcel.readInt32();
@@ -825,7 +825,7 @@
         }
     }
 }
-#endif  // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#endif  // defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 
 sp<AMessage> AMessage::changesFrom(const sp<const AMessage> &other, bool deep) const {
     if (other == NULL) {
diff --git a/media/libstagefright/foundation/AString.cpp b/media/libstagefright/foundation/AString.cpp
index b1ed077..a5e0ff8 100644
--- a/media/libstagefright/foundation/AString.cpp
+++ b/media/libstagefright/foundation/AString.cpp
@@ -27,7 +27,7 @@
 #include "ADebug.h"
 #include "AString.h"
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 #include <binder/Parcel.h>
 #endif
 
@@ -365,7 +365,7 @@
     return !strcasecmp(mData + mSize - suffixLen, suffix);
 }
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 // static
 AString AString::FromParcel(const Parcel &parcel) {
     size_t size = static_cast<size_t>(parcel.readInt32());
@@ -380,7 +380,7 @@
     }
     return err;
 }
-#endif // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#endif // defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 
 AString AStringPrintf(const char *format, ...) {
     va_list ap;
diff --git a/media/libstagefright/foundation/MetaData.cpp b/media/libstagefright/foundation/MetaData.cpp
index 7f48cfd..77913d5 100644
--- a/media/libstagefright/foundation/MetaData.cpp
+++ b/media/libstagefright/foundation/MetaData.cpp
@@ -28,7 +28,7 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MetaData.h>
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 #include <binder/Parcel.h>
 #endif
 
@@ -48,7 +48,7 @@
 MetaData::~MetaData() {
 }
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 /* static */
 sp<MetaData> MetaData::createFromParcel(const Parcel &parcel) {
 
diff --git a/media/libstagefright/foundation/MetaDataBase.cpp b/media/libstagefright/foundation/MetaDataBase.cpp
index 3f050ea..980eb22 100644
--- a/media/libstagefright/foundation/MetaDataBase.cpp
+++ b/media/libstagefright/foundation/MetaDataBase.cpp
@@ -28,7 +28,7 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MetaDataBase.h>
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 #include <binder/Parcel.h>
 #endif
 
@@ -452,7 +452,7 @@
     }
 }
 
-#if !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#if defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 status_t MetaDataBase::writeToParcel(Parcel &parcel) {
     status_t ret;
     size_t numItems = mInternalData->mItems.size();
@@ -532,7 +532,7 @@
     ALOGW("no metadata in parcel");
     return UNKNOWN_ERROR;
 }
-#endif // !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
+#endif // defined(__ANDROID__) && !defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
 
 }  // namespace android
 
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index 8d527e9..6c5e6cb 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -167,7 +167,7 @@
     stubs: {
         symbol_file: "libmediandk.map.txt",
         versions: ["29"],
-    },
+    }
 }
 
 cc_library {
diff --git a/media/ndk/NdkMediaDrm.cpp b/media/ndk/NdkMediaDrm.cpp
index 6e9945d..59c1103 100644
--- a/media/ndk/NdkMediaDrm.cpp
+++ b/media/ndk/NdkMediaDrm.cpp
@@ -97,6 +97,8 @@
     List<idvec_t> mIds;
     KeyedVector<String8, String8> mQueryResults;
     Vector<uint8_t> mKeyRequest;
+    String8 mDefaultUrl;
+    AMediaDrmKeyRequestType mkeyRequestType;
     Vector<uint8_t> mProvisionRequest;
     String8 mProvisionUrl;
     String8 mPropertyString;
@@ -416,6 +418,21 @@
         const AMediaDrmKeyValue *optionalParameters, size_t numOptionalParameters,
         const uint8_t **keyRequest, size_t *keyRequestSize) {
 
+    return AMediaDrm_getKeyRequestWithDefaultUrlAndType(mObj,
+        scope, init, initSize, mimeType, keyType, optionalParameters,
+        numOptionalParameters, keyRequest,
+        keyRequestSize, NULL, NULL);
+}
+
+EXPORT
+media_status_t AMediaDrm_getKeyRequestWithDefaultUrlAndType(AMediaDrm *mObj,
+        const AMediaDrmScope *scope, const uint8_t *init, size_t initSize,
+        const char *mimeType, AMediaDrmKeyType keyType,
+        const AMediaDrmKeyValue *optionalParameters,
+        size_t numOptionalParameters, const uint8_t **keyRequest,
+        size_t *keyRequestSize, const char **defaultUrl,
+        AMediaDrmKeyRequestType *keyRequestType) {
+
     if (!mObj || mObj->mDrm == NULL) {
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
@@ -449,18 +466,43 @@
         mdOptionalParameters.add(String8(optionalParameters[i].mKey),
                 String8(optionalParameters[i].mValue));
     }
-    String8 defaultUrl;
-    DrmPlugin::KeyRequestType keyRequestType;
+
+    DrmPlugin::KeyRequestType requestType;
     mObj->mKeyRequest.clear();
     status_t status = mObj->mDrm->getKeyRequest(*iter, mdInit, String8(mimeType),
-            mdKeyType, mdOptionalParameters, mObj->mKeyRequest, defaultUrl,
-            &keyRequestType);
+            mdKeyType, mdOptionalParameters, mObj->mKeyRequest, mObj->mDefaultUrl,
+            &requestType);
     if (status != OK) {
         return translateStatus(status);
     } else {
         *keyRequest = mObj->mKeyRequest.array();
         *keyRequestSize = mObj->mKeyRequest.size();
+        if (defaultUrl != NULL)
+            *defaultUrl = mObj->mDefaultUrl.string();
+        switch(requestType) {
+            case DrmPlugin::kKeyRequestType_Initial:
+                mObj->mkeyRequestType = KEY_REQUEST_TYPE_INITIAL;
+                break;
+            case DrmPlugin::kKeyRequestType_Renewal:
+                mObj->mkeyRequestType = KEY_REQUEST_TYPE_RENEWAL;
+                break;
+            case DrmPlugin::kKeyRequestType_Release:
+                mObj->mkeyRequestType = KEY_REQUEST_TYPE_RELEASE;
+                break;
+            case DrmPlugin::kKeyRequestType_None:
+                mObj->mkeyRequestType = KEY_REQUEST_TYPE_NONE;
+                break;
+            case DrmPlugin::kKeyRequestType_Update:
+                mObj->mkeyRequestType = KEY_REQUEST_TYPE_UPDATE;
+                break;
+            default:
+                return AMEDIA_ERROR_UNKNOWN;
+        }
+
+        if (keyRequestType != NULL)
+            *keyRequestType = mObj->mkeyRequestType;
     }
+
     return AMEDIA_OK;
 }
 
diff --git a/media/ndk/include/media/NdkMediaDrm.h b/media/ndk/include/media/NdkMediaDrm.h
index 849a8f9..4eca3d7 100644
--- a/media/ndk/include/media/NdkMediaDrm.h
+++ b/media/ndk/include/media/NdkMediaDrm.h
@@ -112,6 +112,41 @@
 } AMediaDrmKeyType;
 
 /**
+ * Introduced in API 33.
+ */
+typedef enum AMediaDrmKeyRequestType : int32_t {
+    /**
+     * Key request type is initial license request.
+     * An initial license request is necessary to load keys.
+     */
+    KEY_REQUEST_TYPE_INITIAL,
+
+    /**
+     * Key request type is license renewal.
+     * A renewal license request is necessary to prevent the keys from expiring.
+     */
+    KEY_REQUEST_TYPE_RENEWAL,
+
+    /**
+     * Key request type is license release.
+     * A license release request indicates that keys are removed.
+     */
+    KEY_REQUEST_TYPE_RELEASE,
+
+    /**
+     * Keys are already loaded and are available for use. No license request is necessary, and
+     * no key request data is returned.
+     */
+    KEY_REQUEST_TYPE_NONE,
+
+    /**
+     * Keys have been loaded but an additional license request is needed
+     * to update their values.
+     */
+    KEY_REQUEST_TYPE_UPDATE
+} AMediaDrmKeyRequestType;
+
+/**
  *  Data type containing {key, value} pair
  */
 typedef struct AMediaDrmKeyValuePair {
@@ -248,7 +283,10 @@
  * to obtain or release keys used to decrypt encrypted content.
  * AMediaDrm_getKeyRequest is used to obtain an opaque key request byte array that
  * is delivered to the license server.  The opaque key request byte array is
- * returned in KeyRequest.data.
+ * returned in *keyRequest and the number of bytes in the request is
+ * returned in *keyRequestSize.
+ * This API has same functionality as AMediaDrm_getKeyRequestWithDefaultUrlAndType()
+ * when defaultUrl and keyRequestType are passed in as NULL.
  *
  * After the app has received the key request response from the server,
  * it should deliver to the response to the DRM engine plugin using the method
@@ -280,11 +318,14 @@
  *   by the caller
  *
  * On exit:
+ *   If this returns AMEDIA_OK,
  *   1. The keyRequest pointer will reference the opaque key request data.  It
  *       will reside in memory owned by the AMediaDrm object, and will remain
- *       accessible until the next call to AMediaDrm_getKeyRequest or until the
+ *       accessible until the next call to AMediaDrm_getKeyRequest
+ *       or AMediaDrm_getKeyRequestWithDefaultUrlAndType or until the
  *       MediaDrm object is released.
  *   2. keyRequestSize will be set to the size of the request
+ *   If this does not return AMEDIA_OK, value of these parameters should not be used.
  *
  * Returns MEDIADRM_NOT_PROVISIONED_ERROR if reprovisioning is needed, due to a
  * problem with the device certificate.
@@ -297,6 +338,72 @@
         const uint8_t **keyRequest, size_t *keyRequestSize) __INTRODUCED_IN(21);
 
 /**
+ * A key request/response exchange occurs between the app and a license server
+ * to obtain or release keys used to decrypt encrypted content.
+ * AMediaDrm_getKeyRequest is used to obtain an opaque key request byte array that
+ * is delivered to the license server.  The opaque key request byte array is
+ * returned in *keyRequest and the number of bytes in the request is
+ * returned in *keyRequestSize.
+ *
+ * After the app has received the key request response from the server,
+ * it should deliver to the response to the DRM engine plugin using the method
+ * AMediaDrm_provideKeyResponse.
+ *
+ * scope may be a sessionId or a keySetId, depending on the specified keyType.
+ * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set
+ * to the sessionId the keys will be provided to.  When the keyType is
+ * KEY_TYPE_RELEASE, scope should be set to the keySetId of the keys being released.
+ * Releasing keys from a device invalidates them for all sessions.
+ *
+ * init container-specific data, its meaning is interpreted based on the mime type
+ * provided in the mimeType parameter.  It could contain, for example, the content
+ * ID, key ID or other data obtained from the content metadata that is required in
+ * generating the key request. init may be null when keyType is KEY_TYPE_RELEASE.
+ *
+ * initSize is the number of bytes of initData
+ *
+ * mimeType identifies the mime type of the content.
+ *
+ * keyType specifes the type of the request. The request may be to acquire keys for
+ *   streaming or offline content, or to release previously acquired keys, which are
+ *   identified by a keySetId.
+ *
+ * optionalParameters are included in the key request message to allow a client
+ *   application to provide additional message parameters to the server.
+ *
+ * numOptionalParameters indicates the number of optional parameters provided
+ *   by the caller
+ *
+ * On exit:
+ *   If this returns AMEDIA_OK,
+ *   1. The keyRequest pointer will reference the opaque key request data.  It
+ *       will reside in memory owned by the AMediaDrm object, and will remain
+ *       accessible until the next call to either AMediaDrm_getKeyRequest
+ *       or AMediaDrm_getKeyRequestWithDefaultUrlAndType or until the
+ *       MediaDrm object is released.
+ *   2. keyRequestSize will be set to the size of the request.
+ *   3. defaultUrl will be set to the recommended URL to deliver the key request.
+ *      The defaultUrl pointer will reference a NULL terminated URL string.
+ *      It will be UTF-8 encoded and have same lifetime with the key request data
+ *      KeyRequest pointer references to. Passing in NULL means you don't need it
+ *      to be reported.
+ *   4. keyRequestType will be set to the key request type. Passing in NULL means
+*       you don't need it to be reported.
+ *
+ * Returns MEDIADRM_NOT_PROVISIONED_ERROR if reprovisioning is needed, due to a
+ * problem with the device certificate.
+ *
+ * Available since API level 33.
+ */
+media_status_t AMediaDrm_getKeyRequestWithDefaultUrlAndType(AMediaDrm *,
+        const AMediaDrmScope *scope, const uint8_t *init, size_t initSize,
+        const char *mimeType, AMediaDrmKeyType keyType,
+        const AMediaDrmKeyValue *optionalParameters,
+        size_t numOptionalParameters, const uint8_t **keyRequest,
+        size_t *keyRequestSize, const char **defaultUrl,
+        AMediaDrmKeyRequestType *keyRequestType) __INTRODUCED_IN(__ANDROID_API_T__);
+
+/**
  * A key response is received from the license server by the app, then it is
  * provided to the DRM engine plugin using provideKeyResponse.  When the
  * response is for an offline key request, a keySetId is returned that can be
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 6f275c7..b228945 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -229,6 +229,7 @@
     AMediaDrm_decrypt;
     AMediaDrm_encrypt;
     AMediaDrm_getKeyRequest;
+    AMediaDrm_getKeyRequestWithDefaultUrlAndType; # introduced=Tiramisu
     AMediaDrm_getPropertyByteArray;
     AMediaDrm_getPropertyString;
     AMediaDrm_getProvisionRequest;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 93118b8..45dd258 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -575,6 +575,12 @@
 
     // create a special playback track to render to playback thread.
     // this track is given the same buffer as the PatchRecord buffer
+
+    // Default behaviour is to start as soon as possible to have the lowest possible latency even if
+    // it might glitch.
+    // Disable this behavior for FM Tuner source if no fast capture/mixer available.
+    const bool isFmBridge = mAudioPatch.sources[0].ext.device.type == AUDIO_DEVICE_IN_FM_TUNER;
+    const size_t frameCountToBeReady = isFmBridge && !usePassthruPatchRecord ? frameCount / 4 : 1;
     sp<PlaybackThread::PatchTrack> tempPatchTrack = new PlaybackThread::PatchTrack(
                                            mPlayback.thread().get(),
                                            streamType,
@@ -584,7 +590,9 @@
                                            frameCount,
                                            tempRecordTrack->buffer(),
                                            tempRecordTrack->bufferSize(),
-                                           outputFlags);
+                                           outputFlags,
+                                           {} /*timeout*/,
+                                           frameCountToBeReady);
     status = mPlayback.checkTrack(tempPatchTrack.get());
     if (status != NO_ERROR) {
         return status;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 3cce998..aecd4d3 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -193,6 +193,12 @@
        }
     }
 
+    static bool checkServerLatencySupported(
+            audio_format_t format, audio_output_flags_t flags) {
+        return audio_is_linear_pcm(format)
+                && (flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) == 0;
+    }
+
     audio_output_flags_t getOutputFlags() const { return mFlags; }
     float getSpeed() const { return mSpeed; }
 
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 616fd78..233865f 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -713,8 +713,7 @@
         thread->mFastTrackAvailMask &= ~(1 << i);
     }
 
-    mServerLatencySupported = thread->type() == ThreadBase::MIXER
-            || thread->type() == ThreadBase::DUPLICATING;
+    mServerLatencySupported = checkServerLatencySupported(format, flags);
 #ifdef TEE_SINK
     mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
             + "_" + std::to_string(mId) + "_T");
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index b0c376a..9a61a05 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -299,8 +299,13 @@
     if (device != nullptr) {
         return DeviceVector(device);
     }
+    return fromCache? getCachedDevices(strategy) : getDevicesForProductStrategy(strategy);
+}
 
-    return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy);
+DeviceVector Engine::getCachedDevices(product_strategy_t ps) const
+{
+    return mDevicesForStrategies.find(ps) != mDevicesForStrategies.end() ?
+                mDevicesForStrategies.at(ps) : DeviceVector{};
 }
 
 DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h
index d8e2742..f665da5 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.h
+++ b/services/audiopolicy/engineconfigurable/src/Engine.h
@@ -126,6 +126,7 @@
     status_t loadAudioPolicyEngineConfig();
 
     DeviceVector getDevicesForProductStrategy(product_strategy_t strategy) const;
+    DeviceVector getCachedDevices(product_strategy_t ps) const;
 
     /**
      * Policy Parameter Manager hidden through a wrapper.
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 00c1f26..05eae98 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1702,6 +1702,8 @@
     // The priority is as follows:
     // 1: the output supporting haptic playback when requesting haptic playback
     // 2: the output with the highest number of requested functional flags
+    //    with tiebreak preferring the minimum number of extra functional flags
+    //    (see b/200293124, the incorrect selection of AUDIO_OUTPUT_FLAG_VOIP_RX).
     // 3: the output supporting the exact channel mask
     // 4: the output with a higher channel count than requested
     // 5: the output with a higher sampling rate than requested
@@ -1743,7 +1745,12 @@
         }
 
         // functional flags match
-        currentMatchCriteria[1] = popcount(outputDesc->mFlags & functionalFlags);
+        const int matchingFunctionalFlags =
+                __builtin_popcount(outputDesc->mFlags & functionalFlags);
+        const int totalFunctionalFlags =
+                __builtin_popcount(outputDesc->mFlags & kFunctionalFlags);
+        // Prefer matching functional flags, but subtract unnecessary functional flags.
+        currentMatchCriteria[1] = 100 * (matchingFunctionalFlags + 1) - totalFunctionalFlags;
 
         // channel mask and channel count match
         uint32_t outputChannelCount = audio_channel_count_from_out_mask(
@@ -6103,7 +6110,8 @@
             return hasVoiceStream(streams) && (outputDesc == mPrimaryOutput ||
                 outputDesc->isActive(toVolumeSource(AUDIO_STREAM_VOICE_CALL))) &&
                 (isInCall() ||
-                 mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc));
+                 mOutputs.isStrategyActiveOnSameModule(productStrategy, outputDesc)) &&
+                !isStreamActive(AUDIO_STREAM_ENFORCED_AUDIBLE, 0);
         };
 
         // With low-latency playing on speaker, music on WFD, when the first low-latency
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 5a18582..b3db66b 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -4453,12 +4453,20 @@
 
     if (dumpVector.empty()) { return; }
 
+    std::string dumpString;
+
+    String8 currentTime = getFormattedCurrentTime();
+    dumpString += "Cached @ ";
+    dumpString += currentTime.string();
+    dumpString += "\n"; // First line is the timestamp of when client is cached.
+
+
     const String16 &packageName = client->getPackageName();
 
     String8 packageName8 = String8(packageName);
     const char *printablePackageName = packageName8.lockBuffer(packageName.size());
 
-    std::string dumpString;
+
     size_t i = dumpVector.size();
 
     // Store the string in reverse order (latest last)
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 7d13941..b03ca62 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -552,8 +552,10 @@
                     mName(name), mId(id), mVersion(version), mProviderTagid(tagId),
                     mIsLogicalCamera(false), mResourceCost(resourceCost),
                     mStatus(hardware::camera::common::V1_0::CameraDeviceStatus::PRESENT),
-                    mParentProvider(parentProvider), mHasFlashUnit(false),
-                    mSupportNativeZoomRatio(false), mPublicCameraIds(publicCameraIds) {}
+                    mParentProvider(parentProvider), mTorchStrengthLevel(0),
+                    mTorchMaximumStrengthLevel(0), mTorchDefaultStrengthLevel(0),
+                    mHasFlashUnit(false), mSupportNativeZoomRatio(false),
+                    mPublicCameraIds(publicCameraIds) {}
             virtual ~DeviceInfo();
         protected:
             bool mHasFlashUnit; // const after constructor
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 74e4715..c98d5fc 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -185,6 +185,10 @@
         "libplatformprotos",
     ],
 
+    header_libs: [
+        "libaaudio_headers",
+    ],
+
     include_dirs: [
         "system/media/audio_utils/include",
     ],
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 270fe2f..46c701c 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -21,6 +21,7 @@
 
 #include "AudioAnalytics.h"
 
+#include <aaudio/AAudio.h>        // error codes
 #include <audio_utils/clock.h>    // clock conversions
 #include <cutils/properties.h>
 #include <statslog.h>             // statsd
@@ -64,6 +65,50 @@
     }
 }
 
+// The status variable contains status_t codes which are used by
+// the core audio framework. We also consider AAudio status codes.
+//
+// Compare with mediametrics::statusToStatusString
+//
+inline constexpr const char* extendedStatusToStatusString(status_t status) {
+    switch (status) {
+    case BAD_VALUE:           // status_t
+    case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
+    case AAUDIO_ERROR_INVALID_FORMAT:
+    case AAUDIO_ERROR_INVALID_RATE:
+    case AAUDIO_ERROR_NULL:
+    case AAUDIO_ERROR_OUT_OF_RANGE:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT;
+    case DEAD_OBJECT:         // status_t
+    case FAILED_TRANSACTION:  // status_t
+    case AAUDIO_ERROR_DISCONNECTED:
+    case AAUDIO_ERROR_INVALID_HANDLE:
+    case AAUDIO_ERROR_NO_SERVICE:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_IO;
+    case NO_MEMORY:           // status_t
+    case AAUDIO_ERROR_NO_FREE_HANDLES:
+    case AAUDIO_ERROR_NO_MEMORY:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_MEMORY;
+    case PERMISSION_DENIED:   // status_t
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_SECURITY;
+    case INVALID_OPERATION:   // status_t
+    case NO_INIT:             // status_t
+    case AAUDIO_ERROR_INVALID_STATE:
+    case AAUDIO_ERROR_UNAVAILABLE:
+    case AAUDIO_ERROR_UNIMPLEMENTED:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_STATE;
+    case WOULD_BLOCK:         // status_t
+    case AAUDIO_ERROR_TIMEOUT:
+    case AAUDIO_ERROR_WOULD_BLOCK:
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_TIMEOUT;
+    default:
+        if (status >= 0) return AMEDIAMETRICS_PROP_STATUS_VALUE_OK; // non-negative values "OK"
+        [[fallthrough]];            // negative values are error.
+    case UNKNOWN_ERROR:       // status_t
+        return AMEDIAMETRICS_PROP_STATUS_VALUE_UNKNOWN;
+    }
+}
+
 static constexpr const auto LOG_LEVEL = android::base::VERBOSE;
 
 static constexpr int PREVIOUS_STATE_EXPIRE_SEC = 60 * 60; // 1 hour.
@@ -392,11 +437,15 @@
 {
     if (!startsWith(item->getKey(), AMEDIAMETRICS_KEY_PREFIX_AUDIO)) return BAD_VALUE;
     status_t status = mAnalyticsState->submit(item, isTrusted);
+
+    // Status is selectively authenticated.
+    processStatus(item);
+
     if (status != NO_ERROR) return status;  // may not be permitted.
 
     // Only if the item was successfully submitted (permission)
     // do we check triggered actions.
-    checkActions(item);
+    processActions(item);
     return NO_ERROR;
 }
 
@@ -430,7 +479,7 @@
     return { ss.str(), lines - ll };
 }
 
-void AudioAnalytics::checkActions(const std::shared_ptr<const mediametrics::Item>& item)
+void AudioAnalytics::processActions(const std::shared_ptr<const mediametrics::Item>& item)
 {
     auto actions = mActions.getActionsForItem(item); // internally locked.
     // Execute actions with no lock held.
@@ -439,6 +488,36 @@
     }
 }
 
+void AudioAnalytics::processStatus(const std::shared_ptr<const mediametrics::Item>& item)
+{
+    int32_t status;
+    if (!item->get(AMEDIAMETRICS_PROP_STATUS, &status)) return;
+
+    // Any record with a status will automatically be added to a heat map.
+    // Standard information.
+    const auto key = item->getKey();
+    const auto uid = item->getUid();
+
+    // from audio.track.10 ->  prefix = audio.track, suffix = 10
+    // from audio.track.error -> prefix = audio.track, suffix = error
+    const auto [prefixKey, suffixKey] = stringutils::splitPrefixKey(key);
+
+    std::string message;
+    item->get(AMEDIAMETRICS_PROP_STATUSMESSAGE, &message); // optional
+
+    int32_t subCode = 0; // not used
+    (void)item->get(AMEDIAMETRICS_PROP_STATUSSUBCODE, &subCode); // optional
+
+    std::string eventStr; // optional
+    item->get(AMEDIAMETRICS_PROP_EVENT, &eventStr);
+
+    const std::string statusString = extendedStatusToStatusString(status);
+
+    // Add to the heat map - we automatically track every item's status to see
+    // the types of errors and the frequency of errors.
+    mHeatMap.add(prefixKey, suffixKey, eventStr, statusString, uid, message, subCode);
+}
+
 // HELPER METHODS
 
 std::string AudioAnalytics::getThreadFromTrack(const std::string& track) const
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index 2b41a95..9b54cf3 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -20,6 +20,7 @@
 #include "AnalyticsActions.h"
 #include "AnalyticsState.h"
 #include "AudioPowerUsage.h"
+#include "HeatMap.h"
 #include "StatsdLog.h"
 #include "TimedAction.h"
 #include "Wrap.h"
@@ -73,11 +74,23 @@
     std::pair<std::string, int32_t> dump(
             int32_t lines = INT32_MAX, int64_t sinceNs = 0, const char *prefix = nullptr) const;
 
+    /**
+     * Returns a pair consisting of the dump string and the number of lines in the string.
+     *
+     * HeatMap dump.
+     */
+    std::pair<std::string, int32_t> dumpHeatMap(int32_t lines = INT32_MAX) const {
+        return mHeatMap.dump(lines);
+    }
+
     void clear() {
         // underlying state is locked.
         mPreviousAnalyticsState->clear();
         mAnalyticsState->clear();
 
+        // Clears the status map
+        mHeatMap.clear();
+
         // Clear power usage state.
         mAudioPowerUsage.clear();
     }
@@ -96,11 +109,18 @@
      */
 
     /**
-     * Checks for any pending actions for a particular item.
+     * Processes any pending actions for a particular item.
      *
      * \param item to check against the current AnalyticsActions.
      */
-    void checkActions(const std::shared_ptr<const mediametrics::Item>& item);
+    void processActions(const std::shared_ptr<const mediametrics::Item>& item);
+
+    /**
+     * Processes status information contained in the item.
+     *
+     * \param item to check against for status handling
+     */
+    void processStatus(const std::shared_ptr<const mediametrics::Item>& item);
 
     // HELPER METHODS
     /**
@@ -124,6 +144,9 @@
     TimedAction mTimedAction; // locked internally
     const std::shared_ptr<StatsdLog> mStatsdLog; // locked internally, ok for multiple threads.
 
+    static constexpr size_t kHeatEntries = 100;
+    HeatMap mHeatMap{kHeatEntries}; // locked internally, ok for multiple threads.
+
     // DeviceUse is a nested class which handles audio device usage accounting.
     // We define this class at the end to ensure prior variables all properly constructed.
     // TODO: Track / Thread interaction
diff --git a/services/mediametrics/AudioTypes.cpp b/services/mediametrics/AudioTypes.cpp
index 838cdd5..b67967b 100644
--- a/services/mediametrics/AudioTypes.cpp
+++ b/services/mediametrics/AudioTypes.cpp
@@ -15,8 +15,10 @@
  */
 
 #include "AudioTypes.h"
+#include "MediaMetricsConstants.h"
 #include "StringUtils.h"
 #include <media/TypeConverter.h> // requires libmedia_helper to get the Audio code.
+#include <statslog.h>            // statsd
 
 namespace android::mediametrics::types {
 
diff --git a/services/mediametrics/HeatMap.h b/services/mediametrics/HeatMap.h
new file mode 100644
index 0000000..950501a
--- /dev/null
+++ b/services/mediametrics/HeatMap.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <iomanip>
+#include <map>
+#include <sstream>
+#include "MediaMetricsConstants.h"
+
+namespace android::mediametrics {
+
+/**
+ * HeatData accumulates statistics on the status reported for a given key.
+ *
+ * HeatData is a helper class used by HeatMap to represent statistics.  We expose it
+ * here for testing purposes currently.
+ *
+ * Note: This class is not thread safe, so mutual exclusion should be obtained by the caller
+ * which in this case is HeatMap.  HeatMap getData() returns a local copy of HeatData, so use
+ * of that is thread-safe.
+ */
+class HeatData {
+    /* HeatData for a key is stored in a map based on the event (e.g. "start", "pause", create)
+     * and then another map based on the status (e.g. "ok", "argument", "state").
+     */
+    std::map<std::string /* event */,
+             std::map<std::string /* status name */, size_t /* count, nonzero */>> mMap;
+
+public:
+    /**
+     * Add status data.
+     *
+     * \param suffix  (ignored) the suffix to the key that was stripped, if any.
+     * \param event             the event (e.g. create, start, pause, stop, etc.).
+     * \param uid     (ignored) the uid associated with the error.
+     * \param message (ignored) the status message, if any.
+     * \param subCode (ignored) the status subcode, if any.
+     */
+    void add(const std::string& suffix, const std::string& event, const std::string& status,
+            uid_t uid, const std::string& message, int32_t subCode) {
+        // Perhaps there could be a more detailed print.
+        (void)suffix;
+        (void)uid;
+        (void)message;
+        (void)subCode;
+        ++mMap[event][status];
+    }
+
+    /** Returns the number of event names with status. */
+    size_t size() const {
+        return mMap.size();
+    }
+
+    /**
+     * Returns a deque with pairs indicating the count of Oks and Errors.
+     * The first pair is total, the other pairs are in order of mMap.
+     *
+     * Example return value of {ok, error} pairs:
+     *     total     key1      key2
+     * { { 2, 1 }, { 1, 0 }, { 1, 1 } }
+     */
+    std::deque<std::pair<size_t /* oks */, size_t /* errors */>> heatCount() const {
+        size_t totalOk = 0;
+        size_t totalError = 0;
+        std::deque<std::pair<size_t /* oks */, size_t /* errors */>> heat;
+        for (const auto &eventPair : mMap) {
+            size_t ok = 0;
+            size_t error = 0;
+            for (const auto &[name, count] : eventPair.second) {
+                if (name == AMEDIAMETRICS_PROP_STATUS_VALUE_OK) {
+                    ok += count;
+                } else {
+                    error += count;
+                }
+            }
+            totalOk += ok;
+            totalError += error;
+            heat.emplace_back(ok, error);
+        }
+        heat.emplace_front(totalOk, totalError);
+        return heat;
+    }
+
+    /** Returns the error fraction from a pair <oks, errors>, a float between 0.f to 1.f. */
+    static float fraction(const std::pair<size_t, size_t>& count) {
+        return (float)count.second / (count.first + count.second);
+    }
+
+    /** Returns the HeatMap information in a single line string. */
+    std::string dump() const {
+        const auto heat = heatCount();
+        auto it = heat.begin();
+        std::stringstream ss;
+        ss << "{ ";
+        float errorFraction = fraction(*it++);
+        if (errorFraction > 0.f) {
+            ss << std::fixed << std::setprecision(2) << errorFraction << " ";
+        }
+        for (const auto &eventPair : mMap) {
+            ss << eventPair.first << ": { ";
+            errorFraction = fraction(*it++);
+            if (errorFraction > 0.f) {
+                ss << std::fixed << std::setprecision(2) << errorFraction << " ";
+            }
+            for (const auto &[name, count]: eventPair.second) {
+                ss << "[ " << name << " : " << count << " ] ";
+            }
+            ss << "} ";
+        }
+        ss << " }";
+        return ss.str();
+    }
+};
+
+/**
+ * HeatMap is a thread-safe collection that counts activity of status errors per key.
+ *
+ * The classic heat map is a 2D picture with intensity shown by color.
+ * Here we accumulate the status results from keys to see if there are consistent
+ * failures in the system.
+ *
+ * TODO(b/210855555): Heatmap improvements.
+ *   1) heat decays in intensity in time for past events, currently we don't decay.
+ */
+
+class HeatMap {
+    const size_t mMaxSize;
+    mutable std::mutex mLock;
+    size_t mRejected GUARDED_BY(mLock) = 0;
+    std::map<std::string, HeatData> mMap GUARDED_BY(mLock);
+
+public:
+    /**
+     * Constructs a HeatMap.
+     *
+     * \param maxSize the maximum number of elements that are tracked.
+     */
+    explicit HeatMap(size_t maxSize) : mMaxSize(maxSize) {
+    }
+
+    /** Returns the number of keys. */
+    size_t size() const {
+        std::lock_guard l(mLock);
+        return mMap.size();
+    }
+
+    /** Clears error history. */
+    void clear() {
+        std::lock_guard l(mLock);
+        return mMap.clear();
+    }
+
+    /** Returns number of keys rejected due to space. */
+    size_t rejected() const {
+        std::lock_guard l(mLock);
+        return mRejected;
+    }
+
+    /** Returns a copy of the heat data associated with key. */
+    HeatData getData(const std::string& key) const {
+        std::lock_guard l(mLock);
+        return mMap.count(key) == 0 ? HeatData{} : mMap.at(key);
+    }
+
+    /**
+     * Adds a new entry.
+     * \param key               the key category (e.g. audio.track).
+     * \param suffix  (ignored) the suffix to the key that was stripped, if any.
+     * \param event             the event (e.g. create, start, pause, stop, etc.).
+     * \param uid     (ignored) the uid associated with the error.
+     * \param message (ignored) the status message, if any.
+     * \param subCode (ignored) the status subcode, if any.
+     */
+    void add(const std::string& key, const std::string& suffix, const std::string& event,
+            const std::string& status, uid_t uid, const std::string& message, int32_t subCode) {
+        std::lock_guard l(mLock);
+
+        // Hard limit on heat map entries.
+        // TODO: have better GC.
+        if (mMap.size() == mMaxSize && mMap.count(key) == 0) {
+            ++mRejected;
+            return;
+        }
+        mMap[key].add(suffix, event, status, uid, message, subCode);
+    }
+
+    /**
+     * Returns a pair consisting of the dump string and the number of lines in the string.
+     */
+    std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const {
+        std::stringstream ss;
+        int32_t ll = lines;
+        std::lock_guard l(mLock);
+        if (ll > 0) {
+            ss << "Error Heat Map (rejected: " << mRejected << "):\n";
+            --ll;
+        }
+        // TODO: restriction is implemented alphabetically not on priority.
+        for (const auto& [name, data] : mMap) {
+            if (ll <= 0) break;
+            ss << name << ": " << data.dump() << "\n";
+            --ll;
+        }
+        return { ss.str(), lines - ll };
+    }
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 35e0ae4..636b343 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -319,11 +319,19 @@
                 result << "-- some lines may be truncated --\n";
             }
 
-            result << "LogSessionId:\n"
+            const int32_t heatLinesToDump = all ? INT32_MAX : 20;
+            const auto [ heatDumpString, heatLines] =
+                    mAudioAnalytics.dumpHeatMap(heatLinesToDump);
+            result << "\n" << heatDumpString;
+            if (heatLines == heatLinesToDump) {
+                result << "-- some lines may be truncated --\n";
+            }
+
+            result << "\nLogSessionId:\n"
                    << mediametrics::ValidateId::get()->dump();
 
             // Dump the statsd atoms we sent out.
-            result << "Statsd atoms:\n"
+            result << "\nStatsd atoms:\n"
                    << mStatsdLog->dumpToString("  " /* prefix */,
                            all ? STATSD_LOG_LINES_MAX : STATSD_LOG_LINES_DUMP);
         }
diff --git a/services/mediametrics/StringUtils.h b/services/mediametrics/StringUtils.h
index 01034d9..a56f5b8 100644
--- a/services/mediametrics/StringUtils.h
+++ b/services/mediametrics/StringUtils.h
@@ -167,4 +167,41 @@
     return ss.str();
 }
 
+/**
+ * Returns true if the string is non-null, not empty, and contains only digits.
+ */
+inline constexpr bool isNumeric(const char *s)
+{
+    if (s == nullptr || *s == 0) return false;
+    do {
+        if (!isdigit(*s)) return false;
+    } while (*++s != 0);
+    return true;  // all digits
+}
+
+/**
+ * Extracts out the prefix from the key, returning a pair of prefix, suffix.
+ *
+ * Usually the key is something like:
+ * Prefix.(ID)
+ *   where ID is an integer,
+ *               or "error" if the id was not returned because of failure,
+ *               or "status" if general status.
+ *
+ * Example: audio.track.10     -> prefix = audio.track, suffix = 10
+ *          audio.track.error  -> prefix = audio.track, suffix = error
+ *          audio.track.status -> prefix = audio.track, suffix = status
+ *          audio.mute         -> prefix = audio.mute,  suffix = ""
+ */
+inline std::pair<std::string /* prefix */,
+                 std::string /* suffix */> splitPrefixKey(const std::string &key)
+{
+    const size_t split = key.rfind('.');
+    const char* suffix = key.c_str() + split + 1;
+    if (*suffix && (!strcmp(suffix, "error") || !strcmp(suffix, "status") || isNumeric(suffix))) {
+        return { key.substr(0, split), suffix };
+    }
+    return { key, "" };
+}
+
 } // namespace android::mediametrics::stringutils
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index cd6af9f..102700a 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -1226,8 +1226,8 @@
     }
 }
 
-TEST(mediametrics_tests, ErrorConversion) {
-    constexpr status_t errors[] = {
+TEST(mediametrics_tests, StatusConversion) {
+    constexpr status_t statuses[] = {
         NO_ERROR,
         BAD_VALUE,
         DEAD_OBJECT,
@@ -1239,15 +1239,58 @@
     };
 
     auto roundTrip = [](status_t status) {
-        return android::mediametrics::errorStringToStatus(
-                android::mediametrics::statusToErrorString(status));
+        return android::mediametrics::statusStringToStatus(
+                android::mediametrics::statusToStatusString(status));
     };
 
     // Primary status error categories.
-    for (const auto error : errors) {
-        ASSERT_EQ(error, roundTrip(error));
+    for (const auto status : statuses) {
+        ASSERT_EQ(status, roundTrip(status));
     }
 
     // Status errors specially considered.
     ASSERT_EQ(DEAD_OBJECT, roundTrip(FAILED_TRANSACTION));
 }
+
+TEST(mediametrics_tests, HeatMap) {
+    constexpr size_t SIZE = 2;
+    android::mediametrics::HeatMap heatMap{SIZE};
+    constexpr uid_t UID = 0;
+    constexpr int32_t SUBCODE = 1;
+
+    ASSERT_EQ((size_t)0, heatMap.size());
+    heatMap.add("someKey", "someSuffix", "someEvent",
+            AMEDIAMETRICS_PROP_STATUS_VALUE_OK, UID, "message", SUBCODE);
+    ASSERT_EQ((size_t)1, heatMap.size());
+    heatMap.add("someKey", "someSuffix", "someEvent",
+            AMEDIAMETRICS_PROP_STATUS_VALUE_OK, UID, "message", SUBCODE);
+    heatMap.add("someKey", "someSuffix", "anotherEvent",
+            AMEDIAMETRICS_PROP_STATUS_VALUE_ARGUMENT, UID, "message", SUBCODE);
+    ASSERT_EQ((size_t)1, heatMap.size());
+    heatMap.add("anotherKey", "someSuffix", "someEvent",
+            AMEDIAMETRICS_PROP_STATUS_VALUE_OK, UID, "message", SUBCODE);
+    ASSERT_EQ((size_t)2, heatMap.size());
+    ASSERT_EQ((size_t)0, heatMap.rejected());
+
+    heatMap.add("thirdKey", "someSuffix", "someEvent",
+            AMEDIAMETRICS_PROP_STATUS_VALUE_OK, UID, "message", SUBCODE);
+    ASSERT_EQ((size_t)2, heatMap.size());
+    ASSERT_EQ((size_t)1, heatMap.rejected());
+
+    android::mediametrics::HeatData heatData = heatMap.getData("someKey");
+    ASSERT_EQ((size_t)2, heatData.size());
+    auto count = heatData.heatCount();
+    ASSERT_EQ((size_t)3, count.size()); // pairs in order { total, "anotherEvent", "someEvent" }
+    // check total value
+    ASSERT_EQ((size_t)2, count[0].first);  // OK
+    ASSERT_EQ((size_t)1, count[0].second); // ERROR;
+    // first key "anotherEvent"
+    ASSERT_EQ((size_t)0, count[1].first);  // OK
+    ASSERT_EQ((size_t)1, count[1].second); // ERROR;
+    // second key "someEvent"
+    ASSERT_EQ((size_t)2, count[2].first);  // OK
+    ASSERT_EQ((size_t)0, count[2].second); // ERROR;
+
+    heatMap.clear();
+    ASSERT_EQ((size_t)0, heatMap.size());
+}
diff --git a/services/oboeservice/AAudioCommandQueue.cpp b/services/oboeservice/AAudioCommandQueue.cpp
index ddaabe8..9bd18b3 100644
--- a/services/oboeservice/AAudioCommandQueue.cpp
+++ b/services/oboeservice/AAudioCommandQueue.cpp
@@ -28,6 +28,10 @@
 aaudio_result_t AAudioCommandQueue::sendCommand(std::shared_ptr<AAudioCommand> command) {
     {
         std::scoped_lock<std::mutex> _l(mLock);
+        if (!mRunning) {
+            ALOGE("Tried to send command while it was not running");
+            return AAUDIO_ERROR_INVALID_STATE;
+        }
         mCommands.push(command);
         mWaitWorkCond.notify_one();
     }
@@ -68,7 +72,7 @@
                 return !mRunning || !mCommands.empty();
             });
         }
-        if (!mCommands.empty()) {
+        if (!mCommands.empty() && mRunning) {
             command = mCommands.front();
             mCommands.pop();
         }
@@ -76,9 +80,27 @@
     return command;
 }
 
+void AAudioCommandQueue::startWaiting() {
+    std::scoped_lock<std::mutex> _l(mLock);
+    mRunning = true;
+}
+
 void AAudioCommandQueue::stopWaiting() {
     std::scoped_lock<std::mutex> _l(mLock);
     mRunning = false;
+    // Clear all commands in the queue as the command thread is stopped.
+    while (!mCommands.empty()) {
+        auto command = mCommands.front();
+        mCommands.pop();
+        std::scoped_lock<std::mutex> _cl(command->lock);
+        // If the command is waiting for result, returns AAUDIO_ERROR_INVALID_STATE
+        // as there is no thread waiting for the command.
+        if (command->isWaitingForReply) {
+            command->result = AAUDIO_ERROR_INVALID_STATE;
+            command->isWaitingForReply = false;
+            command->conditionVariable.notify_one();
+        }
+    }
     mWaitWorkCond.notify_one();
 }
 
diff --git a/services/oboeservice/AAudioCommandQueue.h b/services/oboeservice/AAudioCommandQueue.h
index 5f25507..64442a3 100644
--- a/services/oboeservice/AAudioCommandQueue.h
+++ b/services/oboeservice/AAudioCommandQueue.h
@@ -78,6 +78,12 @@
     std::shared_ptr<AAudioCommand> waitForCommand(int64_t timeoutNanos = -1);
 
     /**
+     * Start waiting for commands. Commands can only be pushed into the command queue after it
+     * starts waiting.
+     */
+    void startWaiting();
+
+    /**
      * Force stop waiting for next command
      */
     void stopWaiting();
@@ -87,7 +93,7 @@
     std::condition_variable mWaitWorkCond;
 
     std::queue<std::shared_ptr<AAudioCommand>> mCommands GUARDED_BY(mLock);
-    bool mRunning GUARDED_BY(mLock) = true;
+    bool mRunning GUARDED_BY(mLock) = false;
 };
 
 } // namespace aaudio
\ No newline at end of file
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index b9c1260..046b84b 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -242,6 +242,14 @@
     setFormat(config.format);
     setSampleRate(config.sample_rate);
 
+    // If the position is not updated while the timestamp is updated for more than a certain amount,
+    // the timestamp reported from the HAL may not be accurate. Here, a timestamp grace period is
+    // set as 5 burst size. We may want to update this value if there is any report from OEMs saying
+    // that is too short.
+    static constexpr int kTimestampGraceBurstCount = 5;
+    mTimestampGracePeriodMs = ((int64_t) kTimestampGraceBurstCount * mFramesPerBurst
+            * AAUDIO_MILLIS_PER_SECOND) / getSampleRate();
+
     ALOGD("%s() actual rate = %d, channels = %d channelMask = %#x, deviceId = %d, capacity = %d\n",
           __func__, getSampleRate(), getSamplesPerFrame(), getChannelMask(),
           deviceId, getBufferCapacity());
@@ -418,14 +426,79 @@
 aaudio_result_t AAudioServiceEndpointMMAP::getExternalPosition(uint64_t *positionFrames,
                                                                int64_t *timeNanos)
 {
-    if (!mExternalPositionSupported) {
-        return AAUDIO_ERROR_INVALID_STATE;
+    if (mHalExternalPositionStatus != AAUDIO_OK) {
+        return mHalExternalPositionStatus;
     }
-    status_t status = mMmapStream->getExternalPosition(positionFrames, timeNanos);
-    if (status == INVALID_OPERATION) {
-        // getExternalPosition is not supported. Set mExternalPositionSupported as false
+    uint64_t tempPositionFrames;
+    int64_t tempTimeNanos;
+    status_t status = mMmapStream->getExternalPosition(&tempPositionFrames, &tempTimeNanos);
+    if (status != OK) {
+        // getExternalPosition reports error. The HAL may not support the API. Cache the result
         // so that the call will not go to the HAL next time.
-        mExternalPositionSupported = false;
+        mHalExternalPositionStatus = AAudioConvert_androidToAAudioResult(status);
+        return mHalExternalPositionStatus;
     }
-    return AAudioConvert_androidToAAudioResult(status);
+
+    // If the HAL keeps reporting the same position or timestamp, the HAL may be having some issues
+    // to report correct external position. In that case, we will not trust the values reported from
+    // the HAL. Ideally, we may want to stop querying external position if the HAL cannot report
+    // correct position within a period. But it may not be a good idea to get system time too often.
+    // In that case, a maximum number of frozen external position is defined so that if the
+    // count of the same timestamp or position is reported by the HAL continuously, the values from
+    // the HAL will no longer be trusted.
+    static constexpr int kMaxFrozenCount = 20;
+    // If the HAL version is less than 7.0, the getPresentationPosition is an optional API.
+    // If the HAL version is 7.0 or later, the getPresentationPosition is a mandatory API.
+    // In that case, even the returned status is NO_ERROR, it doesn't indicate the returned
+    // position is a valid one. Do a simple validation, which is checking if the position is
+    // forward within half a second or not, here so that this function can return error if
+    // the validation fails. Note that we don't only apply this validation logic to HAL API
+    // less than 7.0. The reason is that there is a chance the HAL is not reporting the
+    // timestamp and position correctly.
+    if (mLastPositionFrames > tempPositionFrames) {
+        // If the position is going backwards, there must be something wrong with the HAL.
+        // In that case, we do not trust the values reported by the HAL.
+        ALOGW("%s position is going backwards, last position(%jd) current position(%jd)",
+              __func__, mLastPositionFrames, tempPositionFrames);
+        mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+        return mHalExternalPositionStatus;
+    } else if (mLastPositionFrames == tempPositionFrames) {
+        if (tempTimeNanos - mTimestampNanosForLastPosition >
+                AAUDIO_NANOS_PER_MILLISECOND * mTimestampGracePeriodMs) {
+            ALOGW("%s, the reported position is not changed within %d msec. "
+                  "Set the external position as not supported", __func__, mTimestampGracePeriodMs);
+            mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+            return mHalExternalPositionStatus;
+        }
+        mFrozenPositionCount++;
+    } else {
+        mFrozenPositionCount = 0;
+    }
+
+    if (mTimestampNanosForLastPosition > tempTimeNanos) {
+        // If the timestamp is going backwards, there must be something wrong with the HAL.
+        // In that case, we do not trust the values reported by the HAL.
+        ALOGW("%s timestamp is going backwards, last timestamp(%jd), current timestamp(%jd)",
+              __func__, mTimestampNanosForLastPosition, tempTimeNanos);
+        mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+        return mHalExternalPositionStatus;
+    } else if (mTimestampNanosForLastPosition == tempTimeNanos) {
+        mFrozenTimestampCount++;
+    } else {
+        mFrozenTimestampCount = 0;
+    }
+
+    if (mFrozenTimestampCount + mFrozenPositionCount > kMaxFrozenCount) {
+        ALOGW("%s too many frozen external position from HAL.", __func__);
+        mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+        return mHalExternalPositionStatus;
+    }
+
+    mLastPositionFrames = tempPositionFrames;
+    mTimestampNanosForLastPosition = tempTimeNanos;
+
+    // Only update the timestamp and position when they looks valid.
+    *positionFrames = tempPositionFrames;
+    *timeNanos = tempTimeNanos;
+    return mHalExternalPositionStatus;
 }
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index ddfac63..6314e5e 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -106,7 +106,12 @@
 
     int64_t                                   mHardwareTimeOffsetNanos = 0; // TODO get from HAL
 
-    bool                                      mExternalPositionSupported = true;
+    aaudio_result_t                           mHalExternalPositionStatus = AAUDIO_OK;
+    uint64_t                                  mLastPositionFrames = 0;
+    int64_t                                   mTimestampNanosForLastPosition = 0;
+    int32_t                                   mTimestampGracePeriodMs;
+    int32_t                                   mFrozenPositionCount = 0;
+    int32_t                                   mFrozenTimestampCount = 0;
 
 };
 
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index a25a791..8b5ccaa 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -52,7 +52,6 @@
         , mAtomicStreamTimestamp()
         , mAudioService(audioService) {
     mMmapClient.attributionSource = AttributionSourceState();
-    mThreadEnabled = true;
 }
 
 AAudioServiceStreamBase::~AAudioServiceStreamBase() {
@@ -178,6 +177,7 @@
 
     // Make sure this object does not get deleted before the run() method
     // can protect it by making a strong pointer.
+    mCommandQueue.startWaiting();
     mThreadEnabled = true;
     incStrong(nullptr); // See run() method.
     result = mCommandThread.start(this);
@@ -188,14 +188,15 @@
     return result;
 
 error:
-    close();
+    closeAndClear();
+    mThreadEnabled = false;
+    mCommandQueue.stopWaiting();
+    mCommandThread.stop();
     return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::close() {
-    auto command = std::make_shared<AAudioCommand>(
-            CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
-    aaudio_result_t result = mCommandQueue.sendCommand(command);
+    aaudio_result_t result = sendCommand(CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
 
     // Stop the command thread as the stream is closed.
     mThreadEnabled = false;
@@ -213,25 +214,7 @@
     // This will stop the stream, just in case it was not already stopped.
     stop_l();
 
-    aaudio_result_t result = AAUDIO_OK;
-    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
-    if (endpoint == nullptr) {
-        result = AAUDIO_ERROR_INVALID_STATE;
-    } else {
-        endpoint->unregisterStream(this);
-        AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance();
-        endpointManager.closeEndpoint(endpoint);
-
-        // AAudioService::closeStream() prevents two threads from closing at the same time.
-        mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns.
-    }
-
-    setState(AAUDIO_STREAM_STATE_CLOSED);
-
-    mediametrics::LogItem(mMetricsId)
-        .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE)
-        .record();
-    return result;
+    return closeAndClear();
 }
 
 aaudio_result_t AAudioServiceStreamBase::startDevice() {
@@ -250,9 +233,7 @@
  * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
  */
 aaudio_result_t AAudioServiceStreamBase::start() {
-    auto command = std::make_shared<AAudioCommand>(
-            START, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
+    return sendCommand(START, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
 }
 
 aaudio_result_t AAudioServiceStreamBase::start_l() {
@@ -300,9 +281,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::pause() {
-    auto command = std::make_shared<AAudioCommand>(
-            PAUSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
+    return sendCommand(PAUSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
 }
 
 aaudio_result_t AAudioServiceStreamBase::pause_l() {
@@ -338,9 +317,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::stop() {
-    auto command = std::make_shared<AAudioCommand>(
-            STOP, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
+    return sendCommand(STOP, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
 }
 
 aaudio_result_t AAudioServiceStreamBase::stop_l() {
@@ -385,9 +362,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush() {
-    auto command = std::make_shared<AAudioCommand>(
-            FLUSH, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
+    return sendCommand(FLUSH, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush_l() {
@@ -514,8 +489,7 @@
 }
 
 void AAudioServiceStreamBase::disconnect() {
-    auto command = std::make_shared<AAudioCommand>(DISCONNECT);
-    mCommandQueue.sendCommand(command);
+    sendCommand(DISCONNECT);
 }
 
 void AAudioServiceStreamBase::disconnect_l() {
@@ -533,12 +507,10 @@
 
 aaudio_result_t AAudioServiceStreamBase::registerAudioThread(pid_t clientThreadId, int priority) {
     const pid_t ownerPid = IPCThreadState::self()->getCallingPid(); // TODO review
-    auto command = std::make_shared<AAudioCommand>(
-            REGISTER_AUDIO_THREAD,
+    return sendCommand(REGISTER_AUDIO_THREAD,
             std::make_shared<RegisterAudioThreadParam>(ownerPid, clientThreadId, priority),
             true /*waitForReply*/,
             TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
 }
 
 aaudio_result_t AAudioServiceStreamBase::registerAudioThread_l(
@@ -561,12 +533,10 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread(pid_t clientThreadId) {
-    auto command = std::make_shared<AAudioCommand>(
-            UNREGISTER_AUDIO_THREAD,
+    return sendCommand(UNREGISTER_AUDIO_THREAD,
             std::make_shared<UnregisterAudioThreadParam>(clientThreadId),
             true /*waitForReply*/,
             TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
 }
 
 aaudio_result_t AAudioServiceStreamBase::unregisterAudioThread_l(pid_t clientThreadId) {
@@ -682,12 +652,11 @@
  * used to communicate with the underlying HAL or Service.
  */
 aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) {
-    auto command = std::make_shared<AAudioCommand>(
+    return sendCommand(
             GET_DESCRIPTION,
             std::make_shared<GetDescriptionParam>(&parcelable),
             true /*waitForReply*/,
             TIMEOUT_NANOS);
-    return mCommandQueue.sendCommand(command);
 }
 
 aaudio_result_t AAudioServiceStreamBase::getDescription_l(AudioEndpointParcelable* parcelable) {
@@ -707,3 +676,33 @@
 void AAudioServiceStreamBase::onVolumeChanged(float volume) {
     sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
 }
+
+aaudio_result_t AAudioServiceStreamBase::sendCommand(aaudio_command_opcode opCode,
+                                                     std::shared_ptr<AAudioCommandParam> param,
+                                                     bool waitForReply,
+                                                     int64_t timeoutNanos) {
+    return mCommandQueue.sendCommand(std::make_shared<AAudioCommand>(
+            opCode, param, waitForReply, timeoutNanos));
+}
+
+aaudio_result_t AAudioServiceStreamBase::closeAndClear() {
+    aaudio_result_t result = AAUDIO_OK;
+    sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
+    if (endpoint == nullptr) {
+        result = AAUDIO_ERROR_INVALID_STATE;
+    } else {
+        endpoint->unregisterStream(this);
+        AAudioEndpointManager &endpointManager = AAudioEndpointManager::getInstance();
+        endpointManager.closeEndpoint(endpoint);
+
+        // AAudioService::closeStream() prevents two threads from closing at the same time.
+        mServiceEndpoint.clear(); // endpoint will hold the pointer after this method returns.
+    }
+
+    setState(AAUDIO_STREAM_STATE_CLOSED);
+
+    mediametrics::LogItem(mMetricsId)
+        .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CLOSE)
+        .record();
+    return result;
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index aa8e8cf..dddd69f 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -366,6 +366,13 @@
     aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
                                      double dataDouble);
 
+    aaudio_result_t sendCommand(aaudio_command_opcode opCode,
+                                std::shared_ptr<AAudioCommandParam> param = nullptr,
+                                bool waitForReply = false,
+                                int64_t timeoutNanos = 0);
+
+    aaudio_result_t closeAndClear();
+
     /**
      * @return true if the queue is getting full.
      */
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 05b7f7d..ffc16ac 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -167,7 +167,6 @@
 // If it fails, get timestamp that was written by getFreeRunningPosition()
 aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp_l(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
-
     sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
     if (endpoint == nullptr) {
         ALOGE("%s() has no endpoint", __func__);
@@ -176,17 +175,17 @@
     sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
             static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
 
-    // Disable this code temporarily because the HAL is not returning
-    // a useful result.
-#if 0
     uint64_t position;
-    if (serviceEndpointMMAP->getExternalPosition(&position, timeNanos) == AAUDIO_OK) {
-        ALOGD("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
+    aaudio_result_t result = serviceEndpointMMAP->getExternalPosition(&position, timeNanos);
+    if (result == AAUDIO_OK) {
+        ALOGV("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
                 __func__, position, *timeNanos);
         *positionFrames = (int64_t) position;
         return AAUDIO_OK;
-    } else
-#endif
+    } else {
+        ALOGV("%s() getExternalPosition() returns error %d", __func__, result);
+    }
+
     if (mAtomicStreamTimestamp.isValid()) {
         Timestamp timestamp = mAtomicStreamTimestamp.read();
         *positionFrames = timestamp.getPosition();