Merge "SPDIFEncoder: Add missing verbose log commented define" into main am: b8a643fb8b am: 3f9e50e4c4 am: 571ca134fc

Original change: https://android-review.googlesource.com/c/platform/system/media/+/2821575

Change-Id: If903cfa4d95aba01b5529d6696a2a1ec5f50c5b0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/audio/include/system/audio-base-utils.h b/audio/include/system/audio-base-utils.h
index 4b81562..a348f05 100644
--- a/audio/include/system/audio-base-utils.h
+++ b/audio/include/system/audio-base-utils.h
@@ -135,7 +135,7 @@
     AUDIO_USAGE_CNT           = AUDIO_USAGE_CALL_ASSISTANT + 1,
 
     AUDIO_LATENCY_MODE_INVALID = -1,
-    AUDIO_LATENCY_MODE_CNT    = AUDIO_LATENCY_MODE_LOW + 1,
+    AUDIO_LATENCY_MODE_CNT    = AUDIO_LATENCY_MODE_DYNAMIC_SPATIAL_AUDIO_HARDWARE + 1,
 }; // enum
 
 // Microphone Field Dimension Constants
diff --git a/audio/include/system/audio.h b/audio/include/system/audio.h
index 826c1a2..c2c80bb 100644
--- a/audio/include/system/audio.h
+++ b/audio/include/system/audio.h
@@ -2423,6 +2423,11 @@
 /* Query if HwModule supports variable Bluetooth latency control */
 #define AUDIO_PARAMETER_BT_VARIABLE_LATENCY_SUPPORTED "isBtVariableLatencySupported"
 
+/* Reconfigure offloaded LE codec */
+#define AUDIO_PARAMETER_RECONFIG_LE "reconfigLe"
+/* Query if HwModule supports reconfiguration of offloaded LE codec */
+#define AUDIO_PARAMETER_LE_RECONFIG_SUPPORTED "isReconfigLeSupported"
+
 /**
  * For querying device supported encapsulation capabilities. All returned values are integer,
  * which are bit fields composed from using encapsulation capability values as position bits.
diff --git a/audio/include/system/audio_effects/effect_spatializer.h b/audio/include/system/audio_effects/effect_spatializer.h
index 971d0e3..b92bb50 100644
--- a/audio/include/system/audio_effects/effect_spatializer.h
+++ b/audio/include/system/audio_effects/effect_spatializer.h
@@ -35,11 +35,11 @@
     SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED,
     SPATIALIZER_PARAM_HEADTRACKING_MODE,            // See SpatializerHeadTrackingMode.aidl
     // list of supported input channel masks:
-    //  first unit32_t is the number of channel masks followed by the corresponding
+    //  first uint32_t is the number of channel masks followed by the corresponding
     // number of audio_channel_mask_t.
     SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS,
     // list of supported spatialization modes:
-    //  first unit32_t is the number of modes followed by the corresponding
+    //  first uint32_t is the number of modes followed by the corresponding
     // number of spatialization_mode_t.
     SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES,
     // Vector of 6 floats representing the head to stage pose:
@@ -68,6 +68,25 @@
     // The open/closed logical state differs than the hinge angle,
     // which may be reported by a hinge sensor.
     SPATIALIZER_PARAM_FOLD_STATE,
+
+    // Query the list of supported connection modes for head tracking data
+    // - FRAMEWORK_PROCESSED: the audio framework provides pre processed IMU data via
+    //   SPATIALIZER_PARAM_HEAD_TO_STAGE. This is the default and typically used for software
+    //   effect implementations (not offloaded to a DSP).
+    // - DIRECT_TO_SENSOR_SW: the audio framework just controls the enabled state of the sensor.
+    //   The Spatializer effect directly connects to the sensor via the sensor software stack.
+    //   Can be used by software implementations which do not want to benefit from the
+    //   preprocessing done on IMU data by AOSP libheadtracking. Can also be used by DSP
+    //   offloaded implementations.
+    // - DIRECT_TO_SENSOR_TUNNEL: the audio framework just controls the enabled state of the
+    //   sensor. The Spatializer effect directly connects to the sensor via hardware tunneling.
+    //   This mode is reserved for DSP offloaded implementations when the offload mode is enabled
+    //   by the framework.
+    SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION,
+
+    // Set/get the head tracking data connection mode: passes the mode followed by the sensor ID
+    // on uint32_t
+    SPATIALIZER_PARAM_HEADTRACKING_CONNECTION
 } t_virtualizer_stage_params;
 
 // See SpatializationLevel.aidl
@@ -82,6 +101,13 @@
     SPATIALIZATION_MODE_TRANSAURAL = 1,
 } spatialization_mode_t;
 
+//TODO b/273373363: use AIDL enum when available
+typedef enum {
+    HEADTRACKING_CONNECTION_FRAMEWORK_PROCESSED = 0,
+    HEADTRACKING_CONNECTION_DIRECT_TO_SENSOR_SW = 1,
+    HEADTRACKING_CONNECTION_DIRECT_TO_SENSOR_TUNNEL = 2,
+} headtracking_connection_t;
+
 #if __cplusplus
 }  // extern "C"
 #endif
diff --git a/audio_utils/Android.bp b/audio_utils/Android.bp
index 41817b3..2de834d 100644
--- a/audio_utils/Android.bp
+++ b/audio_utils/Android.bp
@@ -56,11 +56,13 @@
         "Metadata.cpp",
         "minifloat.c",
         "mono_blend.cpp",
+        "mutex.cpp",
         "power.cpp",
         "PowerLog.cpp",
         "primitives.c",
         "roundup.c",
         "sample.c",
+        "threads.cpp",
         "hal_smoothness.c",
     ],
 
@@ -78,9 +80,13 @@
         "libcutils",
         "liblog",
         "libutils",
+        "server_configurable_flags",
     ],
 
     whole_static_libs: [
+        // if libaudioutils is added as a static lib AND flags are used in the utils object,
+        // then add server_configurable_flags as a shared lib.
+        "com.android.media.audioserver-aconfig-cc",
         "libaudioutils_fastmath",
     ],
 
@@ -172,7 +178,10 @@
     name: "libsndfile",
     defaults: ["audio_utils_defaults"],
     host_supported: true,
-    srcs: ["tinysndfile.c"],
+    srcs: [
+        "primitives.c",
+        "tinysndfile.c",
+    ],
     cflags: [
         "-UHAVE_STDERR",
     ],
@@ -200,7 +209,7 @@
     },
 }
 
-cc_library_shared {
+cc_library {
     name: "libaudiospdif",
     host_supported: true,
     defaults: ["audio_utils_defaults"],
diff --git a/audio_utils/benchmarks/Android.bp b/audio_utils/benchmarks/Android.bp
index cb7c41a..1d7ab17 100644
--- a/audio_utils/benchmarks/Android.bp
+++ b/audio_utils/benchmarks/Android.bp
@@ -9,6 +9,23 @@
 }
 
 cc_benchmark {
+    name: "audio_mutex_benchmark",
+
+    srcs: ["audio_mutex_benchmark.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libaudioutils",
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_benchmark {
     name: "biquad_filter_benchmark",
     host_supported: true,
 
diff --git a/audio_utils/benchmarks/audio_mutex_benchmark.cpp b/audio_utils/benchmarks/audio_mutex_benchmark.cpp
new file mode 100644
index 0000000..0280706
--- /dev/null
+++ b/audio_utils/benchmarks/audio_mutex_benchmark.cpp
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2023 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 <audio_utils/mutex.h>
+
+#include <benchmark/benchmark.h>
+#include <thread>
+#include <utils/Timers.h>
+
+/*
+On Pixel 7 U arm64-v8a
+
+Note: to bump up the scheduler clock frequency, one can use the toybox uclampset:
+$ adb shell uclampset -m 1024 /data/benchmarktest64/audio_mutex_benchmark/audio_mutex_benchmark
+
+For simplicity these tests use the regular invocation:
+$ atest audio_mutex_benchmark
+
+PI disabled
+Benchmark                                                     Time        CPU        Iteration
+audio_mutex_benchmark:
+  #BM_gettid                                                 2.114707270012698 ns     2.1060503125712704 ns     316574041
+  #BM_systemTime                                             45.21805864916908 ns     45.026921817247256 ns      15550399
+  #BM_thread_8_variables                                    2.8218655987348464 ns     2.8084059508955304 ns     249245377
+  #BM_thread_local_8_variables                              2.8193214063325636 ns      2.808094924629991 ns     249270437
+  #BM_StdMutexLockUnlock                                    18.284456262132316 ns     18.198265636345102 ns      38452720
+  #BM_AudioUtilsMutexLockUnlock                              65.64444199789075 ns      65.27982033200155 ns      10727119
+  #BM_StdMutexInitializationLockUnlock                      27.511005854186497 ns     27.400197055351832 ns      25576570
+  #BM_AudioUtilsMutexInitializationLockUnlock                80.13035874526041 ns       79.7832739530702 ns       8771433
+  #BM_StdMutexBlockingConditionVariable/threads:2           20162.129644197645 ns     23610.966860300927 ns         49578
+  #BM_AudioUtilsMutexBlockingConditionVariable/threads:2    15137.825614929703 ns     17290.483328991908 ns         53746
+  #BM_StdMutexScopedLockUnlock/threads:2                     237.3259685000022 ns     471.24025449999993 ns       2000000
+  #BM_AudioUtilsMutexScopedLockUnlock/threads:2             1166.2574495961544 ns     2286.2857883742913 ns        696078
+  #BM_StdMutexReverseScopedLockUnlock/threads:2              78.13460285213144 ns      147.4075991604524 ns       4367114
+  #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2       652.4407601321186 ns      1260.665055303518 ns        729610
+  #BM_empty_while                                           0.3525216479999926 ns    0.35102758499999887 ns    1000000000
+
+PI enabled
+Benchmark                                                     Time        CPU        Iteration
+audio_mutex_benchmark:
+  #BM_gettid                                                2.1172475906830703 ns    2.1060892051984452 ns     320818635
+  #BM_systemTime                                            45.199132456479795 ns     45.00638566260978 ns      15548269
+  #BM_thread_8_variables                                    2.8225443366713554 ns     2.808487087931257 ns     249225013
+  #BM_thread_local_8_variables                               2.821986069144024 ns    2.8082593311342396 ns     249257908
+  #BM_StdMutexLockUnlock                                     18.40590257629023 ns    18.181680287238002 ns      38568989
+  #BM_AudioUtilsMutexLockUnlock                              67.93231825441279 ns      67.0807303508343 ns      10434985
+  #BM_StdMutexInitializationLockUnlock                       27.68553624974349 ns    27.411678887121656 ns      25531919
+  #BM_AudioUtilsMutexInitializationLockUnlock                87.67312115717117 ns     86.83834669971819 ns       8058524
+  #BM_StdMutexBlockingConditionVariable/threads:2            17272.46593515828 ns     19906.88809450987 ns         78468
+  #BM_AudioUtilsMutexBlockingConditionVariable/threads:2    32005.869201264206 ns     37545.03395758123 ns         44320
+  #BM_StdMutexScopedLockUnlock/threads:2                    486.46992430399723 ns     956.4308063689062 ns       1080374
+  #BM_AudioUtilsMutexScopedLockUnlock/threads:2              2054.297687080403 ns    2381.4833355667856 ns        462662
+  #BM_StdMutexReverseScopedLockUnlock/threads:2              82.48454541925697 ns     156.5002537866742 ns       3502942
+  #BM_AudioUtilsMutexReverseScopedLockUnlock/threads:2       7202.760827499759 ns     8601.367344999997 ns        200000
+  #BM_empty_while                                           0.3630271000000107 ns    0.3611862729999995 ns    1000000000
+
+*/
+
+// ---
+
+// Benchmark gettid().  The mutex class uses this to get the linux thread id.
+static void BM_gettid(benchmark::State &state) {
+    int32_t value = 0;
+    while (state.KeepRunning()) {
+        value ^= android::audio_utils::gettid_wrapper();  // ensure the return value used.
+    }
+    ALOGD("%s: value:%d", __func__, value);
+}
+
+BENCHMARK(BM_gettid);
+
+// Benchmark systemTime().  The mutex class uses this for timing.
+static void BM_systemTime(benchmark::State &state) {
+    int64_t value = 0;
+    while (state.KeepRunning()) {
+        value ^= systemTime();
+    }
+    ALOGD("%s: value:%lld", __func__, (long long)value);
+}
+
+BENCHMARK(BM_systemTime);
+
+// Benchmark access to 8 thread local storage variables by compiler built_in __thread.
+__thread volatile int tls_value1 = 1;
+__thread volatile int tls_value2 = 2;
+__thread volatile int tls_value3 = 3;
+__thread volatile int tls_value4 = 4;
+__thread volatile int tls_value5 = 5;
+__thread volatile int tls_value6 = 6;
+__thread volatile int tls_value7 = 7;
+__thread volatile int tls_value8 = 8;
+
+static void BM_thread_8_variables(benchmark::State &state) {
+    while (state.KeepRunning()) {
+        tls_value1 ^= tls_value1 ^ tls_value2 ^ tls_value3 ^ tls_value4 ^
+                tls_value5 ^ tls_value6 ^ tls_value7 ^ tls_value8;
+    }
+    ALOGD("%s: value:%d", __func__, tls_value1);
+}
+
+BENCHMARK(BM_thread_8_variables);
+
+// Benchmark access to 8 thread local storage variables through the
+// the C/C++ 11 standard thread_local.
+thread_local volatile int tlsa_value1 = 1;
+thread_local volatile int tlsa_value2 = 2;
+thread_local volatile int tlsa_value3 = 3;
+thread_local volatile int tlsa_value4 = 4;
+thread_local volatile int tlsa_value5 = 5;
+thread_local volatile int tlsa_value6 = 6;
+thread_local volatile int tlsa_value7 = 7;
+thread_local volatile int tlsa_value8 = 8;
+
+static void BM_thread_local_8_variables(benchmark::State &state) {
+    while (state.KeepRunning()) {
+        tlsa_value1 ^= tlsa_value1 ^ tlsa_value2 ^ tlsa_value3 ^ tlsa_value4 ^
+                tlsa_value5 ^ tlsa_value6 ^ tlsa_value7 ^ tlsa_value8;
+    }
+    ALOGD("%s: value:%d", __func__, tlsa_value1);
+}
+
+BENCHMARK(BM_thread_local_8_variables);
+
+// ---
+
+template <typename Mutex>
+void MutexLockUnlock(benchmark::State& state) {
+    Mutex m;
+    while (state.KeepRunning()) {
+        m.lock();
+        m.unlock();
+    }
+}
+
+static void BM_StdMutexLockUnlock(benchmark::State &state) {
+    MutexLockUnlock<std::mutex>(state);
+}
+
+// Benchmark repeated mutex lock/unlock from a single thread
+// using std::mutex.
+BENCHMARK(BM_StdMutexLockUnlock);
+
+static void BM_AudioUtilsMutexLockUnlock(benchmark::State &state) {
+    MutexLockUnlock<android::audio_utils::mutex>(state);
+}
+
+// Benchmark repeated mutex lock/unlock from a single thread
+// using audio_utils::mutex.  This will differ when priority inheritance
+// is used or not.
+BENCHMARK(BM_AudioUtilsMutexLockUnlock);
+
+// ---
+
+template <typename Mutex>
+void MutexInitializationLockUnlock(benchmark::State& state) {
+    while (state.KeepRunning()) {
+        Mutex m;
+        m.lock();
+        m.unlock();
+    }
+}
+
+static void BM_StdMutexInitializationLockUnlock(benchmark::State &state) {
+    MutexInitializationLockUnlock<std::mutex>(state);
+}
+
+// Benchmark repeated mutex creation then lock/unlock from a single thread
+// using std::mutex.
+BENCHMARK(BM_StdMutexInitializationLockUnlock);
+
+static void BM_AudioUtilsMutexInitializationLockUnlock(benchmark::State &state) {
+    MutexInitializationLockUnlock<android::audio_utils::mutex>(state);
+}
+
+// Benchmark repeated mutex creation then lock/unlock from a single thread
+// using audio_utils::mutex.  This will differ when priority inheritance
+// is used or not.
+BENCHMARK(BM_AudioUtilsMutexInitializationLockUnlock);
+
+// ---
+
+static constexpr size_t THREADS = 2;
+
+template <typename Mutex, typename UniqueLock, typename ConditionVariable>
+class MutexBlockingConditionVariable {
+    Mutex m;
+    ConditionVariable cv[THREADS];
+    bool wake[THREADS];
+
+public:
+    void run(benchmark::State& state) {
+        const size_t local = state.thread_index();
+        const size_t remote = (local + 1) % THREADS;
+        if (local == 0) wake[local] = true;
+        for (auto _ : state) {
+            UniqueLock ul(m);
+            cv[local].wait(ul, [&]{ return wake[local]; });
+            wake[remote] = true;
+            wake[local] = false;
+            cv[remote].notify_one();
+        }
+        UniqueLock ul(m);
+        wake[remote] = true;
+        cv[remote].notify_one();
+    }
+};
+
+MutexBlockingConditionVariable<std::mutex,
+            std::unique_lock<std::mutex>,
+            std::condition_variable> CvStd;
+
+static void BM_StdMutexBlockingConditionVariable(benchmark::State &state) {
+    CvStd.run(state);
+}
+
+// Benchmark 2 threads that use condition variables to wake each other up,
+// where only one thread is active at a given time.  This benchmark
+// uses std::mutex, std::unique_lock, and std::condition_variable.
+BENCHMARK(BM_StdMutexBlockingConditionVariable)->Threads(THREADS);
+
+MutexBlockingConditionVariable<android::audio_utils::mutex,
+        android::audio_utils::unique_lock,
+        android::audio_utils::condition_variable> CvAu;
+
+static void BM_AudioUtilsMutexBlockingConditionVariable(benchmark::State &state) {
+    CvAu.run(state);
+}
+
+// Benchmark 2 threads that use condition variables to wake each other up,
+// where only one thread is active at a given time.  This benchmark
+// uses audio_utils::mutex, audio_utils::unique_lock, and audio_utils::condition_variable.
+//
+// Priority inheritance mutexes will affect the performance of this benchmark.
+BENCHMARK(BM_AudioUtilsMutexBlockingConditionVariable)->Threads(THREADS);
+
+// ---
+
+template <typename Mutex, typename ScopedLock>
+class MutexScopedLockUnlock {
+    const bool reverse_;
+    Mutex m1_, m2_;
+    int counter_ = 0;
+
+public:
+    MutexScopedLockUnlock(bool reverse = false) : reverse_(reverse) {}
+
+    void run(benchmark::State& state) {
+        const size_t index = state.thread_index();
+        for (auto _ : state) {
+            if (!reverse_ || index & 1) {
+                ScopedLock s1(m1_, m2_);
+                ++counter_;
+            } else {
+                ScopedLock s1(m2_, m1_);
+                ++counter_;
+            }
+        }
+    }
+};
+
+MutexScopedLockUnlock<std::mutex,
+        std::scoped_lock<std::mutex, std::mutex>> ScopedStd;
+
+static void BM_StdMutexScopedLockUnlock(benchmark::State &state) {
+    ScopedStd.run(state);
+}
+
+// Benchmark scoped_lock where two threads try to obtain the
+// same 2 locks with the same initial acquisition order.
+// This uses std::mutex, std::scoped_lock.
+BENCHMARK(BM_StdMutexScopedLockUnlock)->Threads(THREADS);
+
+MutexScopedLockUnlock<android::audio_utils::mutex,
+        android::audio_utils::scoped_lock<
+                android::audio_utils::mutex, android::audio_utils::mutex>> ScopedAu;
+
+static void BM_AudioUtilsMutexScopedLockUnlock(benchmark::State &state) {
+    ScopedAu.run(state);
+}
+
+// Benchmark scoped_lock where two threads try to obtain the
+// same 2 locks with the same initial acquisition order.
+// This uses audio_utils::mutex and audio_utils::scoped_lock.
+//
+// Priority inheritance mutexes will affect the performance of this benchmark.
+BENCHMARK(BM_AudioUtilsMutexScopedLockUnlock)->Threads(THREADS);
+
+MutexScopedLockUnlock<std::mutex,
+        std::scoped_lock<std::mutex, std::mutex>> ReverseScopedStd(true);
+
+static void BM_StdMutexReverseScopedLockUnlock(benchmark::State &state) {
+    ReverseScopedStd.run(state);
+}
+
+// Benchmark scoped_lock with odd thread having reversed scoped_lock mutex acquisition order.
+// This uses std::mutex, std::scoped_lock.
+BENCHMARK(BM_StdMutexReverseScopedLockUnlock)->Threads(THREADS);
+
+MutexScopedLockUnlock<android::audio_utils::mutex,
+        android::audio_utils::scoped_lock<
+                android::audio_utils::mutex, android::audio_utils::mutex>> ReverseScopedAu(true);
+
+static void BM_AudioUtilsMutexReverseScopedLockUnlock(benchmark::State &state) {
+    ReverseScopedAu.run(state);
+}
+
+// Benchmark scoped_lock with odd thread having reversed scoped_lock mutex acquisition order.
+// This uses audio_utils::mutex and audio_utils::scoped_lock.
+//
+// Priority inheritance mutexes will affect the performance of this benchmark.
+BENCHMARK(BM_AudioUtilsMutexReverseScopedLockUnlock)->Threads(THREADS);
+
+static void BM_empty_while(benchmark::State &state) {
+
+    while (state.KeepRunning()) {
+        ;
+    }
+    ALOGD("%s", android::audio_utils::mutex::all_stats_to_string().c_str());
+}
+
+// Benchmark to see the cost of doing nothing.
+BENCHMARK(BM_empty_while);
+
+BENCHMARK_MAIN();
diff --git a/audio_utils/fuzz/fdtostring_fuzzer/fdtostring_fuzzer.cpp b/audio_utils/fuzz/fdtostring_fuzzer/fdtostring_fuzzer.cpp
index 090ef5e..bfee878 100644
--- a/audio_utils/fuzz/fdtostring_fuzzer/fdtostring_fuzzer.cpp
+++ b/audio_utils/fuzz/fdtostring_fuzzer/fdtostring_fuzzer.cpp
@@ -30,10 +30,10 @@
   const std::string PREFIX{data_str.substr(0, 3)};
   const std::string TEST_STRING{data_str.substr(3)+"\n"};
 
-  FdToString fdToString(PREFIX);
-  const int fd = fdToString.fd();
+  auto writer = FdToString::createWriter(PREFIX);
+  const int fd = writer->borrowFdUnsafe();
   write(fd, TEST_STRING.c_str(), TEST_STRING.size());
 
-  (void)fdToString.getStringAndClose();
+  (void)FdToString::closeWriterAndGetString(std::move(*writer));
   return 0;
 }
diff --git a/audio_utils/include/audio_utils/FdToString.h b/audio_utils/include/audio_utils/FdToString.h
index 4ca0b8d..64cb875 100644
--- a/audio_utils/include/audio_utils/FdToString.h
+++ b/audio_utils/include/audio_utils/FdToString.h
@@ -17,12 +17,18 @@
 #ifndef ANDROID_AUDIO_FD_TO_STRING_H
 #define ANDROID_AUDIO_FD_TO_STRING_H
 
+#include <android-base/unique_fd.h>
 #include <fcntl.h>
-#include <future>
 #include <poll.h>
-#include <sstream>
 #include <unistd.h>
 #include <utils/Timers.h>
+#include <chrono>
+#include <future>
+#include <iostream>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <string_view>
 
 #include "clock.h"
 
@@ -30,22 +36,20 @@
 namespace audio_utils {
 
 /**
- * FdToString
+ * FdToStringOldImpl
  *
  * Captures string data written to a file descriptor.
  * The class will furnish a writable file descriptor by fd().
- * The string may be read through getStringAndClose().
+ * The string may be read through closeAndGetString().
  */
-
-class FdToString {
-public:
+class FdToStringOldImpl {
+  public:
     /**
      * \param prefix is the prefix string prepended to each new line.
      * \param timeoutMs is the total timeout to wait for obtaining data in milliseconds.
      */
-    explicit FdToString(const std::string &prefix = "- ", int timeoutMs = 200)
-            : mPrefix(prefix)
-            , mTimeoutTimeNs(systemTime() + timeoutMs * NANOS_PER_MILLISECOND) {
+    explicit FdToStringOldImpl(const std::string& prefix = "- ", int timeoutMs = 200)
+        : mPrefix(prefix), mTimeoutTimeNs(systemTime() + timeoutMs * NANOS_PER_MILLISECOND) {
         const int status = pipe2(mPipeFd, O_CLOEXEC);
         if (status == 0) {
             mOutput = std::async(std::launch::async, reader, mPipeFd[0], mTimeoutTimeNs, mPrefix);
@@ -53,8 +57,8 @@
         // on initialization failure fd() returns -1.
     }
 
-    ~FdToString() {
-        for (auto &fd : mPipeFd) {
+    ~FdToStringOldImpl() {
+        for (auto& fd : mPipeFd) {
             if (fd >= 0) {
                 close(fd);
                 fd = -1;
@@ -64,21 +68,19 @@
 
     /**
      * Returns the write end of the pipe as a file descriptor or -1 if invalid or already closed.
-     *
      * Do not close this fd directly as this class should own the fd. Instead, use
-     * getStringAndClose() to close the fd and return the string.
+     * closeAndGetString() to close the fd and return the string.
      */
-    int fd() const {
-        return mPipeFd[1];
-    }
+    int borrowFdUnsafe() const { return mPipeFd[1]; }
 
     /**
-     * Returns the string representation of data written to the fd.
+     * Returns the string representation of data written to the fd. Awaits reader thread.
      *
-     * An empty string is returned on failure (or timeout).  It is acceptable to call this
-     * method multiple times to obtain the final string; the fd is closed after the first call.
+     * All writers should have returned by this point.
+     *
+     * An empty string is returned on initialization failure or timeout. Closes fd.
      */
-    std::string getStringAndClose() {
+    std::string closeAndGetString() {
         if (!mOutput.valid()) return "";
         if (mPipeFd[1] >= 0) {
             close(mPipeFd[1]);
@@ -89,7 +91,7 @@
         return status == std::future_status::ready ? mOutput.get() : "";
     }
 
-private:
+  private:
     static std::string reader(int fd, int64_t timeoutTimeNs, std::string prefix) {
         char buf[4096];
         int red;
@@ -98,18 +100,19 @@
 
         while (true) {
             struct pollfd pfd = {
-                .fd = fd,
-                .events = POLLIN | POLLRDHUP,
+                    .fd = fd,
+                    .events = POLLIN | POLLRDHUP,
             };
             const int waitMs = toMillisecondTimeoutDelay(systemTime(), timeoutTimeNs);
             // ALOGD("waitMs: %d", waitMs);
             if (waitMs <= 0) break;
             const int retval = poll(&pfd, 1 /* nfds*/, waitMs);
-            if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break; // error or timeout
-            // data is available
+            // error, timeout, or hangup (without data to read)
+            if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break;
+            // data should be available
             if ((red = read(fd, buf, sizeof(buf))) <= 0) break;
             char *delim, *bptr = buf;
-            while (!prefix.empty() && (delim = (char *)memchr(bptr, '\n', red)) != nullptr) {
+            while (!prefix.empty() && (delim = (char*)memchr(bptr, '\n', red)) != nullptr) {
                 if (requiresPrefix) ss << prefix;
                 const size_t line = delim - bptr + 1;
                 ss.write(bptr, line);
@@ -132,7 +135,137 @@
     std::future<std::string> mOutput;
 };
 
-} // namespace audio_utils
-} // namespace android
+/**
+ * Launch reader task which accumulates data written to the fd that this class exposes.
+ * Usage as follows:
+ *  {
+ *     writer = FdToString::createWriter(); // fork point, reader launched
+ *     sendFdToWriters(writer.borrowFdUnsafe()); // fd is safe while writer is valid
+ *     st = FdToString::closeWriterAndGetString(std::move(writer));
+ *     // join point (writers must be done)
+ *  } // writer dtor closes fd, joins reader if close not called
+ *
+ * This class expects that the write fd is unduped when close is called, otherwise the reader will
+ * always hit the timeout. We implicitly trust that the borrowed fd won't be duped (or that its
+ * dupes will be closed by closeWriterAndGetString()).
+ * Note, the reader closes the fd to signal which closes the read end of the pipe. If the writer is
+ * living in a process without signal(SIGPIPE, SIGIGN), they will crash.
+ */
+class FdToString {
+  public:
+    class Writer {
+      public:
+        /**
+         * Returns the write end of the pipe as a file descriptor.
+         * Non-Owning reference! This object must be valid to keep accessing the fd.
+         * Do not close this fd directly as this class should own the fd.
+         * Leaking dupes of this fd will keep the reader alive.
+         * Use closeWriterAndGetString(Writer&& w) to consume this object and return the string.
+         * The fd returned by this method is invalid after this point.
+         */
+        int borrowFdUnsafe() const { return mWriteFd.get(); }
 
-#endif // !ANDROID_AUDIO_FD_TO_STRING_H
+        const android::base::unique_fd& getFd() const { return mWriteFd; }
+
+      private:
+        friend FdToString;
+        // Pre-condition: fd and future both valid. Should only be called from create.
+        Writer(android::base::unique_fd writeFd, std::future<std::string> output)
+            : mOutput(std::move(output)), mWriteFd(std::move(writeFd)) {}
+
+        std::future<std::string> mOutput;
+        android::base::unique_fd mWriteFd;  // order important! must be destroyed first to join
+    };
+
+  public:
+    /**
+     * Factory method for Writer object. Launches the async reader.
+     * \param prefix is the prefix string prepended to each new line.
+     * \param timeoutMs is the total timeout to wait for obtaining data in milliseconds.
+     * \returns nullopt on init error.
+     */
+    static std::optional<Writer> createWriter(
+            std::string_view prefix_ = "- ",
+            std::chrono::milliseconds timeout = std::chrono::milliseconds{200}) {
+        android::base::unique_fd readFd, writeFd;
+        if (!android::base::Pipe(&readFd, &writeFd)) return {};
+        const auto flags = fcntl(readFd.get(), F_GETFL);
+        if (flags < 0) return {};
+        // Set (only) the reader as non-blocking. We want to only read until the deadline.
+        if (fcntl(readFd, F_SETFL, flags | O_NONBLOCK) < 0) return {};
+        const auto deadline = systemTime() + std::chrono::nanoseconds{timeout}.count();
+        // Launch async reader task, will return after deadline
+        return Writer{
+                std::move(writeFd),
+                std::async(std::launch::async,
+                           // reader task to follow, logically oneshot
+                           [fd = std::move(readFd), deadline,
+                            prefix = std::string{prefix_}]() mutable {
+                               char buf[4096];
+                               std::string out;
+                               bool requiresPrefix = true;
+
+                               while (true) {
+                                   struct pollfd pfd = {
+                                           .fd = fd.get(),
+                                           .events = POLLIN | POLLRDHUP,
+                                   };
+                                   const int waitMs =
+                                           toMillisecondTimeoutDelay(systemTime(), deadline);
+                                   if (waitMs <= 0) break;
+                                   const int retval = poll(&pfd, 1 /* nfds*/, waitMs);
+                                   // break on error or timeout
+                                   if (retval <= 0 || (pfd.revents & POLLIN) != POLLIN) break;
+                                   // data is available
+                                   int red = read(fd, buf, sizeof(buf));
+                                   if (red < 0) {
+                                       break;
+                                   } else if (red == 0) {
+                                       continue;
+                                   }
+
+                                   std::string_view sv{buf, static_cast<size_t>(red)};
+
+                                   if (!prefix.empty()) {
+                                       size_t ind;
+                                       while ((ind = sv.find('\n', 0)) != std::string_view::npos) {
+                                           if (requiresPrefix) {
+                                               out.append(prefix);
+                                           }
+                                           out.append(sv.data(), ind + 1);
+                                           sv.remove_prefix(ind + 1);
+                                           requiresPrefix = true;
+                                       }
+                                       if (sv.length() > 0) {
+                                           out.append(sv);
+                                           requiresPrefix = false;
+                                       }
+                                   } else {
+                                       out.append(sv);
+                                   }
+                               }
+                               // Explicit clear, because state is kept until future consumption
+                               fd.reset();
+                               return out;
+                           })};
+    }
+
+    /**
+     * Closes the write side. Returns the string representation of data written to the fd.
+     * Awaits reader thread.
+     *
+     * All writers should have returned by this point.
+     *
+     */
+    static std::string closeWriterAndGetString(Writer&& writer) {
+        // Closes the fd, which finishes the reader
+        writer.mWriteFd.reset();
+        // moved out of future + NVRO
+        return writer.mOutput.get();
+    }
+};
+
+}  // namespace audio_utils
+}  // namespace android
+
+#endif  // !ANDROID_AUDIO_FD_TO_STRING_H
diff --git a/audio_utils/include/audio_utils/mutex.h b/audio_utils/include/audio_utils/mutex.h
index 674228a..c78860f 100644
--- a/audio_utils/include/audio_utils/mutex.h
+++ b/audio_utils/include/audio_utils/mutex.h
@@ -17,9 +17,21 @@
 #pragma once
 
 #include <android-base/thread_annotations.h>
+#include <audio_utils/threads.h>
 #include <utils/Log.h>
+#include <utils/Timers.h>
 
+#include <algorithm>
+#include <array>
+#include <cmath>
+#include <map>
+#include <memory>
 #include <mutex>
+#include <sys/syscall.h>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
 
 #pragma push_macro("LOG_TAG")
 #undef LOG_TAG
@@ -27,6 +39,892 @@
 
 namespace android::audio_utils {
 
+// Define global capabilities for thread-safety annotation.
+//
+// These can be manually modified, or
+// compile generate_mutex_order.cpp in the tests directory
+// to generate this.
+
+// --- Begin generated section
+
+// Lock order
+enum class MutexOrder : uint32_t {
+    kEffectHandle_Mutex = 0,
+    kEffectBase_PolicyMutex = 1,
+    kAudioFlinger_Mutex = 2,
+    kAudioFlinger_HardwareMutex = 3,
+    kDeviceEffectManager_Mutex = 4,
+    kPatchCommandThread_Mutex = 5,
+    kThreadBase_Mutex = 6,
+    kAudioFlinger_ClientMutex = 7,
+    kMelReporter_Mutex = 8,
+    kEffectChain_Mutex = 9,
+    kDeviceEffectProxy_ProxyMutex = 10,
+    kEffectBase_Mutex = 11,
+    kAudioFlinger_UnregisteredWritersMutex = 12,
+    kAsyncCallbackThread_Mutex = 13,
+    kConfigEvent_Mutex = 14,
+    kOutputTrack_TrackMetadataMutex = 15,
+    kPassthruPatchRecord_ReadMutex = 16,
+    kPatchCommandThread_ListenerMutex = 17,
+    kPlaybackThread_AudioTrackCbMutex = 18,
+    kMediaLogNotifier_Mutex = 19,
+    kOtherMutex = 20,
+    kSize = 21,
+};
+
+// Lock by name
+inline constexpr const char* const gMutexNames[] = {
+    "EffectHandle_Mutex",
+    "EffectBase_PolicyMutex",
+    "AudioFlinger_Mutex",
+    "AudioFlinger_HardwareMutex",
+    "DeviceEffectManager_Mutex",
+    "PatchCommandThread_Mutex",
+    "ThreadBase_Mutex",
+    "AudioFlinger_ClientMutex",
+    "MelReporter_Mutex",
+    "EffectChain_Mutex",
+    "DeviceEffectProxy_ProxyMutex",
+    "EffectBase_Mutex",
+    "AudioFlinger_UnregisteredWritersMutex",
+    "AsyncCallbackThread_Mutex",
+    "ConfigEvent_Mutex",
+    "OutputTrack_TrackMetadataMutex",
+    "PassthruPatchRecord_ReadMutex",
+    "PatchCommandThread_ListenerMutex",
+    "PlaybackThread_AudioTrackCbMutex",
+    "MediaLogNotifier_Mutex",
+    "OtherMutex",
+};
+
+// Forward declarations
+class AudioMutexAttributes;
+template <typename T> class mutex_impl;
+using mutex = mutex_impl<AudioMutexAttributes>;
+
+// Capabilities in priority order
+// (declaration only, value is nullptr)
+inline mutex* EffectHandle_Mutex;
+inline mutex* EffectBase_PolicyMutex
+        ACQUIRED_AFTER(android::audio_utils::EffectHandle_Mutex);
+inline mutex* AudioFlinger_Mutex
+        ACQUIRED_AFTER(android::audio_utils::EffectBase_PolicyMutex);
+inline mutex* AudioFlinger_HardwareMutex
+        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_Mutex);
+inline mutex* DeviceEffectManager_Mutex
+        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_HardwareMutex);
+inline mutex* PatchCommandThread_Mutex
+        ACQUIRED_AFTER(android::audio_utils::DeviceEffectManager_Mutex);
+inline mutex* ThreadBase_Mutex
+        ACQUIRED_AFTER(android::audio_utils::PatchCommandThread_Mutex);
+inline mutex* AudioFlinger_ClientMutex
+        ACQUIRED_AFTER(android::audio_utils::ThreadBase_Mutex);
+inline mutex* MelReporter_Mutex
+        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_ClientMutex);
+inline mutex* EffectChain_Mutex
+        ACQUIRED_AFTER(android::audio_utils::MelReporter_Mutex);
+inline mutex* DeviceEffectProxy_ProxyMutex
+        ACQUIRED_AFTER(android::audio_utils::EffectChain_Mutex);
+inline mutex* EffectBase_Mutex
+        ACQUIRED_AFTER(android::audio_utils::DeviceEffectProxy_ProxyMutex);
+inline mutex* AudioFlinger_UnregisteredWritersMutex
+        ACQUIRED_AFTER(android::audio_utils::EffectBase_Mutex);
+inline mutex* AsyncCallbackThread_Mutex
+        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_UnregisteredWritersMutex);
+inline mutex* ConfigEvent_Mutex
+        ACQUIRED_AFTER(android::audio_utils::AsyncCallbackThread_Mutex);
+inline mutex* OutputTrack_TrackMetadataMutex
+        ACQUIRED_AFTER(android::audio_utils::ConfigEvent_Mutex);
+inline mutex* PassthruPatchRecord_ReadMutex
+        ACQUIRED_AFTER(android::audio_utils::OutputTrack_TrackMetadataMutex);
+inline mutex* PatchCommandThread_ListenerMutex
+        ACQUIRED_AFTER(android::audio_utils::PassthruPatchRecord_ReadMutex);
+inline mutex* PlaybackThread_AudioTrackCbMutex
+        ACQUIRED_AFTER(android::audio_utils::PatchCommandThread_ListenerMutex);
+inline mutex* MediaLogNotifier_Mutex
+        ACQUIRED_AFTER(android::audio_utils::PlaybackThread_AudioTrackCbMutex);
+inline mutex* OtherMutex
+        ACQUIRED_AFTER(android::audio_utils::MediaLogNotifier_Mutex);
+
+// Exclusion by capability
+#define EXCLUDES_BELOW_OtherMutex
+#define EXCLUDES_OtherMutex \
+    EXCLUDES(android::audio_utils::OtherMutex) \
+    EXCLUDES_BELOW_OtherMutex
+
+#define EXCLUDES_BELOW_MediaLogNotifier_Mutex \
+    EXCLUDES_OtherMutex
+#define EXCLUDES_MediaLogNotifier_Mutex \
+    EXCLUDES(android::audio_utils::MediaLogNotifier_Mutex) \
+    EXCLUDES_BELOW_MediaLogNotifier_Mutex
+
+#define EXCLUDES_BELOW_PlaybackThread_AudioTrackCbMutex \
+    EXCLUDES_MediaLogNotifier_Mutex
+#define EXCLUDES_PlaybackThread_AudioTrackCbMutex \
+    EXCLUDES(android::audio_utils::PlaybackThread_AudioTrackCbMutex) \
+    EXCLUDES_BELOW_PlaybackThread_AudioTrackCbMutex
+
+#define EXCLUDES_BELOW_PatchCommandThread_ListenerMutex \
+    EXCLUDES_PlaybackThread_AudioTrackCbMutex
+#define EXCLUDES_PatchCommandThread_ListenerMutex \
+    EXCLUDES(android::audio_utils::PatchCommandThread_ListenerMutex) \
+    EXCLUDES_BELOW_PatchCommandThread_ListenerMutex
+
+#define EXCLUDES_BELOW_PassthruPatchRecord_ReadMutex \
+    EXCLUDES_PatchCommandThread_ListenerMutex
+#define EXCLUDES_PassthruPatchRecord_ReadMutex \
+    EXCLUDES(android::audio_utils::PassthruPatchRecord_ReadMutex) \
+    EXCLUDES_BELOW_PassthruPatchRecord_ReadMutex
+
+#define EXCLUDES_BELOW_OutputTrack_TrackMetadataMutex \
+    EXCLUDES_PassthruPatchRecord_ReadMutex
+#define EXCLUDES_OutputTrack_TrackMetadataMutex \
+    EXCLUDES(android::audio_utils::OutputTrack_TrackMetadataMutex) \
+    EXCLUDES_BELOW_OutputTrack_TrackMetadataMutex
+
+#define EXCLUDES_BELOW_ConfigEvent_Mutex \
+    EXCLUDES_OutputTrack_TrackMetadataMutex
+#define EXCLUDES_ConfigEvent_Mutex \
+    EXCLUDES(android::audio_utils::ConfigEvent_Mutex) \
+    EXCLUDES_BELOW_ConfigEvent_Mutex
+
+#define EXCLUDES_BELOW_AsyncCallbackThread_Mutex \
+    EXCLUDES_ConfigEvent_Mutex
+#define EXCLUDES_AsyncCallbackThread_Mutex \
+    EXCLUDES(android::audio_utils::AsyncCallbackThread_Mutex) \
+    EXCLUDES_BELOW_AsyncCallbackThread_Mutex
+
+#define EXCLUDES_BELOW_AudioFlinger_UnregisteredWritersMutex \
+    EXCLUDES_AsyncCallbackThread_Mutex
+#define EXCLUDES_AudioFlinger_UnregisteredWritersMutex \
+    EXCLUDES(android::audio_utils::AudioFlinger_UnregisteredWritersMutex) \
+    EXCLUDES_BELOW_AudioFlinger_UnregisteredWritersMutex
+
+#define EXCLUDES_BELOW_EffectBase_Mutex \
+    EXCLUDES_AudioFlinger_UnregisteredWritersMutex
+#define EXCLUDES_EffectBase_Mutex \
+    EXCLUDES(android::audio_utils::EffectBase_Mutex) \
+    EXCLUDES_BELOW_EffectBase_Mutex
+
+#define EXCLUDES_BELOW_DeviceEffectProxy_ProxyMutex \
+    EXCLUDES_EffectBase_Mutex
+#define EXCLUDES_DeviceEffectProxy_ProxyMutex \
+    EXCLUDES(android::audio_utils::DeviceEffectProxy_ProxyMutex) \
+    EXCLUDES_BELOW_DeviceEffectProxy_ProxyMutex
+
+#define EXCLUDES_BELOW_EffectChain_Mutex \
+    EXCLUDES_DeviceEffectProxy_ProxyMutex
+#define EXCLUDES_EffectChain_Mutex \
+    EXCLUDES(android::audio_utils::EffectChain_Mutex) \
+    EXCLUDES_BELOW_EffectChain_Mutex
+
+#define EXCLUDES_BELOW_MelReporter_Mutex \
+    EXCLUDES_EffectChain_Mutex
+#define EXCLUDES_MelReporter_Mutex \
+    EXCLUDES(android::audio_utils::MelReporter_Mutex) \
+    EXCLUDES_BELOW_MelReporter_Mutex
+
+#define EXCLUDES_BELOW_AudioFlinger_ClientMutex \
+    EXCLUDES_MelReporter_Mutex
+#define EXCLUDES_AudioFlinger_ClientMutex \
+    EXCLUDES(android::audio_utils::AudioFlinger_ClientMutex) \
+    EXCLUDES_BELOW_AudioFlinger_ClientMutex
+
+#define EXCLUDES_BELOW_ThreadBase_Mutex \
+    EXCLUDES_AudioFlinger_ClientMutex
+#define EXCLUDES_ThreadBase_Mutex \
+    EXCLUDES(android::audio_utils::ThreadBase_Mutex) \
+    EXCLUDES_BELOW_ThreadBase_Mutex
+
+#define EXCLUDES_BELOW_PatchCommandThread_Mutex \
+    EXCLUDES_ThreadBase_Mutex
+#define EXCLUDES_PatchCommandThread_Mutex \
+    EXCLUDES(android::audio_utils::PatchCommandThread_Mutex) \
+    EXCLUDES_BELOW_PatchCommandThread_Mutex
+
+#define EXCLUDES_BELOW_DeviceEffectManager_Mutex \
+    EXCLUDES_PatchCommandThread_Mutex
+#define EXCLUDES_DeviceEffectManager_Mutex \
+    EXCLUDES(android::audio_utils::DeviceEffectManager_Mutex) \
+    EXCLUDES_BELOW_DeviceEffectManager_Mutex
+
+#define EXCLUDES_BELOW_AudioFlinger_HardwareMutex \
+    EXCLUDES_DeviceEffectManager_Mutex
+#define EXCLUDES_AudioFlinger_HardwareMutex \
+    EXCLUDES(android::audio_utils::AudioFlinger_HardwareMutex) \
+    EXCLUDES_BELOW_AudioFlinger_HardwareMutex
+
+#define EXCLUDES_BELOW_AudioFlinger_Mutex \
+    EXCLUDES_AudioFlinger_HardwareMutex
+#define EXCLUDES_AudioFlinger_Mutex \
+    EXCLUDES(android::audio_utils::AudioFlinger_Mutex) \
+    EXCLUDES_BELOW_AudioFlinger_Mutex
+
+#define EXCLUDES_BELOW_EffectBase_PolicyMutex \
+    EXCLUDES_AudioFlinger_Mutex
+#define EXCLUDES_EffectBase_PolicyMutex \
+    EXCLUDES(android::audio_utils::EffectBase_PolicyMutex) \
+    EXCLUDES_BELOW_EffectBase_PolicyMutex
+
+#define EXCLUDES_BELOW_EffectHandle_Mutex \
+    EXCLUDES_EffectBase_PolicyMutex
+#define EXCLUDES_EffectHandle_Mutex \
+    EXCLUDES(android::audio_utils::EffectHandle_Mutex) \
+    EXCLUDES_BELOW_EffectHandle_Mutex
+
+#define EXCLUDES_AUDIO_ALL \
+    EXCLUDES_EffectHandle_Mutex
+
+// --- End generated section
+
+/**
+ * AudioMutexAttributes is a collection of types and constexpr configuration
+ * used for the Android audio mutex.
+ *
+ * A different AudioMutexAttributes configuration will instantiate a completely
+ * independent set of mutex strategies, statics and thread locals,
+ * for a different type of mutexes.
+ */
+
+class AudioMutexAttributes {
+public:
+    // Order types, name arrays.
+    using order_t = MutexOrder;
+    static constexpr auto& order_names_ = gMutexNames;
+    static constexpr size_t order_size_ = static_cast<size_t>(MutexOrder::kSize);
+    static constexpr order_t order_default_ = MutexOrder::kOtherMutex;
+
+    // verify order information
+    static_assert(std::size(order_names_) == order_size_);
+    static_assert(static_cast<size_t>(order_default_) < order_size_);
+
+    // Set mutex_tracking_enabled_ to true to enable mutex
+    // statistics and debugging (order checking) features.
+    static constexpr bool mutex_tracking_enabled_ = true;
+
+    // Control the depth of the mutex stack per thread (the mutexes
+    // we track).  Set this to the maximum expected
+    // number of mutexes held by a thread.  If the depth is too small,
+    // deadlock detection, order checking, and recursion checking
+    // may result in a false negative.  This is a static configuration
+    // because reallocating memory for the stack requires a lock for
+    // the reader.
+    static constexpr size_t mutex_stack_depth_ = 16;
+
+    // Enable or disable log always fatal.
+    // This also requires the mutex feature flag to be set.
+    static constexpr bool abort_on_order_check_ = true;
+    static constexpr bool abort_on_recursion_check_ = true;
+    static constexpr bool abort_on_invalid_unlock_ = true;
+};
+
+/**
+ * Helper method to accumulate floating point values to an atomic
+ * prior to C++23 support of atomic<float> atomic<double> accumulation.
+ */
+template <typename AccumulateType, typename ValueType>
+void atomic_add_to(std::atomic<AccumulateType> &dst, ValueType src) {
+    static_assert(std::atomic<AccumulateType>::is_always_lock_free);
+    AccumulateType expected;
+    do {
+        expected = dst;
+    } while (!dst.compare_exchange_weak(expected, expected + src));
+}
+
+/**
+ * mutex_stat is a struct composed of atomic members associated
+ * with usage of a particular mutex order.
+ *
+ * Access of a snapshot of this does not have a global lock, so the reader
+ * may experience temporal shear. Use of this by a different reader thread
+ * is for informative purposes only.
+ */
+
+// CounterType == uint64_t, AccumulatorType == double
+template <typename CounterType, typename AccumulatorType>
+struct mutex_stat {
+    static_assert(std::is_floating_point_v<AccumulatorType>);
+    static_assert(std::is_integral_v<CounterType>);
+    std::atomic<CounterType> locks = 0;        // number of times locked
+    std::atomic<CounterType> unlocks = 0;      // number of times unlocked
+    std::atomic<CounterType> waits = 0;         // number of locks that waitedwa
+    std::atomic<AccumulatorType> wait_sum_ns = 0.;    // sum of time waited.
+    std::atomic<AccumulatorType> wait_sumsq_ns = 0.;  // sumsq of time waited.
+
+    template <typename WaitTimeType>
+    void add_wait_time(WaitTimeType wait_ns) {
+        AccumulatorType value_ns = wait_ns;
+        atomic_add_to(wait_sum_ns, value_ns);
+        atomic_add_to(wait_sumsq_ns, value_ns * value_ns);
+    }
+
+    std::string to_string() const {
+        CounterType uncontested = locks - waits;
+        AccumulatorType recip = waits == 0 ? 0. : 1. / waits;
+        AccumulatorType avg_wait_ms = waits == 0 ? 0. : wait_sum_ns * 1e-6 * recip;
+        AccumulatorType std_wait_ms = waits < 2 ? 0. :
+                std::sqrt(wait_sumsq_ns * recip * 1e-12 - avg_wait_ms * avg_wait_ms);
+        return std::string("locks: ").append(std::to_string(locks))
+            .append("\nuncontested: ").append(std::to_string(uncontested))
+            .append("\nwaits: ").append(std::to_string(waits))
+            .append("\nunlocks: ").append(std::to_string(unlocks))
+            .append("\navg_wait_ms: ").append(std::to_string(avg_wait_ms))
+            .append("\nstd_wait_ms: ").append(std::to_string(std_wait_ms))
+            .append("\n");
+    }
+};
+
+/**
+ * atomic_stack is a single writer, multiple reader object.
+ * Readers not on the same thread as the writer may experience temporal shear,
+ * but individual members are accessed atomic-safe, i.e. no partial member
+ * reads or delayed writes due to caching.
+ *
+ * For use with mutex checking, the atomic_stack maintains an ordering on
+ * P (payload) such that the top item pushed must always be greater than or
+ * equal to the P (payload) of items below it.
+ *
+ * Pushes always go to the top of the stack.  Removes can occur
+ * from any place in the stack, but typically near the top.
+ *
+ * The atomic_stack never reallocates beyond its fixed capacity of N.
+ * This prevents a lockless reader from accessing invalid memory because
+ * the address region does not change.
+ *
+ * If the number of pushes exceed the capacity N, then items may be discarded.
+ * In that case, the stack is a subset stack of the "true" unlimited
+ * capacity stack.  Nevertheless, a subset of an ordered stack
+ * with items deleted is also ordered.
+ *
+ * The size() of the atomic_stack is the size of the subset stack of tracked items.
+ * The true_size() is the size of the number of items pushed minus the
+ * number of items removed (the "true" size if capacity were unlimited).
+ * Since the capacity() is constant the true_size() may include
+ * items we don't track except by count.  If true_size() == size() then
+ * the subset stack is complete.
+ *
+ * In this single writer, multiple reader model, we could get away with
+ * memory_order_relaxed as the reader is purely informative,
+ * but we choose memory_order_seq_cst which imposes the most
+ * restrictions on the compiler (variable access reordering) and the
+ * processor (memory access reordering).  This means operations take effect
+ * in the order written.  However, this isn't strictly needed - as there is
+ * only one writer, a read-modify-write operation is safe (no need for special
+ * memory instructions), and there isn't the acquire-release semantics with
+ * non-atomic memory access needed for a lockless fifo, for example.
+ */
+
+ /*
+  * For audio mutex purposes, one question arises - why don't we use
+  * a bitmask to represent the capabilities taken by a thread
+  * instead of a stack?
+  *
+  * A bitmask arrangement works if there exists a one-to-one relationship
+  * from a physical mutex to its capability.  That may exist for some
+  * projects, but not AudioFlinger.
+  *
+  * As a consequence, we need the actual count and handle:
+  *
+  * 1) A single thread may hold multiple instances of some capabilities
+  * (e.g. ThreadBase_Mutex and EffectChain_Mutex).
+  * For example there may be multiple effect chains locked during mixing.
+  * There may be multiple PlaybackThreads locked during effect chain movement.
+  * A bit per capability can't count beyond 1.
+  *
+  * 2) Deadlock detection requires tracking the actual MutexHandle (a void*)
+  * to form a cycle, because there may be many mutexes associated with a
+  * given capability order.
+  * For example, each PlaybackThread or RecordThread will have its own mutex
+  * with the ThreadBase_Mutex capability.
+  *
+  */
+
+template <typename Item, typename Payload, size_t N>
+class atomic_stack {
+public:
+    using item_payload_pair_t = std::pair<std::atomic<Item>, std::atomic<Payload>>;
+
+    /**
+     * Puts the item at the top of the stack.
+     *
+     * If the stack depth is exceeded the item
+     * replaces the top.
+     *
+     * Mutexes when locked are always placed on the top of the stack;
+     * however, they may be unlocked in a non last-in-first-out (LIFO)
+     * order.  It is rare to see a non LIFO order, but it can happen.
+     */
+    void push(const Item& item, const Payload& payload) {
+        size_t location = top_;
+        size_t increment = 1;
+        if (location >= N) {
+            // we exceed the top of stack.
+            //
+            // although we could ignore this item (subset is the oldest),
+            // the better solution is to replace the topmost entry as
+            // it allows quicker removal.
+            location = N - 1;
+            increment = 0;
+        }
+        // issue the operations close together.
+        pairs_[location].first = item;
+        pairs_[location].second = payload;
+        ++true_top_;
+        top_ += increment;
+    }
+
+    /**
+     * Removes the item which is expected at the top of the stack
+     * but may be lower.  Mutexes are generally unlocked in stack
+     * order (LIFO), but this is not a strict requirement.
+     */
+    bool remove(const Item& item) {
+        if (true_top_ == 0) {
+            return false;  // cannot remove.
+        }
+        // there is a temporary benign read race here where true_top_ != top_.
+        --true_top_;
+        for (size_t i = top_; i > 0; ) {
+            if (item == pairs_[--i].first) {
+                // We shift to preserve order.
+                // A reader may temporarily see a "duplicate" entry
+                // but that is preferable to a "missing" entry
+                // for the purposes of deadlock detection.
+                const size_t limit = top_ - 1;
+                while (i < limit) {  // using atomics, we need to assign first, second separately.
+                    pairs_[i].first = pairs_[i + 1].first.load();
+                    pairs_[i].second = pairs_[i + 1].second.load();
+                    ++i;
+                }
+                --top_; // now we restrict our range.
+                // on relaxed semantics, it might be better to clear out the last
+                // pair, but we are seq_cst.
+                return true;
+            }
+        }
+        // not found in our subset.
+        //
+        // we return true upon correct removal (true_top_ must always be >= top_).
+        if (true_top_ >= top_) return true;
+
+        // else recover and return false to notify that removal was invalid.
+        true_top_ = top_.load();
+        return false;
+    }
+
+    /**
+     * return the top of our atomic subset stack
+     * or the invalid_ (zero-initialized) entry if it doesn't exist.
+     */
+    // Consideration of using std::optional<> is a possibility
+    // but as std::atomic doesn't have a copy ctor (and does not make sense),
+    // we would want to directly return an optional on the non-atomic values,
+    // in a custom pair.
+    const item_payload_pair_t& top(size_t offset = 0) const {
+        const ssize_t top = static_cast<ssize_t>(top_) - static_cast<ssize_t>(offset);
+        if (top > 0 && top <= static_cast<ssize_t>(N)) return pairs_[top - 1];
+        return invalid_;  // we don't know anything.
+    }
+
+    /**
+     * return the bottom (or base) of our atomic subset stack
+     * or the invalid_ (zero-initialized) entry if it doesn't exist.
+     */
+    const item_payload_pair_t& bottom(size_t offset = 0) const {
+        if (offset < top_) return pairs_[offset];
+        return invalid_;  // we don't know anything.
+    }
+
+    /**
+     * prints the contents of the stack starting from the most recent first.
+     *
+     * If the thread is not the same as the writer thread, there could be
+     * temporal shear in the data printed.
+     */
+    std::string to_string() const {
+        std::string s("size: ");
+        s.append(std::to_string(size()))
+            .append(" true_size: ").append(std::to_string(true_size()))
+            .append(" items: [");
+        for (size_t i = 0; i < top_; ++i) {
+            s.append("{ ")
+            .append(std::to_string(reinterpret_cast<uintptr_t>(pairs_[i].first.load())))
+            .append(", ")
+            .append(std::to_string(static_cast<size_t>(pairs_[i].second.load())))
+            .append(" } ");
+        }
+        s.append("]");
+        return s;
+    }
+
+   /*
+    * stack configuration
+    */
+    static size_t capacity() { return N; }
+    size_t true_size() const { return true_top_; }
+    size_t size() const { return top_; }
+    const auto& invalid() const { return invalid_; }
+
+private:
+    std::atomic<size_t> top_ = 0;       // ranges from 0 to N - 1
+    std::atomic<size_t> true_top_ = 0;  // always >= top_.
+    // if true_top_ == top_ the subset stack is complete.
+
+    /*
+     * The subset stack entries are a pair of atomics rather than an atomic<pair>
+     * to prevent lock requirements if T and P are small enough, i.e. <= sizeof(size_t).
+     *
+     * As atomics are not composable from smaller atomics, there may be some
+     * temporary inconsistencies when reading from a different thread than the writer.
+     */
+    item_payload_pair_t pairs_[N]{};
+
+    /*
+     * The invalid pair is returned when top() is called without a tracked item.
+     * This might occur with an empty subset of the "true" stack.
+     */
+    static constexpr item_payload_pair_t invalid_{};
+};
+
+/**
+ * thread_mutex_info is a struct that is associated with every
+ * thread the first time a mutex is used on it.  Writing will be through
+ * a single thread (essentially thread_local), but the thread_registry
+ * debug methods may access this through a different reader thread.
+ *
+ * If the thread does not use the audio_utils mutex, the allocation of this
+ * struct never occurs, although there is approx 16 bytes for a shared ptr and
+ * 1 byte for a thread local once bool.
+ *
+ * Here, we use for the MutexHandle a void*, which is used as an opaque unique ID
+ * representing the mutex.
+ *
+ * Since there is no global locking, the validity of the mutex* associated to
+ * the void* is unknown -- the mutex* could be deallocated in a different
+ * thread.  Nevertheless the opaque ID can still be used to check deadlocks
+ * realizing there could be a false positive on a potential reader race
+ * where a new mutex is created at the same storage location.
+ */
+template <typename MutexHandle, typename Order, size_t N>
+class thread_mutex_info {
+public:
+    using atomic_stack_t = atomic_stack<MutexHandle, Order, N>;
+
+    thread_mutex_info(pid_t tid) : tid_(tid) {}
+
+    // the destructor releases the thread_mutex_info.
+    // declared here, defined below due to use of thread_registry.
+    ~thread_mutex_info();
+
+    void reset_waiter(MutexHandle waiter = nullptr) {
+        mutex_wait_ = waiter;
+    }
+
+    /**
+     * check_held returns the stack pair that conflicts
+     * with the existing mutex handle and order, or the invalid
+     * stack pair (empty mutex handle and empty order).
+     */
+    const typename atomic_stack_t::item_payload_pair_t&
+    check_held(MutexHandle mutex, Order order) const {
+        // validate mutex order.
+        const size_t size = mutexes_held_.size();
+        for (size_t i = 0; i < size; ++i) {
+            const auto& top = mutexes_held_.top(i);
+            const auto top_order = top.second.load();
+
+            if (top_order < order) break;              // ok
+            if (top_order > order) return top;         // inverted order
+            if (top.first.load() == mutex) return top; // recursive mutex
+        }
+        return mutexes_held_.invalid();
+    }
+
+    /*
+     * This is unverified push.  Use check_held() prior to this to
+     * verify no lock inversion or replication.
+     */
+    void push_held(MutexHandle mutex, Order order) {
+        mutexes_held_.push(mutex, order);
+    }
+
+    bool remove_held(MutexHandle mutex) {
+        return mutexes_held_.remove(mutex);
+    }
+
+    /*
+     * Due to the fact that the thread_mutex_info contents are not globally locked,
+     * there may be temporal shear.  The string representation is
+     * informative only.
+     */
+    std::string to_string() const {
+        std::string s;
+        s.append("tid: ").append(std::to_string(static_cast<int>(tid_)));
+        s.append("\nwaiting: ").append(std::to_string(
+                reinterpret_cast<uintptr_t>(mutex_wait_.load())));
+        s.append("\nheld: ").append(mutexes_held_.to_string());
+        return s;
+    }
+
+    /*
+     * empty() indicates that the thread is not waiting for or
+     * holding any mutexes.
+     */
+    bool empty() const {
+        return mutex_wait_ == nullptr && mutexes_held_.size() == 0;
+    }
+
+    const auto& stack() const {
+        return mutexes_held_;
+    }
+
+    const pid_t tid_;                                   // me
+    std::atomic<MutexHandle> mutex_wait_{};             // mutex waiting for
+    atomic_stack_t mutexes_held_;  // mutexes held
+};
+
+
+/**
+ * deadlock_info_t encapsulates the mutex wait / cycle information from
+ * thread_registry::deadlock_detection().
+ *
+ * If a cycle is detected, the last element of the vector chain represents
+ * a tid that is repeated somewhere earlier in the vector.
+ */
+struct deadlock_info_t {
+public:
+    explicit deadlock_info_t(pid_t tid_param) : tid(tid_param) {}
+
+    bool empty() const {
+        return chain.empty();
+    }
+
+    std::string to_string() const {
+        std::string description;
+
+        if (has_cycle) {
+            description.append("mutex cycle found (last tid repeated) ");
+        } else {
+            description.append("mutex wait chain ");
+        }
+        description.append("[ ").append(std::to_string(tid));
+        // Note: when we dump here, we add the timeout tid to the start of the wait chain.
+        for (const auto& [ tid2, name ] : chain) {
+            description.append(", ").append(std::to_string(tid2))
+                    .append(" (holding ").append(name).append(")");
+        }
+        description.append(" ]");
+        return description;
+    }
+
+    const pid_t tid;         // tid for which the deadlock was checked
+    bool has_cycle = false;  // true if there is a cycle detected
+    std::vector<std::pair<pid_t, std::string>> chain;  // wait chain of tids and mutexes.
+};
+
+/**
+ * The thread_registry is a thread-safe locked structure that
+ * maintains a list of the threads that contain thread_mutex_info.
+ *
+ * Only first mutex access from a new thread and the destruction of that
+ * thread will trigger an access to the thread_registry map.
+ *
+ * The debug methods to_string() and deadlock_detection() will also lock the struct
+ * long enough to copy the map and safely obtain the weak pointers,
+ * and then deal with the thread local data afterwards.
+ *
+ * It is recommended to keep a static singleton of the thread_registry for the
+ * type desired.  The singleton should be associated properly with the object
+ * it should be unique for, which in this case is the mutex_impl template.
+ * This enables access to the elements as needed.
+ */
+template <typename ThreadInfo>
+class thread_registry {
+public:
+    bool add_to_registry(const std::shared_ptr<ThreadInfo>& tminfo) EXCLUDES(mutex_) {
+        ALOGV("%s: registered for %d", __func__, tminfo->tid_);
+        std::lock_guard l(mutex_);
+        if (registry_.count(tminfo->tid_) > 0) {
+            ALOGW_IF("%s: tid %d already exists", __func__, tminfo->tid_);
+            return false;
+        }
+        registry_[tminfo->tid_] = tminfo;
+        return true;
+    }
+
+    bool remove_from_registry(pid_t tid) EXCLUDES(mutex_) {
+        ALOGV("%s: unregistered for %d", __func__, tid);
+        std::lock_guard l(mutex_);
+        // don't crash here because it might be a test app.
+        const bool success = registry_.erase(tid) == 1;
+        ALOGW_IF(!success, "%s: Cannot find entry for tid:%d", __func__, tid);
+        return success;
+    }
+
+    // Returns a std::unordered_map for easy access on tid.
+    auto copy_map() EXCLUDES(mutex_) {
+        std::lock_guard l(mutex_);
+        return registry_;
+    }
+
+    // Returns a std::map sorted on tid for easy debug reading.
+    auto copy_ordered_map() EXCLUDES(mutex_) {
+        std::lock_guard l(mutex_);
+        std::map<pid_t, std::weak_ptr<ThreadInfo>> sorted(registry_.begin(), registry_.end());
+        return sorted;
+    }
+
+    /**
+     * Returns a string containing the thread mutex info for each
+     * thread that has accessed the audio_utils mutex.
+     */
+    std::string to_string() {
+        // for debug purposes it is much easier to see the tids in numeric order.
+        const auto registry_map = copy_ordered_map();
+        ALOGV("%s: dumping tids: %zu", __func__, registry_map.size());
+        std::string s("thread count: ");
+        s.append(std::to_string(registry_map.size())).append("\n");
+
+        std::vector<pid_t> empty;
+        for (const auto& [tid, weak_info] : registry_map) {
+            const auto info = weak_info.lock();
+            if (info) {
+                if (info->empty()) {
+                    empty.push_back(tid);
+                } else {
+                    s.append(info->to_string()).append("\n");
+                }
+            }
+        }
+
+        // dump remaining empty tids out
+        s.append("tids without current activity [ ");
+        for (const auto tid : empty) {
+            s.append(std::to_string(tid)).append(" ");
+        }
+        s.append("]\n");
+        return s;
+    }
+
+    /**
+     * Returns the tid mutex pointer (a void*) if it is waiting.
+     *
+     * It should use a copy of the registry map which is not changing
+     * as it does not take any lock.
+     */
+    static void* tid_to_mutex_wait(
+            const std::unordered_map<pid_t, std::weak_ptr<ThreadInfo>>& registry_map,
+            pid_t tid) {
+        const auto it = registry_map.find(tid);
+        if (it == registry_map.end()) return {};
+        const auto& weak_info = it->second;  // unmapped returns empty weak_ptr.
+        const auto info = weak_info.lock();
+        if (!info) return {};
+        return info->mutex_wait_.load();
+    }
+
+    /**
+     * Returns a deadlock_info_t struct describing the mutex wait / cycle information.
+     *
+     * The deadlock_detection() method is not exceptionally fast
+     * and is not designed to be called for every mutex locked (and contended).
+     * It is designed to run as a diagnostic routine to enhance
+     * dumping for watchdogs, like TimeCheck, when a tid is believed blocked.
+     *
+     * Access of state is through atomics, so has minimal overhead on
+     * concurrent execution, with the possibility of (mostly) false
+     * negatives due to race.
+     *
+     * \param tid target tid which may be in a cycle or blocked.
+     * \param mutex_names a string array of mutex names indexed on capability order.
+     * \return a deadlock_info_t struct, which contains whether a cycle was found and
+     *         a vector of tids and mutex names in the mutex wait chain.
+     */
+    template <typename StringArray>
+    deadlock_info_t deadlock_detection(pid_t tid, const StringArray& mutex_names) {
+        const auto registry_map = copy_map();
+        deadlock_info_t deadlock_info{tid};
+
+        // if tid not waiting, return.
+        void* m = tid_to_mutex_wait(registry_map, tid);
+        if (m == nullptr) return deadlock_info;
+
+        bool subset = false; // do we have missing mutex data per thread?
+
+        // Create helper map from mutex to tid.
+        //
+        // The helper map is built up from thread_local info rather than from
+        // a global mutex list.
+        //
+        // There are multiple reasons behind this.
+        // 1) There are many mutexes (mostly not held). We don't want to keep and
+        //    manage a "global" list of them.
+        // 2) The mutex pointer itself may be deallocated from a different thread
+        //    from the reader. To keep it alive requires either a mutex, or a
+        //    weak_ptr to shared_ptr promotion.
+        //    Lifetime management is expensive on a per-mutex basis as there are many
+        //    of them, but cheaper on a per-thread basis as the threads are fewer.
+        // 3) The thread_local lookup is very inexpensive for thread info (special
+        //    acceleration by C++ and the OS), but more complex for a mutex list
+        //    which at best is a static concurrent hash map.
+        //
+        // Note that the mutex_ptr handle is opaque -- it may be deallocated from
+        // a different thread, so we use the tid from the thread registry map.
+        //
+        using pid_order_index_pair_t = std::pair<pid_t, size_t>;
+        std::unordered_map<void*, pid_order_index_pair_t> mutex_to_tid;
+        for (const auto& [tid2, weak_info] : registry_map) {
+            const auto info = weak_info.lock();
+            if (info == nullptr) continue;
+            const auto& stack = info->mutexes_held_;
+            subset = subset || stack.size() != stack.true_size();
+            for (size_t i = 0; i < stack.size(); ++i) {
+                const auto& mutex_order_pair = stack.bottom(i);
+                // if this method is not called by the writer thread
+                // it is possible for data to change.
+                const auto mutex_ptr = mutex_order_pair.first.load();
+                const auto order = static_cast<size_t>(mutex_order_pair.second.load());
+                if (mutex_ptr != nullptr) {
+                    mutex_to_tid[mutex_ptr] = pid_order_index_pair_t{tid2, order};
+                }
+            }
+        }
+        ALOGD_IF(subset, "%s: mutex info only subset, deadlock detection may be inaccurate",
+                __func__);
+
+        // traverse from tid -> waiting mutex, then
+        // mutex -> tid holding
+        // until we get no more tids, or a tid cycle.
+        std::unordered_set<pid_t> visited;
+        visited.insert(tid);  // mark the original tid, we start there for cycle detection.
+        while (true) {
+            // no tid associated with the mutex.
+            if (mutex_to_tid.count(m) == 0) return deadlock_info;
+            const auto [tid2, order] = mutex_to_tid[m];
+
+            // add to chain.
+            const auto name = order < std::size(mutex_names) ? mutex_names[order] : "unknown";
+            deadlock_info.chain.emplace_back(tid2, name);
+
+            // cycle detected
+            if (visited.count(tid2)) {
+                deadlock_info.has_cycle = true;
+                return deadlock_info;
+            }
+            visited.insert(tid2);
+
+            // if tid not waiting return (could be blocked on binder).
+            m = tid_to_mutex_wait(registry_map, tid2);
+            if (m == nullptr) return deadlock_info;
+        }
+    }
+
+private:
+    mutable std::mutex mutex_;
+    std::unordered_map<pid_t, std::weak_ptr<ThreadInfo>> registry_ GUARDED_BY(mutex_);
+};
+
 // audio_utils::mutex, audio_utils::lock_guard, audio_utils::unique_lock,
 // and audio_utils::condition_variable are method compatible versions
 // of std::mutex, std::lock_guard, std::unique_lock, and std::condition_variable
@@ -36,14 +934,24 @@
 // is inefficient.  One is better off making a custom timed implementation using
 // pthread_mutex_timedlock() on the mutex::native_handle().
 
-class CAPABILITY("mutex") mutex {
+extern bool mutex_get_enable_flag();
+
+template <typename Attributes>
+class CAPABILITY("mutex") mutex_impl {
 public:
-    static constexpr bool kPriorityInheritance = false;
+    using attributes_t = Attributes;
 
     // We use composition here.
     // No copy/move ctors as the member std::mutex has it deleted.
-    mutex() {
-        if constexpr (!kPriorityInheritance) return;
+    mutex_impl(typename Attributes::order_t order = Attributes::order_default_)
+        : order_(order)
+        , stat_{get_mutex_stat_array()[static_cast<size_t>(order)]}
+    {
+        LOG_ALWAYS_FATAL_IF(static_cast<size_t>(order) >= Attributes::order_size_,
+                "mutex order %u is equal to or greater than order limit:%zu",
+                order, Attributes::order_size_);
+
+        if (!mutex_get_enable_flag()) return;
 
         pthread_mutexattr_t attr;
         int ret = pthread_mutexattr_init(&attr);
@@ -59,12 +967,16 @@
         }
 
         // use of the native_handle() is implementation defined.
-        auto handle = m_.native_handle();
+        const auto handle = m_.native_handle();
         ret = pthread_mutex_init(handle, &attr);
         if (ret != 0) {
             ALOGW("%s, pthread_mutex_init returned %d", __func__, ret);
         }
-        ALOGV("%s: audio_mutex initialized: %d", __func__, ret);
+        ALOGV("%s: audio_mutex initialized: ret:%d  order:%u", __func__, ret, order_);
+    }
+
+    ~mutex_impl() {
+        // Note: std::mutex behavior is undefined if released holding ownership.
     }
 
     auto native_handle() {
@@ -72,15 +984,38 @@
     }
 
     void lock() ACQUIRE() {
-        m_.lock();
+        lock_scoped_stat_t::pre_lock(*this);
+        if (!m_.try_lock()) {  // if we directly use futex, we can optimize this with m_.lock().
+            // lock_scoped_stat_t accumulates waiting time for the mutex lock call.
+            lock_scoped_stat_t ls(*this);
+            m_.lock();
+        }
+        lock_scoped_stat_t::post_lock(*this);
     }
 
     void unlock() RELEASE() {
+        lock_scoped_stat_t::pre_unlock(*this);
         m_.unlock();
     }
 
-    bool try_lock() TRY_ACQUIRE(true) {
-        return m_.try_lock();
+    bool try_lock(int64_t timeout_ns = 0) TRY_ACQUIRE(true) {
+        lock_scoped_stat_t::pre_lock(*this);
+        if (timeout_ns <= 0) {
+            if (!m_.try_lock()) return false;
+        } else {
+            const int64_t deadline_ns = timeout_ns + systemTime(SYSTEM_TIME_REALTIME);
+            const struct timespec ts = {
+                .tv_sec = static_cast<time_t>(deadline_ns / 1'000'000'000),
+                .tv_nsec = static_cast<long>(deadline_ns % 1'000'000'000),
+            };
+            lock_scoped_stat_t ls(*this);
+            if (pthread_mutex_timedlock(m_.native_handle(), &ts) != 0) {
+                ls.ignoreWaitTime();  // didn't get lock, don't count wait time
+                return false;
+            }
+        }
+        lock_scoped_stat_t::post_lock(*this);
+        return true;
     }
 
     // additional method to obtain the underlying std::mutex.
@@ -88,10 +1023,216 @@
         return m_;
     }
 
+    using mutex_stat_t = mutex_stat<uint64_t, double>;
+
+    mutex_stat_t& get_stat() const {
+        return stat_;
+    }
+
+    /**
+     * Returns the locking statistics per mutex capability category.
+     */
+    static std::string all_stats_to_string() {
+        std::string out("mutex stats: priority inheritance ");
+        out.append(mutex_get_enable_flag() ? "enabled" : "disabled")
+            .append("\n");
+        const auto& stat_array = get_mutex_stat_array();
+        for (size_t i = 0; i < stat_array.size(); ++i) {
+            if (stat_array[i].locks != 0) {
+                out.append("Capability: ").append(Attributes::order_names_[i]).append("\n")
+                    .append(stat_array[i].to_string());
+            }
+        }
+        return out;
+    }
+
+    /**
+     * Returns the thread locks held per tid.
+     */
+    static std::string all_threads_to_string() {
+        return get_registry().to_string();
+    }
+
+    /**
+     * Returns a pair of bool (whether a cycle is detected) and a vector
+     * of mutex wait dependencies.
+     *
+     * If a cycle is detected, the last element of the vector represents
+     * a tid that is repeated somewhere earlier in the vector.
+     *
+     * The deadlock_detection() method is not exceptionally fast
+     * and is not designed to be called for every mutex locked (and contended).
+     * It is designed to run as a diagnostic routine to enhance
+     * dumping for watchdogs, like TimeCheck, when a tid is believed blocked.
+     *
+     * Access of state is through atomics, so has minimal overhead on
+     * concurrent execution, with the possibility of (mostly) false
+     * negatives due to race.
+     */
+    static deadlock_info_t
+    deadlock_detection(pid_t tid) {
+        return get_registry().deadlock_detection(tid, Attributes::order_names_);
+    }
+
+    using thread_mutex_info_t = thread_mutex_info<
+            void* /* mutex handle */, MutexOrder, Attributes::mutex_stack_depth_>;
+
+    // get_thread_mutex_info is a thread-local "singleton".
+    //
+    // We write it like a Meyer's singleton with a single thread_local
+    // assignment that is guaranteed to be called on first time initialization.
+    // Since the variables are thread_local, there is no thread contention
+    // for initialization that would happen with a traditional Meyer's singleton,
+    // so really a simple thread-local bool will do for a once_flag.
+    static const std::shared_ptr<thread_mutex_info_t>& get_thread_mutex_info() {
+        thread_local std::shared_ptr<thread_mutex_info_t> tminfo = []() {
+            auto info = std::make_shared<thread_mutex_info_t>(gettid_wrapper());
+            get_registry().add_to_registry(info);
+            return info;
+        }();
+        return tminfo;
+    }
+
+    // helper class for registering statistics for a mutex lock.
+
+    class lock_scoped_stat_enabled {
+    public:
+        explicit lock_scoped_stat_enabled(mutex& m)
+            : mutex_(m)
+            , time_(systemTime()) {
+           ++mutex_.stat_.waits;
+           mutex_.get_thread_mutex_info()->reset_waiter(&mutex_);
+        }
+
+        ~lock_scoped_stat_enabled() {
+           if (!discard_wait_time_) mutex_.stat_.add_wait_time(systemTime() - time_);
+           mutex_.get_thread_mutex_info()->reset_waiter();
+        }
+
+        void ignoreWaitTime() {
+            discard_wait_time_ = true;
+        }
+
+        static void pre_unlock(mutex& m) {
+            ++m.stat_.unlocks;
+            const bool success = m.get_thread_mutex_info()->remove_held(&m);
+            LOG_ALWAYS_FATAL_IF(Attributes::abort_on_invalid_unlock_
+                    && mutex_get_enable_flag()
+                    && !success,
+                    "%s: invalid mutex unlock when not previously held", __func__);
+        }
+
+        // before we lock, we check order and recursion.
+        static void pre_lock(mutex& m) {
+            if constexpr (!Attributes::abort_on_order_check_ &&
+                    !Attributes::abort_on_recursion_check_) return;
+
+            const auto& p = m.get_thread_mutex_info()->check_held(&m, m.order_);
+            if (p.first == nullptr) return;  // no problematic mutex.
+
+            // problem!
+            const size_t p_order = static_cast<size_t>(p.second.load());
+            const size_t m_order = static_cast<size_t>(m.order_);
+
+            // lock inversion
+            LOG_ALWAYS_FATAL_IF(Attributes::abort_on_order_check_
+                    && mutex_get_enable_flag()
+                    && p_order > m_order,
+                    "%s: invalid mutex order (previous) %zu %s> (new) %zu %s",
+                    __func__, p_order, Attributes::order_names_[p_order],
+                    m_order, Attributes::order_names_[m_order]);
+
+            // lock recursion
+            LOG_ALWAYS_FATAL_IF(Attributes::abort_on_recursion_check_
+                    && mutex_get_enable_flag()
+                    && p_order == m_order,
+                    "%s: recursive mutex access detected (order: %zu %s)",
+                    __func__, p_order, Attributes::order_names_[p_order]);
+        }
+
+        static void post_lock(mutex& m) {
+            ++m.stat_.locks;
+            m.get_thread_mutex_info()->push_held(&m, m.order_);
+        }
+
+    private:
+        mutex& mutex_;
+        const int64_t time_;
+        bool discard_wait_time_ = false;
+    };
+
+    class lock_scoped_stat_disabled {
+    public:
+        explicit lock_scoped_stat_disabled(mutex&) {}
+
+        void ignoreWaitTime() {}
+
+        static void pre_unlock(mutex&) {}
+
+        static void pre_lock(mutex&) {}
+
+        static void post_lock(mutex&) {}
+    };
+
+    using lock_scoped_stat_t = std::conditional_t<Attributes::mutex_tracking_enabled_,
+            lock_scoped_stat_enabled, lock_scoped_stat_disabled>;
+
+    // helper class for registering statistics for a cv wait.
+    class cv_wait_scoped_stat_enabled {
+    public:
+        explicit cv_wait_scoped_stat_enabled(mutex& m) : mutex_(m) {
+            ++mutex_.stat_.unlocks;
+            const bool success = mutex_.get_thread_mutex_info()->remove_held(&mutex_);
+            LOG_ALWAYS_FATAL_IF(Attributes::abort_on_invalid_unlock_
+                    && mutex_get_enable_flag()
+                    && !success,
+                    "%s: invalid mutex unlock when not previously held", __func__);
+        }
+
+        ~cv_wait_scoped_stat_enabled() {
+            ++mutex_.stat_.locks;
+            mutex_.get_thread_mutex_info()->push_held(&mutex_, mutex_.order_);
+        }
+    private:
+        mutex& mutex_;
+    };
+
+    class cv_wait_scoped_stat_disabled {
+        explicit cv_wait_scoped_stat_disabled(mutex&) {}
+    };
+
+    using cv_wait_scoped_stat_t = std::conditional_t<Attributes::mutex_tracking_enabled_,
+            cv_wait_scoped_stat_enabled, cv_wait_scoped_stat_disabled>;
+
+    using thread_registry_t = thread_registry<thread_mutex_info_t>;
+
+    // One per-process thread registry, one instance per template typename.
+    // Declared here but must be defined in a .cpp otherwise there will be multiple
+    // instances if the header is included into different shared libraries.
+    static thread_registry_t& get_registry();
+
+    using stat_array_t = std::array<mutex_stat_t, Attributes::order_size_>;
+
+    // One per-process mutex statistics array, one instance per template typename.
+    // Declared here but must be defined in a .cpp otherwise there will be multiple
+    // instances if the header is included into different shared libraries.
+    static stat_array_t& get_mutex_stat_array();
+
 private:
+
     std::mutex m_;
+    const typename Attributes::order_t order_;
+    mutex_stat_t& stat_;  // set in ctor
 };
 
+// define the destructor to remove from registry.
+template <typename MutexHandle, typename Order, size_t N>
+inline thread_mutex_info<MutexHandle, Order, N>::~thread_mutex_info() {
+    if (tid_ != 0) {
+        mutex::get_registry().remove_from_registry(tid_);
+    }
+}
+
 // audio_utils::lock_guard only works with the defined mutex.
 using lock_guard = std::lock_guard<mutex>;
 
@@ -108,32 +1249,57 @@
 class SCOPED_CAPABILITY unique_lock {
 public:
     explicit unique_lock(mutex& m) ACQUIRE(m)
-        : ul_(m.std_mutex()) {}
+        : ul_(m.std_mutex(), std::defer_lock)
+        , mutex_(m) {
+        lock();
+    }
 
-    ~unique_lock() RELEASE() = default;
+    ~unique_lock() RELEASE() {
+        if (held) unlock();
+    }
 
     void lock() ACQUIRE() {
-       ul_.lock();
+        mutex::lock_scoped_stat_t::pre_lock(mutex_);
+        if (!ul_.try_lock()) {
+            mutex::lock_scoped_stat_t ls(mutex_);
+            ul_.lock();
+        }
+        mutex::lock_scoped_stat_t::post_lock(mutex_);
+        held = true;
     }
 
     void unlock() RELEASE() {
+        mutex::lock_scoped_stat_t::pre_unlock(mutex_);
+        held = false;
         ul_.unlock();
     }
 
     bool try_lock() TRY_ACQUIRE(true) {
-        return ul_.try_lock();
+        mutex::lock_scoped_stat_t::pre_lock(mutex_);
+        if (!ul_.try_lock()) return false;
+        mutex::lock_scoped_stat_t::post_lock(mutex_);
+        held = true;
+        return true;
     }
 
     template<class Rep, class Period>
     bool try_lock_for(const std::chrono::duration<Rep,Period>& timeout_duration)
             TRY_ACQUIRE(true) {
-        return ul_.try_lock_for(timeout_duration);
+        mutex::lock_scoped_stat_t::pre_lock(mutex_);
+        if (!ul_.try_lock_for(timeout_duration)) return false;
+        mutex::lock_scoped_stat_t::post_lock(mutex_);
+        held = true;
+        return true;
     }
 
     template<class Clock, class Duration>
     bool try_lock_until(const std::chrono::time_point<Clock,Duration>& timeout_time)
             TRY_ACQUIRE(true) {
-        return ul_.try_lock_until(timeout_time);
+        mutex::lock_scoped_stat_t::pre_lock(mutex_);
+        if (!ul_.try_lock_until(timeout_time)) return false;
+        mutex::lock_scoped_stat_t::post_lock(mutex_);
+        held = true;
+        return true;
     }
 
     // additional method to obtain the underlying std::unique_lock
@@ -141,8 +1307,15 @@
         return ul_;
     }
 
+    // additional method to obtain the underlying mutex
+    mutex& native_mutex() {
+        return mutex_;
+    }
+
 private:
     std::unique_lock<std::mutex> ul_;
+    bool held = false;
+    mutex& mutex_;
 };
 
 // audio_utils::condition_variable uses the optimized version of
@@ -161,17 +1334,20 @@
     }
 
     void wait(unique_lock& lock) {
+        mutex::cv_wait_scoped_stat_t ws(lock.native_mutex());
         cv_.wait(lock.std_unique_lock());
     }
 
     template<typename Predicate>
     void wait(unique_lock& lock, Predicate stop_waiting) {
+        mutex::cv_wait_scoped_stat_t ws(lock.native_mutex());
         cv_.wait(lock.std_unique_lock(), std::move(stop_waiting));
     }
 
     template<typename Rep, typename Period>
     std::cv_status wait_for(unique_lock& lock,
             const std::chrono::duration<Rep, Period>& rel_time) {
+        mutex::cv_wait_scoped_stat_t ws(lock.native_mutex());
         return cv_.wait_for(lock.std_unique_lock(), rel_time);
     }
 
@@ -179,12 +1355,14 @@
     bool wait_for(unique_lock& lock,
             const std::chrono::duration<Rep, Period>& rel_time,
             Predicate stop_waiting) {
+        mutex::cv_wait_scoped_stat_t ws(lock.native_mutex());
         return cv_.wait_for(lock.std_unique_lock(), rel_time, std::move(stop_waiting));
     }
 
     template<typename Clock, typename Duration>
     std::cv_status wait_until(unique_lock& lock,
             const std::chrono::time_point<Clock, Duration>& timeout_time) {
+        mutex::cv_wait_scoped_stat_t ws(lock.native_mutex());
         return cv_.wait_until(lock.std_unique_lock(), timeout_time);
     }
 
@@ -192,6 +1370,7 @@
     bool wait_until(unique_lock& lock,
             const std::chrono::time_point<Clock, Duration>& timeout_time,
             Predicate stop_waiting) {
+        mutex::cv_wait_scoped_stat_t ws(lock.native_mutex());
         return cv_.wait_until(lock.std_unique_lock(), timeout_time, std::move(stop_waiting));
     }
 
@@ -268,157 +1447,6 @@
     lock_guard_no_thread_safety_analysis(Mutex1& m) : std::lock_guard<Mutex1>(m) {}
 };
 
-// Define global capabilities for thread-safety annotation.
-//
-// These can be manually modified, or
-// compile generate_mutex_order.cpp in the tests directory
-// to generate this.
-
-// --- Begin generated section
-
-// Capabilities in priority order
-// (declaration only, value is nullptr)
-inline mutex* AudioFlinger_Mutex;
-inline mutex* EffectHandle_Mutex
-        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_Mutex);
-inline mutex* AudioFlinger_HardwareMutex
-        ACQUIRED_AFTER(android::audio_utils::EffectHandle_Mutex);
-inline mutex* DeviceEffectManager_Mutex
-        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_HardwareMutex);
-inline mutex* PatchCommandThread_Mutex
-        ACQUIRED_AFTER(android::audio_utils::DeviceEffectManager_Mutex);
-inline mutex* ThreadBase_Mutex
-        ACQUIRED_AFTER(android::audio_utils::PatchCommandThread_Mutex);
-inline mutex* AudioFlinger_ClientMutex
-        ACQUIRED_AFTER(android::audio_utils::ThreadBase_Mutex);
-inline mutex* MelReporter_Mutex
-        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_ClientMutex);
-inline mutex* EffectChain_Mutex
-        ACQUIRED_AFTER(android::audio_utils::MelReporter_Mutex);
-inline mutex* EffectBase_Mutex
-        ACQUIRED_AFTER(android::audio_utils::EffectChain_Mutex);
-inline mutex* AudioFlinger_UnregisteredWritersMutex
-        ACQUIRED_AFTER(android::audio_utils::EffectBase_Mutex);
-inline mutex* AsyncCallbackThread_Mutex
-        ACQUIRED_AFTER(android::audio_utils::AudioFlinger_UnregisteredWritersMutex);
-inline mutex* ConfigEvent_Mutex
-        ACQUIRED_AFTER(android::audio_utils::AsyncCallbackThread_Mutex);
-inline mutex* OutputTrack_TrackMetadataMutex
-        ACQUIRED_AFTER(android::audio_utils::ConfigEvent_Mutex);
-inline mutex* PassthruPatchRecord_ReadMutex
-        ACQUIRED_AFTER(android::audio_utils::OutputTrack_TrackMetadataMutex);
-inline mutex* PatchCommandThread_ListenerMutex
-        ACQUIRED_AFTER(android::audio_utils::PassthruPatchRecord_ReadMutex);
-inline mutex* PlaybackThread_AudioTrackCbMutex
-        ACQUIRED_AFTER(android::audio_utils::PatchCommandThread_ListenerMutex);
-
-// Exclusion by capability
-#define EXCLUDES_BELOW_PlaybackThread_AudioTrackCbMutex
-#define EXCLUDES_PlaybackThread_AudioTrackCbMutex \
-    EXCLUDES(android::audio_utils::PlaybackThread_AudioTrackCbMutex) \
-    EXCLUDES_BELOW_PlaybackThread_AudioTrackCbMutex
-
-#define EXCLUDES_BELOW_PatchCommandThread_ListenerMutex \
-    EXCLUDES_PlaybackThread_AudioTrackCbMutex
-#define EXCLUDES_PatchCommandThread_ListenerMutex \
-    EXCLUDES(android::audio_utils::PatchCommandThread_ListenerMutex) \
-    EXCLUDES_BELOW_PatchCommandThread_ListenerMutex
-
-#define EXCLUDES_BELOW_PassthruPatchRecord_ReadMutex \
-    EXCLUDES_PatchCommandThread_ListenerMutex
-#define EXCLUDES_PassthruPatchRecord_ReadMutex \
-    EXCLUDES(android::audio_utils::PassthruPatchRecord_ReadMutex) \
-    EXCLUDES_BELOW_PassthruPatchRecord_ReadMutex
-
-#define EXCLUDES_BELOW_OutputTrack_TrackMetadataMutex \
-    EXCLUDES_PassthruPatchRecord_ReadMutex
-#define EXCLUDES_OutputTrack_TrackMetadataMutex \
-    EXCLUDES(android::audio_utils::OutputTrack_TrackMetadataMutex) \
-    EXCLUDES_BELOW_OutputTrack_TrackMetadataMutex
-
-#define EXCLUDES_BELOW_ConfigEvent_Mutex \
-    EXCLUDES_OutputTrack_TrackMetadataMutex
-#define EXCLUDES_ConfigEvent_Mutex \
-    EXCLUDES(android::audio_utils::ConfigEvent_Mutex) \
-    EXCLUDES_BELOW_ConfigEvent_Mutex
-
-#define EXCLUDES_BELOW_AsyncCallbackThread_Mutex \
-    EXCLUDES_ConfigEvent_Mutex
-#define EXCLUDES_AsyncCallbackThread_Mutex \
-    EXCLUDES(android::audio_utils::AsyncCallbackThread_Mutex) \
-    EXCLUDES_BELOW_AsyncCallbackThread_Mutex
-
-#define EXCLUDES_BELOW_AudioFlinger_UnregisteredWritersMutex \
-    EXCLUDES_AsyncCallbackThread_Mutex
-#define EXCLUDES_AudioFlinger_UnregisteredWritersMutex \
-    EXCLUDES(android::audio_utils::AudioFlinger_UnregisteredWritersMutex) \
-    EXCLUDES_BELOW_AudioFlinger_UnregisteredWritersMutex
-
-#define EXCLUDES_BELOW_EffectBase_Mutex \
-    EXCLUDES_AudioFlinger_UnregisteredWritersMutex
-#define EXCLUDES_EffectBase_Mutex \
-    EXCLUDES(android::audio_utils::EffectBase_Mutex) \
-    EXCLUDES_BELOW_EffectBase_Mutex
-
-#define EXCLUDES_BELOW_EffectChain_Mutex \
-    EXCLUDES_EffectBase_Mutex
-#define EXCLUDES_EffectChain_Mutex \
-    EXCLUDES(android::audio_utils::EffectChain_Mutex) \
-    EXCLUDES_BELOW_EffectChain_Mutex
-
-#define EXCLUDES_BELOW_MelReporter_Mutex \
-    EXCLUDES_EffectChain_Mutex
-#define EXCLUDES_MelReporter_Mutex \
-    EXCLUDES(android::audio_utils::MelReporter_Mutex) \
-    EXCLUDES_BELOW_MelReporter_Mutex
-
-#define EXCLUDES_BELOW_AudioFlinger_ClientMutex \
-    EXCLUDES_MelReporter_Mutex
-#define EXCLUDES_AudioFlinger_ClientMutex \
-    EXCLUDES(android::audio_utils::AudioFlinger_ClientMutex) \
-    EXCLUDES_BELOW_AudioFlinger_ClientMutex
-
-#define EXCLUDES_BELOW_ThreadBase_Mutex \
-    EXCLUDES_AudioFlinger_ClientMutex
-#define EXCLUDES_ThreadBase_Mutex \
-    EXCLUDES(android::audio_utils::ThreadBase_Mutex) \
-    EXCLUDES_BELOW_ThreadBase_Mutex
-
-#define EXCLUDES_BELOW_PatchCommandThread_Mutex \
-    EXCLUDES_ThreadBase_Mutex
-#define EXCLUDES_PatchCommandThread_Mutex \
-    EXCLUDES(android::audio_utils::PatchCommandThread_Mutex) \
-    EXCLUDES_BELOW_PatchCommandThread_Mutex
-
-#define EXCLUDES_BELOW_DeviceEffectManager_Mutex \
-    EXCLUDES_PatchCommandThread_Mutex
-#define EXCLUDES_DeviceEffectManager_Mutex \
-    EXCLUDES(android::audio_utils::DeviceEffectManager_Mutex) \
-    EXCLUDES_BELOW_DeviceEffectManager_Mutex
-
-#define EXCLUDES_BELOW_AudioFlinger_HardwareMutex \
-    EXCLUDES_DeviceEffectManager_Mutex
-#define EXCLUDES_AudioFlinger_HardwareMutex \
-    EXCLUDES(android::audio_utils::AudioFlinger_HardwareMutex) \
-    EXCLUDES_BELOW_AudioFlinger_HardwareMutex
-
-#define EXCLUDES_BELOW_EffectHandle_Mutex \
-    EXCLUDES_AudioFlinger_HardwareMutex
-#define EXCLUDES_EffectHandle_Mutex \
-    EXCLUDES(android::audio_utils::EffectHandle_Mutex) \
-    EXCLUDES_BELOW_EffectHandle_Mutex
-
-#define EXCLUDES_BELOW_AudioFlinger_Mutex \
-    EXCLUDES_EffectHandle_Mutex
-#define EXCLUDES_AudioFlinger_Mutex \
-    EXCLUDES(android::audio_utils::AudioFlinger_Mutex) \
-    EXCLUDES_BELOW_AudioFlinger_Mutex
-
-#define EXCLUDES_AUDIO_ALL \
-    EXCLUDES_AudioFlinger_Mutex
-
-// --- End generated section
-
 } // namespace android::audio_utils
 
 #pragma pop_macro("LOG_TAG")
diff --git a/audio_utils/include/audio_utils/safe_math.h b/audio_utils/include/audio_utils/safe_math.h
index 7dcab3f..7397fe1 100644
--- a/audio_utils/include/audio_utils/safe_math.h
+++ b/audio_utils/include/audio_utils/safe_math.h
@@ -14,8 +14,65 @@
  * limitations under the License.
  */
 
+#pragma once
+
+#include <bit>
+#include <cmath>
+
 namespace android::audio_utils {
 
+/*
+ * The compilation option
+ * -ffast-math
+ *
+ * https://gcc.gnu.org/wiki/FloatingPointMath
+ *
+ * enables the following flags:
+ *
+ * -fno-trapping-math
+ * -funsafe-math-optimizations
+ * -ffinite-math-only
+ * -fno-errno-math
+ * -fno-signaling-nans
+ * -fno-rounding-math
+ * -fcx-limited-range
+ * -fno-signed-zeros.
+ *
+ * -ffinite-math-only means isnan() and isinf() detection may not work properly.
+ */
+
+/**
+ * Returns the unsigned integer layout of a float.
+ */
+inline constexpr unsigned float_as_unsigned(float f) {
+    // gnu++20 does not include std::bit_cast, so we use the builtin
+    // supported by gnu and clang. That being said, the latest gnu, clang, and msvc
+    // compilers all support std::bit_cast so update when language support is upgraded.
+    // return std::bit_cast<unsigned>(f);
+
+    return __builtin_bit_cast(unsigned, f);
+}
+
+/**
+ * Returns true if the float is nan regardless of -ffast-math compilation.
+ */
+inline constexpr bool safe_isnan(float f) {
+    // float is signed-magnitude, so shift sign bit out to do nan comparison.
+    // (This works for ILP32 / LP64 as the sign bit is removed by the shift).
+    // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
+    return float_as_unsigned(f) << 1 >= 0xff800001 << 1;
+}
+
+/**
+ * Returns true if the float is infinite (pos or neg) regardless of -ffast-math compilation.
+ */
+inline constexpr bool safe_isinf(float f) {
+    // float is signed-magnitude, so shift sign bit out to do inf comparison.
+    // (This works for ILP32 / LP64 as the sign bit is removed by the shift).
+    // https://en.wikipedia.org/wiki/Single-precision_floating-point_format
+    return float_as_unsigned(f) << 1 == 0xff800000 << 1;
+}
+
 // safe_sub_overflow is used ensure that subtraction occurs in the same native
 // type with proper 2's complement overflow.  Without calling this function, it
 // is possible, for example, that optimizing compilers may elect to treat 32 bit
diff --git a/audio_utils/include/audio_utils/threads.h b/audio_utils/include/audio_utils/threads.h
new file mode 100644
index 0000000..2bf59eb
--- /dev/null
+++ b/audio_utils/include/audio_utils/threads.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2023 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 <algorithm>
+#include <sys/syscall.h>   // SYS_gettid
+#include <unistd.h>        // bionic gettid
+#include <utils/Errors.h>  // status_t
+
+namespace android::audio_utils {
+
+/*
+ * Some basic priority definitions from linux,
+ * see MACROs in common/include/linux/sched/prio.h.
+ *
+ * On device limits may be found with the following command $ adb shell chrt -m
+ */
+inline constexpr int kMaxNice = 19;  // inclusive
+inline constexpr int kMinNice = -20; // inclusive
+inline constexpr int kNiceWidth = (kMaxNice - kMinNice + 1);
+inline constexpr int kMinRtPrio = 1; // inclusive
+inline constexpr int kMaxRtPrio = 100;                    // [sic] exclusive
+inline constexpr int kMaxPrio = kMaxRtPrio + kNiceWidth;  // [sic] exclusive
+inline constexpr int kDefaultPrio = kMaxRtPrio + kNiceWidth / 2;
+
+/*
+ * The following conversion routines follow the linux equivalent.
+ * Here we use the term "unified priority" as the linux normal_prio.
+ *
+ * See common/kernel/sched/core.c __normal_prio().
+ */
+
+/**
+ * Convert CFS (SCHED_OTHER) nice to unified priority.
+ */
+inline int nice_to_unified_priority(int nice) {
+    return kDefaultPrio + nice;
+}
+
+/**
+ * Convert unified priority to CFS (SCHED_OTHER) nice.
+ *
+ * Some unified priorities are not CFS, they will be clamped in range.
+ * Use is_cfs_priority() to check if a CFS priority.
+ */
+inline int unified_priority_to_nice(int priority) {
+    return std::clamp(priority - kDefaultPrio, kMinNice, kMaxNice);
+}
+
+/**
+ * Convert SCHED_FIFO/SCHED_RR rtprio 1 - 99 to unified priority 98 to 0.
+ */
+inline int rtprio_to_unified_priority(int rtprio) {
+    return kMaxRtPrio - 1 - rtprio;
+}
+
+/**
+ * Convert unified priority 0 to 98 to SCHED_FIFO/SCHED_RR rtprio 99 to 1.
+ *
+ * Some unified priorities are not real time, they will be clamped in range.
+ * Use is_realtime_priority() to check if real time priority.
+ */
+inline int unified_priority_to_rtprio(int priority) {
+    return std::clamp(kMaxRtPrio - 1 - priority, kMinRtPrio, kMaxRtPrio - 1);
+}
+
+/**
+ * Returns whether the unified priority is realtime.
+ */
+inline bool is_realtime_priority(int priority) {
+    return priority >= 0 && priority < kMaxRtPrio;  // note this allows the unified value 99.
+}
+
+/**
+ * Returns whether the unified priority is CFS.
+ */
+inline bool is_cfs_priority(int priority) {
+    return priority >= kMaxRtPrio && priority < kMaxPrio;
+}
+
+/**
+ * Returns the linux thread id.
+ *
+ * We use gettid() which is available on bionic libc and modern glibc;
+ * for other libc, we syscall directly.
+ *
+ * This is not the same as std::thread::get_id() which returns pthread_self().
+ */
+pid_t inline gettid_wrapper() {
+#if defined(__BIONIC__)
+    return ::gettid();
+#else
+    return syscall(SYS_gettid);
+#endif
+}
+
+/*
+ * set_thread_priority() and get_thread_priority()
+ * both use the unified scheduler priority, where a lower value represents
+ * increasing priority.
+ *
+ * The linux kernel unified scheduler priority values are as follows:
+ * 0 - 98    (A real time priority rtprio between 99 and 1)
+ * 100 - 139 (A Completely Fair Scheduler niceness between -20 and 19)
+ *
+ * Real time schedulers (SCHED_FIFO and SCHED_RR) have rtprio between 1 and 99,
+ * where 1 is the lowest and 99 is the highest.
+ *
+ * The Completely Fair Scheduler (also known as SCHED_OTHER) has a
+ * nice value between 19 and -20, where 19 is the lowest and -20 the highest.
+ *
+ * Note: the unified priority is reported on /proc/<tid>/stat file as "prio".
+ *
+ * See common/kernel/sched/debug.c proc_sched_show_task().
+ */
+
+/**
+ * Sets the priority of tid to a unified priority.
+ *
+ * The range of priority is 0 through 139, inclusive.
+ * A priority value of 99 is changed to 98.
+ */
+status_t set_thread_priority(pid_t tid, int priority);
+
+/**
+ * Returns the unified priority of the tid.
+ *
+ * A negative number represents error.
+ */
+int get_thread_priority(int tid);
+
+} // namespace android::audio_utils
diff --git a/audio_utils/mutex.cpp b/audio_utils/mutex.cpp
new file mode 100644
index 0000000..a219614
--- /dev/null
+++ b/audio_utils/mutex.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2023 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 <audio_utils/mutex.h>
+
+#define LOG_TAG "audio_utils::mutex"
+#include <utils/Log.h>
+
+#include <com_android_media_audioserver.h>
+
+namespace android::audio_utils {
+
+bool mutex_get_enable_flag() {
+    static const bool enable = []() {
+        const bool flag = com::android::media::audioserver::mutex_priority_inheritance();
+        ALOGD("get_enable_flag: mutex_priority_inheritance: %s", flag ? "true" : "false");
+        return flag;
+    }();
+    return enable;
+}
+
+// Define mutex::get_mutex_stat_array here because header-only ODR inline linking
+// results in multiple objects if included into multiple shared libraries.
+template<>
+mutex::stat_array_t& mutex::get_mutex_stat_array() {
+    static stat_array_t stat_array{};
+    return stat_array;
+}
+
+// Define mutex::get_registry here because header-only ODR inline linking
+// results in multiple objects if included into multiple shared libraries.
+template<>
+mutex::thread_registry_t& mutex::get_registry() {
+    static thread_registry_t thread_registry{};
+    return thread_registry;
+}
+
+}  // namespace android::audio_utils
diff --git a/audio_utils/tests/Android.bp b/audio_utils/tests/Android.bp
index 3e9064b..2764631 100644
--- a/audio_utils/tests/Android.bp
+++ b/audio_utils/tests/Android.bp
@@ -8,6 +8,49 @@
     default_applicable_licenses: ["system_media_license"],
 }
 
+cc_defaults {
+    name: "audio_math_test_defaults",
+    host_supported: true,
+
+    srcs: [
+        "audio_math_tests.cpp"
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libaudioutils",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+}
+
+cc_test {
+    name: "audio_fast_math_tests",
+    defaults: [
+        "audio_math_test_defaults",
+    ],
+    cflags: [
+        "-DFAST_MATH_ENABLED",
+        "-ffast-math",
+    ],
+}
+
+cc_test {
+    name: "audio_math_tests",
+    defaults: [
+        "audio_math_test_defaults",
+    ],
+}
+
 cc_test {
     name: "audio_mutex_tests",
     host_supported: true,
@@ -16,10 +59,36 @@
         "audio_mutex_tests.cpp",
     ],
 
-    static_libs: [
+    shared_libs: [
         "libaudioutils",
         "libbase",
         "liblog",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wthread-safety",
+    ],
+}
+
+cc_test {
+    name: "audio_thread_tests",
+
+    srcs: [
+        "audio_thread_tests.cpp",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libutils",
+    ],
+
+    static_libs: [
+        "libaudioutils",
     ],
 
     cflags: [
diff --git a/audio_utils/tests/audio_math_tests.cpp b/audio_utils/tests/audio_math_tests.cpp
new file mode 100644
index 0000000..45df16d
--- /dev/null
+++ b/audio_utils/tests/audio_math_tests.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_math_tests"
+
+#include <audio_utils/safe_math.h>
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+TEST(audio_math_tests, safe_isnan) {
+    const auto nf = std::nanf("");
+    const bool std_isnan = std::isnan(nf);
+    const bool eq_isnan = nf != nf;
+    const bool au_isnan = android::audio_utils::safe_isnan(nf);
+
+#ifndef FAST_MATH_ENABLED
+    EXPECT_TRUE(std_isnan);  // not always true for -ffast-math, assumed false
+    EXPECT_TRUE(eq_isnan);   // not always true for -ffast-math
+#endif
+
+    EXPECT_TRUE(au_isnan);
+    constexpr bool not_a_nan = android::audio_utils::safe_isnan(1.f);
+    EXPECT_FALSE(not_a_nan);
+    EXPECT_FALSE(android::audio_utils::safe_isnan(std::numeric_limits<float>::infinity()));
+    EXPECT_FALSE(android::audio_utils::safe_isnan(std::numeric_limits<float>::max()));
+    EXPECT_FALSE(android::audio_utils::safe_isnan(std::numeric_limits<float>::min()));
+
+    EXPECT_TRUE(android::audio_utils::safe_isnan(nan("")));
+
+    ALOGD("%s: std::isnan:%d  eq_isnan:%d  audio_utils::isnan:%d",
+            __func__, std_isnan, eq_isnan, au_isnan);
+}
+
+TEST(audio_math_tests, safe_isinf) {
+    const auto inf = std::numeric_limits<float>::infinity();
+    const bool std_isinf = std::isinf(inf);
+    const bool eq_isinf = inf == std::numeric_limits<float>::infinity();
+    const bool au_isinf = android::audio_utils::safe_isinf(inf);
+
+#ifndef FAST_MATH_ENABLED
+    EXPECT_TRUE(std_isinf);  // not always true for -ffast-math, assumed false
+#endif
+
+    EXPECT_TRUE(eq_isinf);
+    EXPECT_TRUE(au_isinf);
+    constexpr bool not_a_inf = android::audio_utils::safe_isinf(1.f);
+    EXPECT_FALSE(not_a_inf);
+    EXPECT_FALSE(android::audio_utils::safe_isinf(std::nanf("")));
+    EXPECT_FALSE(android::audio_utils::safe_isinf(std::numeric_limits<float>::max()));
+    EXPECT_FALSE(android::audio_utils::safe_isinf(std::numeric_limits<float>::min()));
+
+    EXPECT_TRUE(android::audio_utils::safe_isinf(std::numeric_limits<double>::infinity()));
+
+    ALOGD("%s: std::isinf:%d  eq_isinf:%d  audio_utils::isinf:%d",
+            __func__, std_isinf, eq_isinf, au_isinf);
+}
diff --git a/audio_utils/tests/audio_mutex_tests.cpp b/audio_utils/tests/audio_mutex_tests.cpp
index 3e6c269..17e9874 100644
--- a/audio_utils/tests/audio_mutex_tests.cpp
+++ b/audio_utils/tests/audio_mutex_tests.cpp
@@ -17,6 +17,15 @@
 #include <audio_utils/mutex.h>
 #include <gtest/gtest.h>
 
+#include <thread>
+
+// Currently tests mutex priority-inheritance (or not) based on flag
+// adb shell setprop \
+// persist.device_config.aconfig_flags.media_audio.\
+// com.android.media.audio.flags.mutex_priority_inheritance true
+//
+// TODO(b/209491695) enable both PI/non-PI mutex tests regardless of flag.
+
 namespace android {
 
 namespace audio_locks {
@@ -213,7 +222,7 @@
     int v3_ GUARDED_BY(cap3) = 3;
 };
 
-TEST(audio_lock_tests, Container) {
+TEST(audio_mutex_tests, Container) {
     Container c;
 
     EXPECT_EQ(1, c.value1()); // success
@@ -236,7 +245,7 @@
 // see that mutex checking is done without knowledge of
 // the actual implementation.
 
-TEST(audio_lock_tests, Interface) {
+TEST(audio_mutex_tests, Interface) {
     Container c;
     IContainer *i = static_cast<IContainer*>(&c);
 
@@ -254,6 +263,272 @@
         audio_utils::lock_guard l(i->mutex1());
         EXPECT_EQ(3, i->combo12_l()); // success
     }
+
+    ALOGD("%s: %s", __func__, audio_utils::mutex::all_stats_to_string().c_str());
+}
+
+TEST(audio_mutex_tests, Stack) {
+    android::audio_utils::atomic_stack<int, int, 2> as;
+
+    // set up stack
+    EXPECT_EQ(0UL, as.size());
+    as.push(1, 10);
+    EXPECT_EQ(1UL, as.size());
+    as.push(2, 20);
+    EXPECT_EQ(2UL, as.size());
+
+    // 3rd item exceeds the stack capacity.
+    as.push(3, 30);
+    // 2 items tracked (subset)
+    EXPECT_EQ(2UL, as.size());
+    // 3 items total.
+    EXPECT_EQ(3UL, as.true_size());
+
+    const auto& bot = as.bottom();
+    const auto& top = as.top();
+
+    // these are the 2 items tracked:
+    EXPECT_EQ(1, bot.first.load());
+    EXPECT_EQ(10, bot.second.load());
+
+    EXPECT_EQ(3, top.first.load());
+    EXPECT_EQ(30, top.second.load());
+
+    // remove the bottom item.
+    EXPECT_EQ(true, as.remove(1));
+    EXPECT_EQ(1UL, as.size());
+    EXPECT_EQ(2UL, as.true_size());
+
+    // now remove the "virtual" item.
+    // (actually any non-existing item value works).
+    EXPECT_EQ(true, as.remove(2));
+    EXPECT_EQ(1UL, as.size());
+    EXPECT_EQ(1UL, as.true_size());
+
+    // now an invalid removal
+    EXPECT_EQ(false, as.remove(5));
+    EXPECT_EQ(1UL, as.size());
+    EXPECT_EQ(1UL, as.true_size());
+
+    // now remove the final item.
+    EXPECT_EQ(true, as.remove(3));
+    EXPECT_EQ(0UL, as.size());
+    EXPECT_EQ(0UL, as.true_size());
+}
+
+TEST(audio_mutex_tests, RecursiveLockDetection) {
+    constexpr pid_t pid = 0;  // avoid registry shutdown.
+    android::audio_utils::thread_mutex_info<int, int, 8 /* stack depth */> tmi(pid);
+
+    // set up stack
+    tmi.push_held(50, 1);
+    tmi.push_held(40, 2);
+    tmi.push_held(30, 3);
+    EXPECT_EQ(3UL, tmi.stack().size());
+
+    // remove bottom.
+    tmi.remove_held(50);
+    EXPECT_EQ(2UL, tmi.stack().size());
+
+    // test recursive lock detection.
+
+    // same order, same item is recursive.
+    const auto& recursive = tmi.check_held(30, 3);
+    EXPECT_EQ(30, recursive.first.load());
+    EXPECT_EQ(3, recursive.second.load());
+
+    // same order but different item (10 != 30) is OK.
+    const auto& nil = tmi.check_held(10, 3);
+    EXPECT_EQ(0, nil.first.load());
+    EXPECT_EQ(0, nil.second.load());
+}
+
+TEST(audio_mutex_tests, OrderDetection) {
+    constexpr pid_t pid = 0;  // avoid registry shutdown.
+    android::audio_utils::thread_mutex_info<int, int, 8 /* stack depth */> tmi(pid);
+
+    // set up stack
+    tmi.push_held(50, 1);
+    tmi.push_held(40, 2);
+    tmi.push_held(30, 3);
+    EXPECT_EQ(3UL, tmi.stack().size());
+
+    // remove middle
+    tmi.remove_held(40);
+    EXPECT_EQ(2UL, tmi.stack().size());
+
+    // test inversion detection.
+
+    // lower order is a problem 1 < 3.
+    const auto& inversion = tmi.check_held(1, 1);
+    EXPECT_EQ(30, inversion.first.load());
+    EXPECT_EQ(3, inversion.second.load());
+
+    // higher order is OK.
+    const auto& nil2 = tmi.check_held(4, 4);
+    EXPECT_EQ(0, nil2.first.load());
+    EXPECT_EQ(0, nil2.second.load());
+}
+
+// Test that the mutex aborts on recursion (if the abort flag is set).
+
+TEST(audio_mutex_tests, FatalRecursiveMutex)
+NO_THREAD_SAFETY_ANALYSIS {
+    if (!android::audio_utils::AudioMutexAttributes::abort_on_recursion_check_
+            || !audio_utils::mutex_get_enable_flag()) {
+        ALOGD("Test FatalRecursiveMutex skipped");
+        return;
+    }
+
+    using Mutex = android::audio_utils::mutex;
+    using LockGuard = android::audio_utils::lock_guard;
+
+    Mutex m;
+    LockGuard lg(m);
+
+    // Can't lock ourselves again.
+    ASSERT_DEATH(m.lock(), ".*recursive mutex.*");
+}
+
+// Test that the mutex aborts on lock order inversion (if the abort flag is set).
+
+TEST(audio_mutex_tests, FatalLockOrder)
+NO_THREAD_SAFETY_ANALYSIS {
+    if (!android::audio_utils::AudioMutexAttributes::abort_on_order_check_
+            || !audio_utils::mutex_get_enable_flag()) {
+        ALOGD("Test FatalLockOrder skipped");
+        return;
+    }
+
+    using Mutex = android::audio_utils::mutex;
+    using LockGuard = android::audio_utils::lock_guard;
+
+    Mutex m1{(android::audio_utils::AudioMutexAttributes::order_t)1};
+    Mutex m2{(android::audio_utils::AudioMutexAttributes::order_t)2};
+
+    LockGuard lg2(m2);
+    // m1 must be locked before m2 as 1 < 2.
+    ASSERT_DEATH(m1.lock(), ".*mutex order.*");
+}
+
+// Test that the mutex aborts on lock order inversion (if the abort flag is set).
+
+TEST(audio_mutex_tests, UnexpectedUnlock)
+NO_THREAD_SAFETY_ANALYSIS {
+    if (!android::audio_utils::AudioMutexAttributes::abort_on_invalid_unlock_
+            || !audio_utils::mutex_get_enable_flag()) {
+        ALOGD("Test UnexpectedUnlock skipped");
+        return;
+    }
+
+    using Mutex = android::audio_utils::mutex;
+
+    Mutex m1{(android::audio_utils::AudioMutexAttributes::order_t)1};
+    ASSERT_DEATH(m1.unlock(), ".*mutex unlock.*");
+}
+
+// Test the deadlock detection algorithm for a single wait chain
+// (no cycle).
+
+TEST(audio_mutex_tests, DeadlockDetection) {
+    using Mutex = android::audio_utils::mutex;
+    using UniqueLock = android::audio_utils::unique_lock;
+    using ConditionVariable = android::audio_utils::condition_variable;
+
+    // order checked below.
+    constexpr size_t kOrder1 = 1;
+    constexpr size_t kOrder2 = 2;
+    constexpr size_t kOrder3 = 3;
+    static_assert(Mutex::attributes_t::order_size_ > kOrder3);
+
+    Mutex m1{static_cast<Mutex::attributes_t::order_t>(kOrder1)};
+    Mutex m2{static_cast<Mutex::attributes_t::order_t>(kOrder2)};
+    Mutex m3{static_cast<Mutex::attributes_t::order_t>(kOrder3)};
+    Mutex m4;
+    Mutex m;
+    ConditionVariable cv;
+    bool quit = false;  // GUARDED_BY(m)
+    std::atomic<pid_t> tid1{}, tid2{}, tid3{}, tid4{};
+
+    std::thread t4([&]() {
+        UniqueLock ul4(m4);
+        UniqueLock ul(m);
+        tid4 = android::audio_utils::gettid_wrapper();
+        while (!quit) {
+            cv.wait(ul, [&]{ return quit; });
+            if (quit) break;
+        }
+    });
+
+    while (tid4 == 0) { usleep(1000); }
+
+    std::thread t3([&]() {
+        UniqueLock ul3(m3);
+        tid3 = android::audio_utils::gettid_wrapper();
+        UniqueLock ul4(m4);
+    });
+
+    while (tid3 == 0) { usleep(1000); }
+
+    std::thread t2([&]() {
+        UniqueLock ul2(m2);
+        tid2 = android::audio_utils::gettid_wrapper();
+        UniqueLock ul3(m3);
+    });
+
+    while (tid2 == 0) { usleep(1000); }
+
+    std::thread t1([&]() {
+        UniqueLock ul1(m1);
+        tid1 = android::audio_utils::gettid_wrapper();
+        UniqueLock ul2(m2);
+    });
+
+    while (tid1 == 0) { usleep(1000); }
+
+    // we know that the threads will now block in the proper order.
+    // however, we need to wait for the block to happen.
+    // this part is racy unless we check the thread state or use
+    // futexes directly in our mutex (which allows atomic accuracy of wait).
+    usleep(20000);
+
+    const auto deadlockInfo = android::audio_utils::mutex::deadlock_detection(tid1);
+
+    // no cycle.
+    EXPECT_EQ(false, deadlockInfo.has_cycle);
+
+    // thread1 is waiting on a chain of 3 other threads.
+    const auto chain = deadlockInfo.chain;
+    const size_t chain_size = chain.size();
+    EXPECT_EQ(3u, chain_size);
+
+    const auto default_idx = static_cast<size_t>(Mutex::attributes_t::order_default_);
+    if (chain_size > 0) {
+        EXPECT_EQ(tid2, chain[0].first);
+        EXPECT_EQ(Mutex::attributes_t::order_names_[kOrder2], chain[0].second);
+    }
+    if (chain_size > 1) {
+        EXPECT_EQ(tid3, chain[1].first);
+        EXPECT_EQ(Mutex::attributes_t::order_names_[kOrder3], chain[1].second);
+    }
+    if (chain_size > 2) {
+        EXPECT_EQ(tid4, chain[2].first);
+        EXPECT_EQ(Mutex::attributes_t::order_names_[default_idx], chain[2].second);
+    }
+
+    ALOGD("%s", android::audio_utils::mutex::all_threads_to_string().c_str());
+
+    {
+        UniqueLock ul(m);
+
+        quit = true;
+        cv.notify_one();
+    }
+
+    t4.join();
+    t3.join();
+    t2.join();
+    t1.join();
 }
 
 } // namespace android
diff --git a/audio_utils/tests/audio_thread_tests.cpp b/audio_utils/tests/audio_thread_tests.cpp
new file mode 100644
index 0000000..360fc3a
--- /dev/null
+++ b/audio_utils/tests/audio_thread_tests.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 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 <audio_utils/threads.h>
+#include <gtest/gtest.h>
+
+using namespace android;
+using namespace android::audio_utils;
+
+TEST(audio_thread_tests, conversion) {
+    EXPECT_EQ(120, kDefaultPrio);
+
+    EXPECT_EQ(kMaxRtPrio, nice_to_unified_priority(kMinNice));
+    EXPECT_EQ(kMaxPrio - 1, nice_to_unified_priority(kMaxNice));
+
+    EXPECT_EQ(kMinNice, unified_priority_to_nice(kMaxRtPrio));
+    EXPECT_EQ(kMaxNice, unified_priority_to_nice(kMaxPrio - 1));
+
+    EXPECT_EQ(kMaxRtPrio - 1, unified_priority_to_rtprio(0));
+    EXPECT_EQ(kMinRtPrio, unified_priority_to_rtprio(98));
+
+    EXPECT_EQ(0, rtprio_to_unified_priority(kMaxRtPrio - 1));
+    EXPECT_EQ(98, rtprio_to_unified_priority(kMinRtPrio));
+
+    EXPECT_FALSE(is_cfs_priority(kMaxRtPrio-1));
+    EXPECT_TRUE(is_cfs_priority(kMaxRtPrio));  // note the bound is exclusive
+
+    EXPECT_TRUE(is_realtime_priority(kMaxRtPrio-1));
+    EXPECT_FALSE(is_realtime_priority(kMaxRtPrio));  // the bound is exclusive.
+}
+
+TEST(audio_thread_tests, priority) {
+    const auto tid = gettid_wrapper();
+    const int priority = get_thread_priority(tid);
+    ASSERT_GE(priority, 0);
+
+    constexpr int kPriority110 = 110;
+    EXPECT_EQ(NO_ERROR, set_thread_priority(tid, kPriority110));
+    EXPECT_EQ(kPriority110, get_thread_priority(tid));
+
+    constexpr int kPriority130 = 130;
+    EXPECT_EQ(NO_ERROR, set_thread_priority(tid, kPriority130));
+    EXPECT_EQ(kPriority130, get_thread_priority(tid));
+
+    // Requires privilege to go RT.
+    // constexpr int kPriority98 = 98;
+    // EXPECT_EQ(NO_ERROR, set_thread_priority(tid, kPriority98));
+    // EXPECT_EQ(kPriority98, get_thread_priority(tid));
+
+    EXPECT_EQ(NO_ERROR, set_thread_priority(tid, priority));
+}
diff --git a/audio_utils/tests/fdtostring_tests.cpp b/audio_utils/tests/fdtostring_tests.cpp
index 683ca56..ab785ab 100644
--- a/audio_utils/tests/fdtostring_tests.cpp
+++ b/audio_utils/tests/fdtostring_tests.cpp
@@ -14,45 +14,75 @@
  * limitations under the License.
  */
 
-//#define LOG_NDEBUG 0
-#define LOG_TAG "audio_utils_fdtostring_tests"
-#include <log/log.h>
-
 #include <audio_utils/FdToString.h>
+
+#include <signal.h>
+#include <chrono>
+
 #include <gtest/gtest.h>
 
 using namespace android::audio_utils;
 
 TEST(audio_utils_fdtostring, basic) {
+    signal(SIGPIPE, SIG_IGN);
     const std::string PREFIX{"aa "};
-    const std::string TEST_STRING{"hello world"};
+    const std::string TEST_STRING{"hello world\n"};
 
-    FdToString fdToString(PREFIX);
-    const int fd = fdToString.fd();
+    auto writer_opt = FdToString::createWriter(PREFIX);
+    ASSERT_TRUE(writer_opt.has_value());
+    FdToString::Writer& writer = *writer_opt;
+    const int fd = writer.borrowFdUnsafe();
     ASSERT_TRUE(fd >= 0);
 
     write(fd, TEST_STRING.c_str(), TEST_STRING.size());
 
-    const std::string result = fdToString.getStringAndClose();
-
+    const std::string result = FdToString::closeWriterAndGetString(std::move(writer));
     ASSERT_EQ((PREFIX + TEST_STRING), result);
 }
 
-TEST(audio_utils_fdtostring, multilines) {
+TEST(audio_utils_fdtostring, multiline) {
+    signal(SIGPIPE, SIG_IGN);
     const std::string PREFIX{"aa "};
-    const std::string DELIM{"\n"};
-    const std::string TEST_STRING1{"hello world\n"};
-    const std::string TEST_STRING2{"goodbye\n"};
+    const std::string INPUT[] = {"hello\n", "pt1", "pt2 ", "\n", "\n", "pt3\n", "pt4"};
+    const std::string GOLDEN = "aa hello\naa pt1pt2 \naa \naa pt3\npt4";
 
-    FdToString fdToString(PREFIX);
-    const int fd = fdToString.fd();
+    auto writer_opt = FdToString::createWriter(PREFIX);
+    ASSERT_TRUE(writer_opt.has_value());
+    FdToString::Writer& writer = *writer_opt;
+    const int fd = writer.borrowFdUnsafe();
     ASSERT_TRUE(fd >= 0);
 
-    write(fd, TEST_STRING1.c_str(), TEST_STRING1.size());
-    write(fd, DELIM.c_str(), DELIM.size()); // double newline
-    write(fd, TEST_STRING2.c_str(), TEST_STRING2.size());
+    for (const auto& str : INPUT) {
+        write(fd, str.c_str(), str.size());
+    }
 
-    const std::string result = fdToString.getStringAndClose();
+    ASSERT_EQ(FdToString::closeWriterAndGetString(std::move(writer)), GOLDEN);
+}
 
-    ASSERT_EQ((PREFIX + TEST_STRING1 + PREFIX + DELIM + PREFIX + TEST_STRING2), result);
+TEST(audio_utils_fdtostring, blocking) {
+    signal(SIGPIPE, SIG_IGN);
+    const std::string PREFIX{"- "};
+    const std::string INPUT[] = {"1\n", "2\n", "3\n", "4\n", "5\n"};
+    const std::string GOLDEN = "- 1\n- 2\n- 3\n- 4\n- 5\n";
+
+    auto writer_opt = FdToString::createWriter(PREFIX, std::chrono::milliseconds{200});
+
+    ASSERT_TRUE(writer_opt.has_value());
+    FdToString::Writer& writer = *writer_opt;
+    const int fd = writer.borrowFdUnsafe();
+    ASSERT_TRUE(fd >= 0);
+
+    // Chosen so that we shouldn't finish the entire array before the timeout
+    constexpr auto WAIT = std::chrono::milliseconds{90};
+
+    int count = 0;
+    for (const auto& str : INPUT) {
+        ASSERT_LT(count, 4) << "The reader has timed out, write should have failed by now";
+        if (write(fd, str.c_str(), str.size()) < 0) break;
+        std::this_thread::sleep_for(WAIT);
+        count++;
+    }
+
+    ASSERT_EQ(FdToString::closeWriterAndGetString(std::move(writer)).substr(0, 8),
+            GOLDEN.substr(0, 8)) << "Format mistake";
 }
diff --git a/audio_utils/tests/generate_mutex_order.cpp b/audio_utils/tests/generate_mutex_order.cpp
index 29cea79..0f9f45d 100644
--- a/audio_utils/tests/generate_mutex_order.cpp
+++ b/audio_utils/tests/generate_mutex_order.cpp
@@ -41,8 +41,10 @@
   // 4) AudioFlinger -> ThreadBase -> EffectChain -> EffectBase(EffectModule)
   // 5) EffectHandle -> ThreadBase -> EffectChain -> EffectBase(EffectModule)
 
-  "AudioFlinger_Mutex",
   "EffectHandle_Mutex",
+  "EffectBase_PolicyMutex",  // held for AudioSystem::registerEffect, must come
+                             // after EffectHandle_Mutex.
+  "AudioFlinger_Mutex",
   "AudioFlinger_HardwareMutex",
   "DeviceEffectManager_Mutex",
   "PatchCommandThread_Mutex",
@@ -50,6 +52,7 @@
   "AudioFlinger_ClientMutex",
   "MelReporter_Mutex",
   "EffectChain_Mutex",
+  "DeviceEffectProxy_ProxyMutex",  // used for device effects (which have no chain).
   "EffectBase_Mutex",
 
   // These mutexes are in leaf objects
@@ -62,6 +65,10 @@
   "PassthruPatchRecord_ReadMutex",
   "PatchCommandThread_ListenerMutex",
   "PlaybackThread_AudioTrackCbMutex",
+  "MediaLogNotifier_Mutex",
+  "OtherMutex", // DO NOT CHANGE THIS: OtherMutex is used for mutexes without a specified order.
+                // An OtherMutex will always be the lowest order mutex and cannot acquire
+                // another named mutex while being held.
 };
 
 using namespace std;
@@ -70,7 +77,28 @@
 // ordering and exclusion as listed above.
 
 int main() {
-  cout << "// Capabilities in priority order\n"
+  cout << "// Lock order\n";
+  cout << "enum class MutexOrder : uint32_t {\n";
+
+  for (size_t i = 0; i < size(mutexes); ++i) {
+      cout << "    k" << mutexes[i] << " = " << i << ",\n";
+  }
+  cout << "    kSize = " << size(mutexes) << ",\n";
+  cout << "};\n";
+
+  cout << "\n// Lock by name\n";
+  cout << "inline constexpr const char* const gMutexNames[] = {\n";
+  for (size_t i = 0; i < size(mutexes); ++i) {
+      cout << "    \"" << mutexes[i] << "\",\n";
+  }
+  cout << "};\n";
+
+  cout << "\n// Forward declarations\n";
+  cout << "class AudioMutexAttributes;\n";
+  cout << "template <typename T> class mutex_impl;\n";
+  cout << "using mutex = mutex_impl<AudioMutexAttributes>;\n";
+
+  cout << "\n// Capabilities in priority order\n"
        << "// (declaration only, value is nullptr)\n";
   const char *last = nullptr;
   for (auto mutex : mutexes) {
diff --git a/audio_utils/threads.cpp b/audio_utils/threads.cpp
new file mode 100644
index 0000000..aeaf59a
--- /dev/null
+++ b/audio_utils/threads.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "audio_utils::threads"
+
+#include <audio_utils/threads.h>
+
+#include <algorithm>  // std::clamp
+#include <errno.h>
+#include <sched.h>    // scheduler
+#include <sys/resource.h>
+#include <utils/Errors.h>  // status_t
+#include <utils/Log.h>
+
+namespace android::audio_utils {
+
+/**
+ * Sets the unified priority of the tid.
+ */
+status_t set_thread_priority(pid_t tid, int priority) {
+    if (is_realtime_priority(priority)) {
+        // audio processes are designed to work with FIFO, not RR.
+        constexpr int new_policy = SCHED_FIFO;
+        const int rtprio = unified_priority_to_rtprio(priority);
+        struct sched_param param {
+            .sched_priority = rtprio,
+        };
+        if (sched_setscheduler(tid, new_policy, &param) != 0) {
+            ALOGW("%s: Cannot set FIFO priority for tid %d to policy %d rtprio %d  %s",
+                    __func__, tid, new_policy, rtprio, strerror(errno));
+            return -errno;
+        }
+        return NO_ERROR;
+    } else if (is_cfs_priority(priority)) {
+        const int policy = sched_getscheduler(tid);
+        const int nice = unified_priority_to_nice(priority);
+        if (policy != SCHED_OTHER) {
+            struct sched_param param{};
+            constexpr int new_policy = SCHED_OTHER;
+            if (sched_setscheduler(tid, new_policy, &param) != 0) {
+                ALOGW("%s: Cannot set CFS priority for tid %d to policy %d nice %d  %s",
+                        __func__, tid, new_policy, nice, strerror(errno));
+                return -errno;
+            }
+        }
+        if (setpriority(PRIO_PROCESS, tid, nice) != 0) return -errno;
+        return NO_ERROR;
+    } else {
+        return BAD_VALUE;
+    }
+}
+
+/**
+ * Returns the unified priority of the tid.
+ *
+ * A negative number represents error.
+ */
+int get_thread_priority(int tid) {
+    const int policy = sched_getscheduler(tid);
+    if (policy < 0) return -errno;
+
+    if (policy == SCHED_OTHER) {
+        errno = 0;  // negative return value valid, so check errno change.
+        const int nice = getpriority(PRIO_PROCESS, tid);
+        if (errno != 0) return -errno;
+        return nice_to_unified_priority(nice);
+    } else if (policy == SCHED_FIFO || policy == SCHED_RR) {
+        struct sched_param param{};
+        if (sched_getparam(tid, &param) < 0) return -errno;
+        return rtprio_to_unified_priority(param.sched_priority);
+    } else {
+        return INVALID_OPERATION;
+    }
+}
+
+} // namespace android::audio_utils
diff --git a/camera/Android.bp b/camera/Android.bp
index 019e892..f144e29 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -67,7 +67,7 @@
     local_include_dirs: ["include"],
 
     static_libs: [
-        "android.hardware.camera.metadata-V2-ndk",
+        "android.hardware.camera.metadata-V3-ndk",
     ],
 
     cflags: [
diff --git a/camera/docs/CameraMetadataEnums.mako b/camera/docs/CameraMetadataEnums.mako
index ee2431e..0ed6ad6 100644
--- a/camera/docs/CameraMetadataEnums.mako
+++ b/camera/docs/CameraMetadataEnums.mako
@@ -51,6 +51,9 @@
     % if value.visibility == 'test':
     @TestApi
     % endif
+    % if value.aconfig_flag:
+    @FlaggedApi(Flags.FLAG_${value.aconfig_flag | jkey_identifier})
+    % endif
     public static final int ${jenum_value(entry, value)} = ${enum_calculate_value_string(value)};
 
   % endfor
diff --git a/camera/docs/CameraMetadataKeys.mako b/camera/docs/CameraMetadataKeys.mako
index 6af0cdf..376b5ce 100644
--- a/camera/docs/CameraMetadataKeys.mako
+++ b/camera/docs/CameraMetadataKeys.mako
@@ -42,10 +42,11 @@
     concatenated_info = description + details + extra_detail
 %>\
 ## Glue description and details together before javadoc-izing. Otherwise @see in middle of javadoc.
+## Avoid @see across differently-flagged API entries for now.
 ${concatenated_info | javadoc(metadata)}\
   % if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
     % for value in entry.enum.values:
-     % if not value.hidden:
+     % if not value.hidden and (value.aconfig_flag == entry.aconfig_flag):
      * @see #${jenum_value(entry, value)}
      % endif
     % endfor
@@ -68,6 +69,9 @@
   % if entry.synthetic:
     @SyntheticKey
   % endif
+  % if entry.aconfig_flag:
+    @FlaggedApi(Flags.FLAG_${entry.aconfig_flag | jkey_identifier})
+  % endif
     public static final Key<${jtype_boxed(entry)}> ${entry.name | jkey_identifier} =
             new Key<${jtype_boxed(entry)}>("${entry.name}", ${jkey_type_token(entry)});
 </%def>\
diff --git a/camera/docs/camera_device_info.proto b/camera/docs/camera_device_info.proto
index 2f922cb..3c58eda 100644
--- a/camera/docs/camera_device_info.proto
+++ b/camera/docs/camera_device_info.proto
@@ -141,7 +141,12 @@
     optional RangeFloat android_control_zoomRatioRange = 131090;
     repeated int32 android_control_availableSettingsOverrides = 131091;
     optional bool android_control_autoframingAvailable = 131092;
+    optional RangeFloat android_control_lowLightBoostInfoLuminanceRange = 131093;
     repeated int32 android_edge_availableEdgeModes = 262144;
+    optional int32 android_flash_singleStrengthMaxLevel = 327680;
+    optional int32 android_flash_singleStrengthDefaultLevel = 327681;
+    optional int32 android_flash_torchStrengthMaxLevel = 327682;
+    optional int32 android_flash_torchStrengthDefaultLevel = 327683;
     optional bool android_flash_info_available = 393216;
     optional int32 android_flash_info_strengthMaximumLevel = 393217;
     optional int32 android_flash_info_strengthDefaultLevel = 393218;
@@ -223,6 +228,7 @@
     optional int32 android_info_supportedHardwareLevel = 1441792;
     optional string android_info_version = 1441793;
     optional DeviceStateSensorOrientationMap android_info_deviceStateSensorOrientationMap = 1441794;
+    optional int32 android_info_sessionConfigurationQueryVersion = 1441795;
     optional int32 android_sync_maxLatency = 1572864;
     optional int32 android_reprocess_maxCaptureStall = 1638400;
     optional bool android_depth_depthIsExclusive = 1703936;
diff --git a/camera/docs/docs.html b/camera/docs/docs.html
index 667c3fb..055ff2a 100644
--- a/camera/docs/docs.html
+++ b/camera/docs/docs.html
@@ -266,6 +266,8 @@
             ><a href="#static_android.control.availableSettingsOverrides">android.control.availableSettingsOverrides</a></li>
             <li
             ><a href="#static_android.control.autoframingAvailable">android.control.autoframingAvailable</a></li>
+            <li
+            ><a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.control.lowLightBoostInfoLuminanceRange</a></li>
           </ul>
         </li>
         <li>
@@ -337,6 +339,8 @@
             ><a href="#dynamic_android.control.autoframing">android.control.autoframing</a></li>
             <li
             ><a href="#dynamic_android.control.autoframingState">android.control.autoframingState</a></li>
+            <li
+            ><a href="#dynamic_android.control.lowLightBoostState">android.control.lowLightBoostState</a></li>
           </ul>
         </li>
       </ul> <!-- toc_section -->
@@ -393,6 +397,8 @@
             ><a href="#controls_android.flash.firingTime">android.flash.firingTime</a></li>
             <li
             ><a href="#controls_android.flash.mode">android.flash.mode</a></li>
+            <li
+            ><a href="#controls_android.flash.strengthLevel">android.flash.strengthLevel</a></li>
           </ul>
         </li>
         <li>
@@ -412,6 +418,14 @@
             ><a href="#static_android.flash.colorTemperature">android.flash.colorTemperature</a></li>
             <li
             ><a href="#static_android.flash.maxEnergy">android.flash.maxEnergy</a></li>
+            <li
+            ><a href="#static_android.flash.singleStrengthMaxLevel">android.flash.singleStrengthMaxLevel</a></li>
+            <li
+            ><a href="#static_android.flash.singleStrengthDefaultLevel">android.flash.singleStrengthDefaultLevel</a></li>
+            <li
+            ><a href="#static_android.flash.torchStrengthMaxLevel">android.flash.torchStrengthMaxLevel</a></li>
+            <li
+            ><a href="#static_android.flash.torchStrengthDefaultLevel">android.flash.torchStrengthDefaultLevel</a></li>
           </ul>
         </li>
         <li>
@@ -425,6 +439,8 @@
             ><a href="#dynamic_android.flash.mode">android.flash.mode</a></li>
             <li
             ><a href="#dynamic_android.flash.state">android.flash.state</a></li>
+            <li
+            ><a href="#dynamic_android.flash.strengthLevel">android.flash.strengthLevel</a></li>
           </ul>
         </li>
       </ul> <!-- toc_section -->
@@ -1115,6 +1131,12 @@
             ><a href="#dynamic_android.statistics.oisYShifts">android.statistics.oisYShifts</a></li>
             <li
             ><a href="#dynamic_android.statistics.oisSamples">android.statistics.oisSamples</a></li>
+            <li
+            ><a href="#dynamic_android.statistics.lensIntrinsicsSamples">android.statistics.lensIntrinsicsSamples</a></li>
+            <li
+            ><a href="#dynamic_android.statistics.lensIntrinsicTimestamps">android.statistics.lensIntrinsicTimestamps</a></li>
+            <li
+            ><a href="#dynamic_android.statistics.lensIntrinsicSamples">android.statistics.lensIntrinsicSamples</a></li>
           </ul>
         </li>
       </ul> <!-- toc_section -->
@@ -1213,6 +1235,8 @@
             ><a href="#static_android.info.deviceStateSensorOrientationMap">android.info.deviceStateSensorOrientationMap</a></li>
             <li
             ><a href="#static_android.info.deviceStateOrientations">android.info.deviceStateOrientations</a></li>
+            <li
+            ><a href="#static_android.info.sessionConfigurationQueryVersion">android.info.sessionConfigurationQueryVersion</a></li>
           </ul>
         </li>
       </ul> <!-- toc_section -->
@@ -1338,6 +1362,8 @@
           <ul class="toc_section">
             <li
             ><a href="#dynamic_android.logicalMultiCamera.activePhysicalId">android.logicalMultiCamera.activePhysicalId</a></li>
+            <li
+            ><a href="#dynamic_android.logicalMultiCamera.activePhysicalSensorCropRegion">android.logicalMultiCamera.activePhysicalSensorCropRegion</a></li>
           </ul>
         </li>
       </ul> <!-- toc_section -->
@@ -2766,6 +2792,39 @@
 be FLASH_<wbr/>REQUIRED after the camera device finishes AE scan and it's too dark without
 flash.<wbr/></p></span>
                   </li>
+                  <li>
+                    <span class="entry_type_enum_name">ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY (v3.9)</span>
+                    <span class="entry_type_enum_optional">[optional]</span>
+                    <span class="entry_type_enum_notes"><p>Like 'ON' but applies additional brightness boost in low light scenes.<wbr/></p>
+<p>When the scene lighting conditions are within the range defined by
+<a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range</a> this mode will apply additional
+brightness boost.<wbr/></p>
+<p>This mode will automatically adjust the intensity of low light boost applied
+according to the scene lighting conditions.<wbr/> A darker scene will receive more boost
+while a brighter scene will receive less boost.<wbr/></p>
+<p>This mode can ignore the set target frame rate to allow more light to be captured
+which can result in choppier motion.<wbr/> The frame rate can extend to lower than the
+<a href="#static_android.control.aeAvailableTargetFpsRanges">android.<wbr/>control.<wbr/>ae<wbr/>Available<wbr/>Target<wbr/>Fps<wbr/>Ranges</a> but will not go below 10 FPS.<wbr/> This mode
+can also increase the sensor sensitivity gain which can result in increased luma
+and chroma noise.<wbr/> The sensor sensitivity gain can extend to higher values beyond
+<a href="#static_android.sensor.info.sensitivityRange">android.<wbr/>sensor.<wbr/>info.<wbr/>sensitivity<wbr/>Range</a>.<wbr/> This mode may also apply additional
+processing to recover details in dark and bright areas of the image,<wbr/>and noise
+reduction at high sensitivity gain settings to manage the trade-off between light
+sensitivity and capture noise.<wbr/></p>
+<p>This mode is restricted to two output surfaces.<wbr/> One output surface type can either
+be SurfaceView or TextureView.<wbr/> Another output surface type can either be MediaCodec
+or MediaRecorder.<wbr/> This mode cannot be used with a target FPS range higher than 30
+FPS.<wbr/></p>
+<p>If the session configuration is not supported,<wbr/> the AE mode reported in the
+CaptureResult will be 'ON' instead of 'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY'.<wbr/></p>
+<p>The application can observe the CapturerResult field
+<a href="#dynamic_android.control.lowLightBoostState">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>State</a> to determine when low light boost is 'ACTIVE' or
+'INACTIVE'.<wbr/></p>
+<p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
+upper bound lux value defined by <a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range</a>.<wbr/>
+This mode will be 'INACTIVE' once the scene lighting condition is greater than the
+upper bound lux value defined by <a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range</a>.<wbr/></p></span>
+                  </li>
                 </ul>
 
             </td> <!-- entry_type -->
@@ -7580,6 +7639,61 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="static_android.control.lowLightBoostInfoLuminanceRange">
+            <td class="entry_name
+             " rowspan="1">
+              android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">float</span>
+                <span class="entry_type_container">x</span>
+
+                <span class="entry_type_array">
+                  2
+                </span>
+              <span class="entry_type_visibility"> [public as rangeFloat]</span>
+
+
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>The operating luminance range of low light boost measured in lux (lx).<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+              <p>The lower bound indicates the lowest scene luminance value the AE mode
+'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY' can operate within.<wbr/> Scenes of lower luminance
+than this may receive less brightening,<wbr/> increased noise,<wbr/> or artifacts.<wbr/></p>
+<p>The upper bound indicates the luminance threshold at the point when the mode is enabled.<wbr/>
+For example,<wbr/> 'Range[0.<wbr/>3,<wbr/> 30.<wbr/>0]' defines 0.<wbr/>3 lux being the lowest scene luminance the
+mode can reliably support.<wbr/> 30.<wbr/>0 lux represents the threshold when this mode is
+activated.<wbr/> Scenes measured at less than or equal to 30 lux will activate low light
+boost.<wbr/></p>
+<p>If this key is defined,<wbr/> then the AE mode 'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY' will
+also be present.<wbr/></p>
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -8050,6 +8164,39 @@
 be FLASH_<wbr/>REQUIRED after the camera device finishes AE scan and it's too dark without
 flash.<wbr/></p></span>
                   </li>
+                  <li>
+                    <span class="entry_type_enum_name">ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY (v3.9)</span>
+                    <span class="entry_type_enum_optional">[optional]</span>
+                    <span class="entry_type_enum_notes"><p>Like 'ON' but applies additional brightness boost in low light scenes.<wbr/></p>
+<p>When the scene lighting conditions are within the range defined by
+<a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range</a> this mode will apply additional
+brightness boost.<wbr/></p>
+<p>This mode will automatically adjust the intensity of low light boost applied
+according to the scene lighting conditions.<wbr/> A darker scene will receive more boost
+while a brighter scene will receive less boost.<wbr/></p>
+<p>This mode can ignore the set target frame rate to allow more light to be captured
+which can result in choppier motion.<wbr/> The frame rate can extend to lower than the
+<a href="#static_android.control.aeAvailableTargetFpsRanges">android.<wbr/>control.<wbr/>ae<wbr/>Available<wbr/>Target<wbr/>Fps<wbr/>Ranges</a> but will not go below 10 FPS.<wbr/> This mode
+can also increase the sensor sensitivity gain which can result in increased luma
+and chroma noise.<wbr/> The sensor sensitivity gain can extend to higher values beyond
+<a href="#static_android.sensor.info.sensitivityRange">android.<wbr/>sensor.<wbr/>info.<wbr/>sensitivity<wbr/>Range</a>.<wbr/> This mode may also apply additional
+processing to recover details in dark and bright areas of the image,<wbr/>and noise
+reduction at high sensitivity gain settings to manage the trade-off between light
+sensitivity and capture noise.<wbr/></p>
+<p>This mode is restricted to two output surfaces.<wbr/> One output surface type can either
+be SurfaceView or TextureView.<wbr/> Another output surface type can either be MediaCodec
+or MediaRecorder.<wbr/> This mode cannot be used with a target FPS range higher than 30
+FPS.<wbr/></p>
+<p>If the session configuration is not supported,<wbr/> the AE mode reported in the
+CaptureResult will be 'ON' instead of 'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY'.<wbr/></p>
+<p>The application can observe the CapturerResult field
+<a href="#dynamic_android.control.lowLightBoostState">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>State</a> to determine when low light boost is 'ACTIVE' or
+'INACTIVE'.<wbr/></p>
+<p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
+upper bound lux value defined by <a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range</a>.<wbr/>
+This mode will be 'INACTIVE' once the scene lighting condition is greater than the
+upper bound lux value defined by <a href="#static_android.control.lowLightBoostInfoLuminanceRange">android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>Info<wbr/>Luminance<wbr/>Range</a>.<wbr/></p></span>
+                  </li>
                 </ul>
 
             </td> <!-- entry_type -->
@@ -11988,6 +12135,71 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="dynamic_android.control.lowLightBoostState">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>control.<wbr/>low<wbr/>Light<wbr/>Boost<wbr/>State
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name entry_type_name_enum">byte</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+
+
+
+                <ul class="entry_type_enum">
+                  <li>
+                    <span class="entry_type_enum_name">INACTIVE (v3.9)</span>
+                    <span class="entry_type_enum_notes"><p>The AE mode 'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY' is enabled but not applied.<wbr/></p></span>
+                  </li>
+                  <li>
+                    <span class="entry_type_enum_name">ACTIVE (v3.9)</span>
+                    <span class="entry_type_enum_notes"><p>The AE mode 'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY' is enabled and applied.<wbr/></p></span>
+                  </li>
+                </ul>
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Current state of the low light boost AE mode.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>When low light boost is enabled by setting the AE mode to
+'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY',<wbr/> it can dynamically apply a low light
+boost when the light level threshold is exceeded.<wbr/></p>
+<p>This state indicates when low light boost is 'ACTIVE' and applied.<wbr/> Similarly,<wbr/> it can
+indicate when it is not being applied by returning 'INACTIVE'.<wbr/></p>
+<p>This key will be absent from the CaptureResult if AE mode is not set to
+'ON_<wbr/>LOW_<wbr/>LIGHT_<wbr/>BOOST_<wbr/>BRIGHTNESS_<wbr/>PRIORITY.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -12758,6 +12970,81 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="controls_android.flash.strengthLevel">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>flash.<wbr/>strength<wbr/>Level
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Flash strength level to be used when manual flash control is active.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+              <p><code>[1-android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level]</code> when the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> is
+set to TORCH;
+<code>[1-android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level]</code> when the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> is
+set to SINGLE</p>
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>Flash strength level to use in capture mode i.<wbr/>e.<wbr/> when the applications control
+flash with either SINGLE or TORCH mode.<wbr/></p>
+<p>Use android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level and
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level to check whether the device supports
+flash strength control or not.<wbr/>
+If the values of android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level and
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level are greater than 1,<wbr/>
+then the device supports manual flash strength control.<wbr/></p>
+<p>If the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> <code>==</code> TORCH the value must be &gt;= 1
+and &lt;= android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level.<wbr/>
+If the application doesn't set the key and
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level &gt; 1,<wbr/>
+then the flash will be fired at the default level set by HAL in
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Default<wbr/>Level.<wbr/>
+If the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> <code>==</code> SINGLE,<wbr/> then the value must be &gt;= 1
+and &lt;= android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level.<wbr/>
+If the application does not set this key and
+android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level &gt; 1,<wbr/>
+then the flash will be fired at the default level set by HAL
+in android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Default<wbr/>Level.<wbr/>
+If <a href="#controls_android.control.aeMode">android.<wbr/>control.<wbr/>ae<wbr/>Mode</a> is set to any of ON_<wbr/>AUTO_<wbr/>FLASH,<wbr/> ON_<wbr/>ALWAYS_<wbr/>FLASH,<wbr/>
+ON_<wbr/>AUTO_<wbr/>FLASH_<wbr/>REDEYE,<wbr/> ON_<wbr/>EXTERNAL_<wbr/>FLASH values,<wbr/> then the strengthLevel will be ignored.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -13121,6 +13408,226 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="static_android.flash.singleStrengthMaxLevel">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>flash.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Maximum flash brightness level for manual flash control in SINGLE mode.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>Maximum flash brightness level in camera capture mode and
+<a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> set to SINGLE.<wbr/>
+Value will be &gt; 1 if the manual flash strength control feature is supported,<wbr/>
+otherwise the value will be equal to 1.<wbr/>
+Note that this level is just a number of supported levels (the granularity of control).<wbr/>
+There is no actual physical power units tied to this level.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
+                
+          <tr class="entry" id="static_android.flash.singleStrengthDefaultLevel">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>flash.<wbr/>single<wbr/>Strength<wbr/>Default<wbr/>Level
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Default flash brightness level for manual flash control in SINGLE mode.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>If flash unit is available this will be greater than or equal to 1 and less
+or equal to <code>android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level</code>.<wbr/>
+Note for devices that do not support the manual flash strength control
+feature,<wbr/> this level will always be equal to 1.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
+                
+          <tr class="entry" id="static_android.flash.torchStrengthMaxLevel">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>flash.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Maximum flash brightness level for manual flash control in TORCH mode</p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>Maximum flash brightness level in camera capture mode and
+<a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> set to TORCH.<wbr/>
+Value will be &gt; 1 if the manual flash strength control feature is supported,<wbr/>
+otherwise the value will be equal to 1.<wbr/></p>
+<p>Note that this level is just a number of supported levels(the granularity of control).<wbr/>
+There is no actual physical power units tied to this level.<wbr/>
+There is no relation between android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level and
+android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level i.<wbr/>e.<wbr/> the ratio of
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level:android.<wbr/>flash.<wbr/>info.<wbr/>singleStrengthMaxLevel
+is not guaranteed to be the ratio of actual brightness.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
+                
+          <tr class="entry" id="static_android.flash.torchStrengthDefaultLevel">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>flash.<wbr/>torch<wbr/>Strength<wbr/>Default<wbr/>Level
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Default flash brightness level for manual flash control in TORCH mode</p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>If flash unit is available this will be greater than or equal to 1 and less
+or equal to android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level.<wbr/>
+Note for the devices that do not support the manual flash strength control feature,<wbr/>
+this level will always be equal to 1.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -13429,6 +13936,81 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="dynamic_android.flash.strengthLevel">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>flash.<wbr/>strength<wbr/>Level
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+
+              <span class="entry_type_visibility"> [public]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>Flash strength level to be used when manual flash control is active.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+              <p><code>[1-android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level]</code> when the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> is
+set to TORCH;
+<code>[1-android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level]</code> when the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> is
+set to SINGLE</p>
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>Flash strength level to use in capture mode i.<wbr/>e.<wbr/> when the applications control
+flash with either SINGLE or TORCH mode.<wbr/></p>
+<p>Use android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level and
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level to check whether the device supports
+flash strength control or not.<wbr/>
+If the values of android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level and
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level are greater than 1,<wbr/>
+then the device supports manual flash strength control.<wbr/></p>
+<p>If the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> <code>==</code> TORCH the value must be &gt;= 1
+and &lt;= android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level.<wbr/>
+If the application doesn't set the key and
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Max<wbr/>Level &gt; 1,<wbr/>
+then the flash will be fired at the default level set by HAL in
+android.<wbr/>flash.<wbr/>info.<wbr/>torch<wbr/>Strength<wbr/>Default<wbr/>Level.<wbr/>
+If the <a href="#controls_android.flash.mode">android.<wbr/>flash.<wbr/>mode</a> <code>==</code> SINGLE,<wbr/> then the value must be &gt;= 1
+and &lt;= android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level.<wbr/>
+If the application does not set this key and
+android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Max<wbr/>Level &gt; 1,<wbr/>
+then the flash will be fired at the default level set by HAL
+in android.<wbr/>flash.<wbr/>info.<wbr/>single<wbr/>Strength<wbr/>Default<wbr/>Level.<wbr/>
+If <a href="#controls_android.control.aeMode">android.<wbr/>control.<wbr/>ae<wbr/>Mode</a> is set to any of ON_<wbr/>AUTO_<wbr/>FLASH,<wbr/> ON_<wbr/>ALWAYS_<wbr/>FLASH,<wbr/>
+ON_<wbr/>AUTO_<wbr/>FLASH_<wbr/>REDEYE,<wbr/> ON_<wbr/>EXTERNAL_<wbr/>FLASH values,<wbr/> then the strengthLevel will be ignored.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -19999,10 +20581,10 @@
 </ul>
 <p>Combinations of logical and physical streams,<wbr/> or physical streams from different
 physical cameras are not guaranteed.<wbr/> However,<wbr/> if the camera device supports
-<a href="https://developer.android.com/reference/CameraDevice.html#isSessionConfigurationSupported">CameraDevice#isSessionConfigurationSupported</a>,<wbr/>
+<a href="https://developer.android.com/reference/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>,<wbr/>
 application must be able to query whether a stream combination involving physical
 streams is supported by calling
-<a href="https://developer.android.com/reference/CameraDevice.html#isSessionConfigurationSupported">CameraDevice#isSessionConfigurationSupported</a>.<wbr/></p>
+<a href="https://developer.android.com/reference/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>.<wbr/></p>
 <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front
 camera in the system.<wbr/> For an application that switches between front and back cameras,<wbr/>
 the recommendation is to switch between the first rear camera and the first front
@@ -24441,7 +25023,7 @@
 <p>When the key is present,<wbr/> only a PRIVATE/<wbr/>YUV output of the specified size is guaranteed
 to be supported by the camera HAL in the secure camera mode.<wbr/> Any other format or
 resolutions might not be supported.<wbr/> Use
-<a href="https://developer.android.com/reference/CameraDevice.html#isSessionConfigurationSupported">CameraDevice#isSessionConfigurationSupported</a>
+<a href="https://developer.android.com/reference/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>
 API to query if a secure session configuration is supported if the device supports this
 API.<wbr/></p>
 <p>If this key returns null on a device with SECURE_<wbr/>IMAGE_<wbr/>DATA capability,<wbr/> the application
@@ -26161,8 +26743,8 @@
             </td> <!-- entry_type -->
 
             <td class="entry_description">
-              <p>Duration from start of frame exposure to
-start of next frame exposure.<wbr/></p>
+              <p>Duration from start of frame readout to
+start of next frame readout.<wbr/></p>
             </td>
 
             <td class="entry_units">
@@ -26250,6 +26832,10 @@
 <p>For more details about stalling,<wbr/> see <a href="https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap.html#getOutputStallDuration">StreamConfigurationMap#getOutputStallDuration</a>.<wbr/></p>
 <p>This control is only effective if <a href="#controls_android.control.aeMode">android.<wbr/>control.<wbr/>ae<wbr/>Mode</a> or <a href="#controls_android.control.mode">android.<wbr/>control.<wbr/>mode</a> is set to
 OFF; otherwise the auto-exposure algorithm will override this value.<wbr/></p>
+<p><em>Note:</em> Prior to Android 13,<wbr/> this field was described as measuring the duration from
+start of frame exposure to start of next frame exposure,<wbr/> which doesn't reflect the
+definition from sensor manufacturer.<wbr/> A mobile sensor defines the frame duration as
+intervals between sensor readouts.<wbr/></p>
             </td>
           </tr>
 
@@ -29387,8 +29973,8 @@
             </td> <!-- entry_type -->
 
             <td class="entry_description">
-              <p>Duration from start of frame exposure to
-start of next frame exposure.<wbr/></p>
+              <p>Duration from start of frame readout to
+start of next frame readout.<wbr/></p>
             </td>
 
             <td class="entry_units">
@@ -29476,6 +30062,10 @@
 <p>For more details about stalling,<wbr/> see <a href="https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap.html#getOutputStallDuration">StreamConfigurationMap#getOutputStallDuration</a>.<wbr/></p>
 <p>This control is only effective if <a href="#controls_android.control.aeMode">android.<wbr/>control.<wbr/>ae<wbr/>Mode</a> or <a href="#controls_android.control.mode">android.<wbr/>control.<wbr/>mode</a> is set to
 OFF; otherwise the auto-exposure algorithm will override this value.<wbr/></p>
+<p><em>Note:</em> Prior to Android 13,<wbr/> this field was described as measuring the duration from
+start of frame exposure to start of next frame exposure,<wbr/> which doesn't reflect the
+definition from sensor manufacturer.<wbr/> A mobile sensor defines the frame duration as
+intervals between sensor readouts.<wbr/></p>
             </td>
           </tr>
 
@@ -33759,6 +34349,181 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="dynamic_android.statistics.lensIntrinsicsSamples">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>statistics.<wbr/>lens<wbr/>Intrinsics<wbr/>Samples
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">float</span>
+                <span class="entry_type_container">x</span>
+
+                <span class="entry_type_array">
+                  n
+                </span>
+              <span class="entry_type_visibility"> [java_public as lensIntrinsicsSample]</span>
+
+              <span class="entry_type_synthetic">[synthetic] </span>
+
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>An array of intra-frame lens intrinsic samples.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>Contains an array of intra-frame <a href="#static_android.lens.intrinsicCalibration">android.<wbr/>lens.<wbr/>intrinsic<wbr/>Calibration</a> updates.<wbr/> This must
+not be confused or compared to <a href="#dynamic_android.statistics.oisSamples">android.<wbr/>statistics.<wbr/>ois<wbr/>Samples</a>.<wbr/> Although OIS could be the
+main driver,<wbr/> all relevant factors such as focus distance and optical zoom must also
+be included.<wbr/> Do note that OIS samples must not be applied on top of the lens intrinsic
+samples.<wbr/>
+Support for this capture result can be queried via
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#getAvailableCaptureResultKeys">CameraCharacteristics#getAvailableCaptureResultKeys</a>.<wbr/>
+If available,<wbr/> clients can expect multiple samples per capture result.<wbr/> The specific
+amount will depend on current frame duration and sampling rate.<wbr/> Generally a sampling rate
+greater than or equal to 200Hz is considered sufficient for high quality results.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
+                
+          <tr class="entry" id="dynamic_android.statistics.lensIntrinsicTimestamps">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>statistics.<wbr/>lens<wbr/>Intrinsic<wbr/>Timestamps
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int64</span>
+                <span class="entry_type_container">x</span>
+
+                <span class="entry_type_array">
+                  n
+                </span>
+              <span class="entry_type_visibility"> [ndk_public]</span>
+
+
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>An array of timestamps of lens intrinsics samples,<wbr/> in nanoseconds.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+              nanoseconds
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>The array contains the timestamps of lens intrinsics samples.<wbr/> The timestamps are in the
+same timebase as and comparable to <a href="#dynamic_android.sensor.timestamp">android.<wbr/>sensor.<wbr/>timestamp</a>.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
+                
+          <tr class="entry" id="dynamic_android.statistics.lensIntrinsicSamples">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>statistics.<wbr/>lens<wbr/>Intrinsic<wbr/>Samples
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">float</span>
+                <span class="entry_type_container">x</span>
+
+                <span class="entry_type_array">
+                  5 x n
+                </span>
+              <span class="entry_type_visibility"> [ndk_public]</span>
+
+
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>An array of intra-frame lens intrinsics.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+              
+          Pixels in the android.<wbr/>sensor.<wbr/>info.<wbr/>pre<wbr/>Correction<wbr/>Active<wbr/>Array<wbr/>Size coordinate system.<wbr/>
+          
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>The data layout and contents of individual array entries matches with
+<a href="#static_android.lens.intrinsicCalibration">android.<wbr/>lens.<wbr/>intrinsic<wbr/>Calibration</a>.<wbr/></p>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -35714,6 +36479,13 @@
                     <span class="entry_type_enum_notes"><p>This camera device supports and opts in to the buffer management APIs provided by
 HIDL ICameraDevice version 3.<wbr/>5.<wbr/></p></span>
                   </li>
+                  <li>
+                    <span class="entry_type_enum_name">SESSION_CONFIGURABLE (v3.9)</span>
+                    <span class="entry_type_enum_notes"><p>This camera device supports the buffer management APIs provided by AIDL ICameraDevice
+version 1.<wbr/> It also supports the ICameraDeviceSession.<wbr/>configureStreamsV2 call to
+inform the camera framework whether HAL buffer manager must be used for the
+particular session configured.<wbr/></p></span>
+                  </li>
                 </ul>
 
             </td> <!-- entry_type -->
@@ -35866,6 +36638,339 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="static_android.info.sessionConfigurationQueryVersion">
+            <td class="entry_name
+             " rowspan="3">
+              android.<wbr/>info.<wbr/>session<wbr/>Configuration<wbr/>Query<wbr/>Version
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name entry_type_name_enum">int32</span>
+
+              <span class="entry_type_visibility"> [fwk_java_public as versionCode]</span>
+
+
+              <span class="entry_type_hwlevel">[legacy] </span>
+
+
+
+                <ul class="entry_type_enum">
+                  <li>
+                    <span class="entry_type_enum_name">UPSIDE_DOWN_CAKE (v3.2)</span>
+                    <span class="entry_type_enum_value">34</span>
+                  </li>
+                  <li>
+                    <span class="entry_type_enum_name">VANILLA_ICE_CREAM (v3.2)</span>
+                    <span class="entry_type_enum_value">35</span>
+                  </li>
+                </ul>
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>The version of the session configuration query
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>
+API</p>
+            </td>
+
+            <td class="entry_units">
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>2</p>
+            </td>
+
+            <td class="entry_tags">
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>The possible values in this key correspond to the values defined in
+android.<wbr/>os.<wbr/>Build.<wbr/>VERSION_<wbr/>CODES.<wbr/> Each version defines a set of feature combinations the
+camera device must reliably report whether they are supported via
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>
+API.<wbr/> And the version is always less or equal to android.<wbr/>os.<wbr/>Build.<wbr/>VERSION.<wbr/>SDK_<wbr/>INT.<wbr/></p>
+<p>If set to UPSIDE_<wbr/>DOWN_<wbr/>CAKE,<wbr/> this camera device doesn't support
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>.<wbr/>
+Calling the method for this camera ID throws an UnsupportedOperationException.<wbr/></p>
+<p>If set to VANILLA_<wbr/>ICE_<wbr/>CREAM,<wbr/> the application can call
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#isSessionConfigurationWithParametersSupported">CameraManager#isSessionConfigurationWithParametersSupported</a>
+to check if the combinations of below features are supported.<wbr/></p>
+<ul>
+<li>A subset of LIMITED-level device stream combinations.<wbr/></li>
+</ul>
+<table>
+<thead>
+<tr>
+<th style="text-align: center;">Target 1</th>
+<th style="text-align: center;">Size</th>
+<th style="text-align: center;">Target 2</th>
+<th style="text-align: center;">Size</th>
+<th style="text-align: center;">Sample use case(s)</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;">Simple preview,<wbr/> GPU video processing,<wbr/> or no-preview video recording.<wbr/></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;">In-application video/<wbr/>image processing.<wbr/></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;">Standard still imaging.<wbr/></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;">In-app processing plus still capture.<wbr/></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">MAXIMUM</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;">JPEG</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;">Standard recording.<wbr/></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">PREVIEW</td>
+<td style="text-align: center;">Preview plus in-app processing.<wbr/></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1440P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S1080P</td>
+<td style="text-align: center;"></td>
+</tr>
+<tr>
+<td style="text-align: center;">PRIV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;">YUV</td>
+<td style="text-align: center;">S720P</td>
+<td style="text-align: center;"></td>
+</tr>
+</tbody>
+</table>
+<pre><code>- {@code MAXIMUM} size refers to the camera device's maximum output resolution for
+  that format from {@code StreamConfigurationMap#getOutputSizes}.<wbr/> {@code PREVIEW} size
+  refers to the best size match to the device's screen resolution,<wbr/> or to 1080p
+  (@code 1920x1080},<wbr/> whichever is smaller.<wbr/> Both sizes are guaranteed to be supported.<wbr/>
+
+- {@code S1440P} refers to {@code 1920x1440 (4:3)} and {@code 2560x1440 (16:9)}.<wbr/>
+  {@code S1080P} refers to {@code 1440x1080 (4:3)} and {@code 1920x1080 (16:9)}.<wbr/>
+  And {@code S720P} refers to {@code 960x720 (4:3)} and {@code 1280x720 (16:9)}.<wbr/>
+
+- If a combination contains a S1440P,<wbr/> S1080P,<wbr/> or S720P stream,<wbr/>
+  both 4:3 and 16:9 aspect ratio sizes can be queried.<wbr/> For example,<wbr/> for the
+  stream combination of {PRIV,<wbr/> S1440P,<wbr/> JPEG,<wbr/> MAXIMUM},<wbr/> and if MAXIMUM ==
+  4032 x 3024,<wbr/> the application will be able to query both
+  {PRIV,<wbr/> 1920 x 1440,<wbr/> JPEG,<wbr/> 4032 x 3024} and {PRIV,<wbr/> 2560 x 1440,<wbr/> JPEG,<wbr/> 4032 x 2268}
+  without an exception being thrown.<wbr/>
+</code></pre>
+<ul>
+<li>VIDEO_<wbr/>STABILIZATION_<wbr/>MODES: {OFF,<wbr/> PREVIEW}</li>
+<li>AE_<wbr/>TARGET_<wbr/>FPS_<wbr/>RANGE: {{<em>,<wbr/> 30},<wbr/> {</em>,<wbr/> 60}}</li>
+<li>DYNAMIC_<wbr/>RANGE_<wbr/>PROFILE: {STANDARD,<wbr/> HLG10}</li>
+</ul>
+            </td>
+          </tr>
+
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -37894,7 +38999,7 @@
                 
           <tr class="entry" id="static_android.logicalMultiCamera.physicalIds">
             <td class="entry_name
-             " rowspan="3">
+             " rowspan="5">
               android.<wbr/>logical<wbr/>Multi<wbr/>Camera.<wbr/>physical<wbr/>Ids
             </td>
             <td class="entry_type">
@@ -37950,6 +39055,28 @@
             </td>
           </tr>
 
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">HAL Implementation Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>Each physical camera id should uniquely identify a camera lens in the system.<wbr/>
+So if each camera lens only backs one logical camera,<wbr/> all camera IDs in the system,<wbr/>
+physical IDs or non-physical IDs,<wbr/> should be unique.<wbr/></p>
+<p>In rare cases,<wbr/> one camera lens backs two different logical cameras,<wbr/> the
+physicalIds of both logical cameras should contain a physical camera ID
+identifying that same camera lens.<wbr/> For example,<wbr/> if the mobile device has 3 rear facing
+cameras and no front facing cameras,<wbr/> and the 3 rear facing lenses may be modelled as
+2 logical cameras:</p>
+<ul>
+<li>"device@1.<wbr/>0/<wbr/>internal/<wbr/>10": physicalIds: "camera0",<wbr/> "camera42"</li>
+<li>"device@1.<wbr/>0/<wbr/>internal/<wbr/>11": physicalIds: "camera1",<wbr/> "camera42"</li>
+</ul>
+<p>In this case,<wbr/> the two logical cameras are conflicting devices because they are backed
+by a common lens.<wbr/></p>
+<p>Physical camera IDs can be an arbitrary string not containing '\0'.<wbr/></p>
+            </td>
+          </tr>
 
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
@@ -38133,6 +39260,106 @@
           <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
            <!-- end of entry -->
         
+                
+          <tr class="entry" id="dynamic_android.logicalMultiCamera.activePhysicalSensorCropRegion">
+            <td class="entry_name
+             " rowspan="5">
+              android.<wbr/>logical<wbr/>Multi<wbr/>Camera.<wbr/>active<wbr/>Physical<wbr/>Sensor<wbr/>Crop<wbr/>Region
+            </td>
+            <td class="entry_type">
+                <span class="entry_type_name">int32</span>
+                <span class="entry_type_container">x</span>
+
+                <span class="entry_type_array">
+                  4
+                </span>
+              <span class="entry_type_visibility"> [public as rectangle]</span>
+
+
+
+
+
+
+            </td> <!-- entry_type -->
+
+            <td class="entry_description">
+              <p>The current region of the active physical sensor that will be read out for this
+capture.<wbr/></p>
+            </td>
+
+            <td class="entry_units">
+              Pixel coordinates relative to
+        android.<wbr/>sensor.<wbr/>info.<wbr/>active<wbr/>Array<wbr/>Size or
+        android.<wbr/>sensor.<wbr/>info.<wbr/>pre<wbr/>Correction<wbr/>Active<wbr/>Array<wbr/>Size of the currently
+        android.<wbr/>logical<wbr/>Multi<wbr/>Camera.<wbr/>active<wbr/>Physical<wbr/>Id depending on distortion correction capability
+        and mode
+            </td>
+
+            <td class="entry_range">
+            </td>
+
+            <td class="entry_hal_version">
+              <p>3.<wbr/>9</p>
+            </td>
+
+            <td class="entry_tags">
+              <ul class="entry_tags">
+                  <li><a href="#tag_LOGICALCAMERA">LOGICALCAMERA</a></li>
+              </ul>
+            </td>
+
+          </tr>
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>This capture result matches with <a href="#controls_android.scaler.cropRegion">android.<wbr/>scaler.<wbr/>crop<wbr/>Region</a> on non-logical single
+camera sensor devices.<wbr/> In case of logical cameras that can switch between several
+physical devices in response to <a href="#controls_android.control.zoomRatio">android.<wbr/>control.<wbr/>zoom<wbr/>Ratio</a>,<wbr/> this capture result will
+not behave like <a href="#controls_android.scaler.cropRegion">android.<wbr/>scaler.<wbr/>crop<wbr/>Region</a> and <a href="#controls_android.control.zoomRatio">android.<wbr/>control.<wbr/>zoom<wbr/>Ratio</a>,<wbr/> where the
+combination of both reflects the effective zoom and crop of the logical camera output.<wbr/>
+Instead,<wbr/> this capture result value will describe the zoom and crop of the active physical
+device.<wbr/> Some examples of when the value of this capture result will change include
+switches between different physical lenses,<wbr/> switches between regular and maximum
+resolution pixel mode and going through the device digital or optical range.<wbr/>
+This capture result is similar to <a href="#controls_android.scaler.cropRegion">android.<wbr/>scaler.<wbr/>crop<wbr/>Region</a> with respect to distortion
+correction.<wbr/> When the distortion correction mode is OFF,<wbr/> the coordinate system follows
+<a href="#static_android.sensor.info.preCorrectionActiveArraySize">android.<wbr/>sensor.<wbr/>info.<wbr/>pre<wbr/>Correction<wbr/>Active<wbr/>Array<wbr/>Size</a>,<wbr/> with (0,<wbr/> 0) being the top-left pixel
+of the pre-correction active array.<wbr/> When the distortion correction mode is not OFF,<wbr/>
+the coordinate system follows <a href="#static_android.sensor.info.activeArraySize">android.<wbr/>sensor.<wbr/>info.<wbr/>active<wbr/>Array<wbr/>Size</a>,<wbr/> with (0,<wbr/> 0) being
+the top-left pixel of the active array.<wbr/></p>
+<p>For camera devices with the
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR">Camera<wbr/>Metadata#REQUEST_<wbr/>AVAILABLE_<wbr/>CAPABILITIES_<wbr/>ULTRA_<wbr/>HIGH_<wbr/>RESOLUTION_<wbr/>SENSOR</a>
+capability or devices where <a href="https://developer.android.com/reference/CameraCharacteristics.html#getAvailableCaptureRequestKeys">CameraCharacteristics#getAvailableCaptureRequestKeys</a>
+lists <a href="https://developer.android.com/reference/CaptureRequest.html#SENSOR_PIXEL_MODE"><a href="#controls_android.sensor.pixelMode">android.<wbr/>sensor.<wbr/>pixel<wbr/>Mode</a></a>
+,<wbr/> the current active physical device
+<a href="#static_android.sensor.info.activeArraySizeMaximumResolution">android.<wbr/>sensor.<wbr/>info.<wbr/>active<wbr/>Array<wbr/>Size<wbr/>Maximum<wbr/>Resolution</a> /<wbr/>
+<a href="#static_android.sensor.info.preCorrectionActiveArraySizeMaximumResolution">android.<wbr/>sensor.<wbr/>info.<wbr/>pre<wbr/>Correction<wbr/>Active<wbr/>Array<wbr/>Size<wbr/>Maximum<wbr/>Resolution</a> must be used as the
+coordinate system for requests where <a href="#controls_android.sensor.pixelMode">android.<wbr/>sensor.<wbr/>pixel<wbr/>Mode</a> is set to
+<a href="https://developer.android.com/reference/android/hardware/camera2/CameraMetadata.html#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION">Camera<wbr/>Metadata#SENSOR_<wbr/>PIXEL_<wbr/>MODE_<wbr/>MAXIMUM_<wbr/>RESOLUTION</a>.<wbr/></p>
+            </td>
+          </tr>
+
+          <tr class="entries_header">
+            <th class="th_details" colspan="6">HAL Implementation Details</th>
+          </tr>
+          <tr class="entry_cont">
+            <td class="entry_details" colspan="6">
+              <p>The output streams must maintain square pixels at all
+times,<wbr/> no matter what the relative aspect ratios of the
+crop region and the stream are.<wbr/>  Negative values for
+corner are allowed for raw output if full pixel array is
+larger than active pixel array.<wbr/> Width and height may be
+rounded to nearest larger supportable width,<wbr/> especially
+for raw output,<wbr/> where only a few fixed scales may be
+possible.<wbr/></p>
+            </td>
+          </tr>
+
+          <tr class="entry_spacer"><td class="entry_spacer" colspan="7"></td></tr>
+           <!-- end of entry -->
+        
         
 
       <!-- end of kind -->
@@ -40373,6 +41600,7 @@
           <li><a href="#static_android.logicalMultiCamera.physicalIds">android.logicalMultiCamera.physicalIds</a> (static)</li>
           <li><a href="#static_android.logicalMultiCamera.sensorSyncType">android.logicalMultiCamera.sensorSyncType</a> (static)</li>
           <li><a href="#dynamic_android.logicalMultiCamera.activePhysicalId">android.logicalMultiCamera.activePhysicalId</a> (dynamic)</li>
+          <li><a href="#dynamic_android.logicalMultiCamera.activePhysicalSensorCropRegion">android.logicalMultiCamera.activePhysicalSensorCropRegion</a> (dynamic)</li>
         </ul>
       </li> <!-- tag_LOGICALCAMERA -->
       <li id="tag_HEIC">HEIC - 
diff --git a/camera/docs/metadata_definitions.xml b/camera/docs/metadata_definitions.xml
index b69fc33..34b3253 100644
--- a/camera/docs/metadata_definitions.xml
+++ b/camera/docs/metadata_definitions.xml
@@ -73,6 +73,9 @@
     <typedef name="imageFormat">
       <language name="java">int</language>
     </typedef>
+    <typedef name="versionCode">
+      <language name="java">int</language>
+    </typedef>
     <typedef name="streamConfigurationMap">
       <language name="java">android.hardware.camera2.params.StreamConfigurationMap</language>
     </typedef>
@@ -155,6 +158,9 @@
     <typedef name="colorSpaceProfiles">
         <language name="java">android.hardware.camera2.params.ColorSpaceProfiles</language>
     </typedef>
+    <typedef name="lensIntrinsicsSample">
+        <language name="java">android.hardware.camera2.params.LensIntrinsicsSample</language>
+    </typedef>
   </types>
 
   <namespace name="android">
@@ -674,6 +680,47 @@
                 flash.
               </notes>
             </value>
+            <value optional="true" hal_version="3.9"
+                aconfig_flag="camera_ae_mode_low_light_boost">ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY
+              <notes>
+                Like 'ON' but applies additional brightness boost in low light scenes.
+
+                When the scene lighting conditions are within the range defined by
+                android.control.lowLightBoostInfoLuminanceRange this mode will apply additional
+                brightness boost.
+
+                This mode will automatically adjust the intensity of low light boost applied
+                according to the scene lighting conditions. A darker scene will receive more boost
+                while a brighter scene will receive less boost.
+
+                This mode can ignore the set target frame rate to allow more light to be captured
+                which can result in choppier motion. The frame rate can extend to lower than the
+                android.control.aeAvailableTargetFpsRanges but will not go below 10 FPS. This mode
+                can also increase the sensor sensitivity gain which can result in increased luma
+                and chroma noise. The sensor sensitivity gain can extend to higher values beyond
+                android.sensor.info.sensitivityRange. This mode may also apply additional
+                processing to recover details in dark and bright areas of the image,and noise
+                reduction at high sensitivity gain settings to manage the trade-off between light
+                sensitivity and capture noise.
+
+                This mode is restricted to two output surfaces. One output surface type can either
+                be SurfaceView or TextureView. Another output surface type can either be MediaCodec
+                or MediaRecorder. This mode cannot be used with a target FPS range higher than 30
+                FPS.
+
+                If the session configuration is not supported, the AE mode reported in the
+                CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.
+
+                The application can observe the CapturerResult field
+                android.control.lowLightBoostState to determine when low light boost is 'ACTIVE' or
+                'INACTIVE'.
+
+                The low light boost is 'ACTIVE' once the scene lighting condition is less than the
+                upper bound lux value defined by android.control.lowLightBoostInfoLuminanceRange.
+                This mode will be 'INACTIVE' once the scene lighting condition is greater than the
+                upper bound lux value defined by android.control.lowLightBoostInfoLuminanceRange.
+              </notes>
+            </value>
           </enum>
           <description>The desired mode for the camera device's
           auto-exposure routine.</description>
@@ -3841,6 +3888,61 @@
         </details>
       </entry>
     </dynamic>
+    <static>
+        <entry name="lowLightBoostInfoLuminanceRange" type="float" visibility="public"
+               optional="true" container="array" typedef="rangeFloat"
+               aconfig_flag="camera_ae_mode_low_light_boost" hal_version="3.9">
+          <array>
+            <size>2</size>
+          </array>
+          <description>
+            The operating luminance range of low light boost measured in lux (lx).
+          </description>
+          <range>
+            The lower bound indicates the lowest scene luminance value the AE mode
+            'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' can operate within. Scenes of lower luminance
+            than this may receive less brightening, increased noise, or artifacts.
+
+            The upper bound indicates the luminance threshold at the point when the mode is enabled.
+            For example, 'Range[0.3, 30.0]' defines 0.3 lux being the lowest scene luminance the
+            mode can reliably support. 30.0 lux represents the threshold when this mode is
+            activated. Scenes measured at less than or equal to 30 lux will activate low light
+            boost.
+
+            If this key is defined, then the AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' will
+            also be present.
+          </range>
+        </entry>
+      </static>
+      <dynamic>
+        <entry name="lowLightBoostState" type="byte" visibility="public" optional="true" enum="true"
+               aconfig_flag="camera_ae_mode_low_light_boost" hal_version="3.9">
+          <enum>
+            <value>INACTIVE
+            <notes>
+              The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled but not applied.
+            </notes></value>
+            <value>ACTIVE
+            <notes>
+              The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled and applied.
+            </notes></value>
+          </enum>
+          <description>
+              Current state of the low light boost AE mode.
+          </description>
+          <details>
+            When low light boost is enabled by setting the AE mode to
+            'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
+            boost when the light level threshold is exceeded.
+
+            This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
+            indicate when it is not being applied by returning 'INACTIVE'.
+
+            This key will be absent from the CaptureResult if AE mode is not set to
+            'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.
+          </details>
+        </entry>
+      </dynamic>
     </section>
     <section name="demosaic">
       <controls>
@@ -4153,6 +4255,102 @@
           </details>
         </entry>
       </dynamic>
+      <controls>
+        <entry name="strengthLevel" type="int32" visibility="public" hwlevel="legacy"
+          aconfig_flag="camera_manual_flash_strength_control" hal_version="3.9">
+          <description>Flash strength level to be used when manual flash control is active.
+          </description>
+          <range>`[1-android.flash.info.torchStrengthMaxLevel]` when the android.flash.mode is
+            set to TORCH;
+            `[1-android.flash.info.singleStrengthMaxLevel]` when the android.flash.mode is
+            set to SINGLE
+          </range>
+          <details>Flash strength level to use in capture mode i.e. when the applications control
+            flash with either SINGLE or TORCH mode.
+
+            Use android.flash.info.singleStrengthMaxLevel and
+            android.flash.info.torchStrengthMaxLevel to check whether the device supports
+            flash strength control or not.
+            If the values of android.flash.info.singleStrengthMaxLevel and
+            android.flash.info.torchStrengthMaxLevel are greater than 1,
+            then the device supports manual flash strength control.
+
+            If the android.flash.mode `==` TORCH the value must be &amp;gt;= 1
+            and &amp;lt;= android.flash.info.torchStrengthMaxLevel.
+            If the application doesn't set the key and
+            android.flash.info.torchStrengthMaxLevel &amp;gt; 1,
+            then the flash will be fired at the default level set by HAL in
+            android.flash.info.torchStrengthDefaultLevel.
+            If the android.flash.mode `==` SINGLE, then the value must be &amp;gt;= 1
+            and &amp;lt;= android.flash.info.singleStrengthMaxLevel.
+            If the application does not set this key and
+            android.flash.info.singleStrengthMaxLevel &amp;gt; 1,
+            then the flash will be fired at the default level set by HAL
+            in android.flash.info.singleStrengthDefaultLevel.
+            If android.control.aeMode is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
+            ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.
+          </details>
+        </entry>
+      </controls>
+      <static>
+        <entry name="singleStrengthMaxLevel" type="int32" visibility="public" hwlevel="legacy"
+               aconfig_flag="camera_manual_flash_strength_control" hal_version="3.9">
+          <description>Maximum flash brightness level for manual flash control in SINGLE mode.
+          </description>
+          <details>
+            Maximum flash brightness level in camera capture mode and
+            android.flash.mode set to SINGLE.
+            Value will be &amp;gt; 1 if the manual flash strength control feature is supported,
+            otherwise the value will be equal to 1.
+            Note that this level is just a number of supported levels (the granularity of control).
+            There is no actual physical power units tied to this level.
+          </details>
+        </entry>
+        <entry name="singleStrengthDefaultLevel" type="int32" visibility="public" hwlevel="legacy"
+               aconfig_flag="camera_manual_flash_strength_control" hal_version="3.9">
+          <description>Default flash brightness level for manual flash control in SINGLE mode.
+          </description>
+          <details>
+            If flash unit is available this will be greater than or equal to 1 and less
+            or equal to `android.flash.info.singleStrengthMaxLevel`.
+            Note for devices that do not support the manual flash strength control
+            feature, this level will always be equal to 1.
+          </details>
+        </entry>
+        <entry name="torchStrengthMaxLevel" type="int32" visibility="public" hwlevel="legacy"
+               aconfig_flag="camera_manual_flash_strength_control" hal_version="3.9">
+          <description>Maximum flash brightness level for manual flash control in TORCH mode
+          </description>
+          <details>
+            Maximum flash brightness level in camera capture mode and
+            android.flash.mode set to TORCH.
+            Value will be &amp;gt; 1 if the manual flash strength control feature is supported,
+            otherwise the value will be equal to 1.
+
+            Note that this level is just a number of supported levels(the granularity of control).
+            There is no actual physical power units tied to this level.
+            There is no relation between android.flash.info.torchStrengthMaxLevel and
+            android.flash.info.singleStrengthMaxLevel i.e. the ratio of
+            android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel
+            is not guaranteed to be the ratio of actual brightness.
+          </details>
+        </entry>
+        <entry name="torchStrengthDefaultLevel" type="int32" visibility="public" hwlevel="legacy"
+               aconfig_flag="camera_manual_flash_strength_control" hal_version="3.9">
+          <description>Default flash brightness level for manual flash control in TORCH mode
+          </description>
+          <details>
+            If flash unit is available this will be greater than or equal to 1 and less
+            or equal to android.flash.info.torchStrengthMaxLevel.
+            Note for the devices that do not support the manual flash strength control feature,
+            this level will always be equal to 1.
+          </details>
+        </entry>
+      </static>
+    <dynamic>
+    <clone entry="android.flash.strengthLevel" kind="controls">
+    </clone>
+    </dynamic>
     </section>
     <section name="hotPixel">
       <controls>
@@ -6320,10 +6518,10 @@
 
               Combinations of logical and physical streams, or physical streams from different
               physical cameras are not guaranteed. However, if the camera device supports
-              {@link CameraDevice#isSessionConfigurationSupported|ACameraDevice_isSessionConfigurationSupported},
+              {@link CameraManager#isSessionConfigurationWithParametersSupported|ACameraDevice_isSessionConfigurationSupported},
               application must be able to query whether a stream combination involving physical
               streams is supported by calling
-              {@link CameraDevice#isSessionConfigurationSupported|ACameraDevice_isSessionConfigurationSupported}.
+              {@link CameraManager#isSessionConfigurationWithParametersSupported|ACameraDevice_isSessionConfigurationSupported}.
 
               Camera application shouldn't assume that there are at most 1 rear camera and 1 front
               camera in the system. For an application that switches between front and back cameras,
@@ -8669,7 +8867,7 @@
             When the key is present, only a PRIVATE/YUV output of the specified size is guaranteed
             to be supported by the camera HAL in the secure camera mode. Any other format or
             resolutions might not be supported. Use
-            {@link CameraDevice#isSessionConfigurationSupported|ACameraDevice_isSessionConfigurationSupported}
+            {@link CameraManager#isSessionConfigurationWithParametersSupported|ACameraDevice_isSessionConfigurationSupported}
             API to query if a secure session configuration is supported if the device supports this
             API.
 
@@ -9323,8 +9521,8 @@
           <tag id="V1" />
         </entry>
         <entry name="frameDuration" type="int64" visibility="public" hwlevel="full">
-          <description>Duration from start of frame exposure to
-          start of next frame exposure.</description>
+          <description>Duration from start of frame readout to
+          start of next frame readout.</description>
           <units>Nanoseconds</units>
           <range>See android.sensor.info.maxFrameDuration, {@link
           android.hardware.camera2.params.StreamConfigurationMap|ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS}.
@@ -9399,6 +9597,11 @@
 
           This control is only effective if android.control.aeMode or android.control.mode is set to
           OFF; otherwise the auto-exposure algorithm will override this value.
+
+          *Note:* Prior to Android 13, this field was described as measuring the duration from
+          start of frame exposure to start of next frame exposure, which doesn't reflect the
+          definition from sensor manufacturer. A mobile sensor defines the frame duration as
+          intervals between sensor readouts.
           </details>
           <hal_details>
           For more details about stalling, see
@@ -12034,6 +12237,59 @@
           is needed.
           </details>
         </entry>
+        <entry name="lensIntrinsicsSamples" type="float" visibility="java_public" synthetic="true"
+               container="array" typedef="lensIntrinsicsSample" aconfig_flag="concert_mode"
+               hal_version="3.9">
+          <array>
+            <size>n</size>
+          </array>
+          <description>
+          An array of intra-frame lens intrinsic samples.
+          </description>
+          <details>
+          Contains an array of intra-frame android.lens.intrinsicCalibration updates. This must
+          not be confused or compared to android.statistics.oisSamples. Although OIS could be the
+          main driver, all relevant factors such as focus distance and optical zoom must also
+          be included. Do note that OIS samples must not be applied on top of the lens intrinsic
+          samples.
+          Support for this capture result can be queried via
+          {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys}.
+          If available, clients can expect multiple samples per capture result. The specific
+          amount will depend on current frame duration and sampling rate. Generally a sampling rate
+          greater than or equal to 200Hz is considered sufficient for high quality results.
+          </details>
+        </entry>
+        <entry name="lensIntrinsicTimestamps" type="int64" visibility="ndk_public" container="array"
+               aconfig_flag="concert_mode" hal_version="3.9">
+          <array>
+            <size>n</size>
+          </array>
+          <description>
+          An array of timestamps of lens intrinsics samples, in nanoseconds.
+          </description>
+          <units>nanoseconds</units>
+          <details>
+          The array contains the timestamps of lens intrinsics samples. The timestamps are in the
+          same timebase as and comparable to android.sensor.timestamp.
+          </details>
+        </entry>
+        <entry name="lensIntrinsicSamples" type="float" visibility="ndk_public"
+               container="array" aconfig_flag="concert_mode" hal_version="3.9">
+          <array>
+            <size>5</size>
+            <size>n</size>
+          </array>
+          <description>
+          An array of intra-frame lens intrinsics.
+          </description>
+          <units>
+          Pixels in the android.sensor.info.preCorrectionActiveArraySize coordinate system.
+          </units>
+          <details>
+          The data layout and contents of individual array entries matches with
+          android.lens.intrinsicCalibration.
+          </details>
+        </entry>
       </dynamic>
     </section>
     <section name="tonemap">
@@ -12685,6 +12941,15 @@
               HIDL ICameraDevice version 3.5.
               </notes>
             </value>
+            <value hal_version="3.9" aconfig_flag="session_hal_buf_manager">
+              SESSION_CONFIGURABLE
+              <notes>
+              This camera device supports the buffer management APIs provided by AIDL ICameraDevice
+              version 1. It also supports the ICameraDeviceSession.configureStreamsV2 call to
+              inform the camera framework whether HAL buffer manager must be used for the
+              particular session configured.
+              </notes>
+            </value>
           </enum>
           <description>
               The version of buffer management API this camera device supports and opts into.
@@ -12725,6 +12990,90 @@
           supported device state bitwise combination.
           </details>
         </entry>
+        <entry name="sessionConfigurationQueryVersion" type="int32"
+          visibility="fwk_java_public" enum="true" typedef="versionCode"
+          hwlevel="legacy" aconfig_flag="feature_combination_query">
+          <enum>
+            <value id="34">UPSIDE_DOWN_CAKE</value>
+            <value id="35">VANILLA_ICE_CREAM</value>
+          </enum>
+          <description>The version of the session configuration query
+          {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported}
+          API
+          </description>
+          <details>The possible values in this key correspond to the values defined in
+          android.os.Build.VERSION_CODES. Each version defines a set of feature combinations the
+          camera device must reliably report whether they are supported via
+          {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported}
+          API. And the version is always less or equal to android.os.Build.VERSION.SDK_INT.
+
+          If set to UPSIDE_DOWN_CAKE, this camera device doesn't support
+          {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported}.
+          Calling the method for this camera ID throws an UnsupportedOperationException.
+
+          If set to VANILLA_ICE_CREAM, the application can call
+          {@link android.hardware.camera2.CameraManager#isSessionConfigurationWithParametersSupported}
+          to check if the combinations of below features are supported.
+
+          * A subset of LIMITED-level device stream combinations.
+
+          Target 1    |     Size      | Target 2    |     Size    | Sample use case(s)
+          :----------:|:-------------:|:-----------:|:-----------:|:-------------------:
+          PRIV        | MAXIMUM       |             |             | Simple preview, GPU video processing, or no-preview video recording.
+          PRIV        | PREVIEW       |             |             |
+          PRIV        | S1440P        |             |             |
+          PRIV        | S1080P        |             |             |
+          PRIV        | S720P         |             |             |
+          YUV         | MAXIMUM       |             |             | In-application video/image processing.
+          YUV         | PREVIEW       |             |             |
+          YUV         | S1440P        |             |             |
+          YUV         | S1080P        |             |             |
+          YUV         | S720P         |             |             |
+          PRIV        | PREVIEW       | JPEG        | MAXIMUM     | Standard still imaging.
+          PRIV        | S1440P        | JPEG        | MAXIMUM     |
+          PRIV        | S1080P        | JPEG        | MAXIMUM     |
+          PRIV        | S720P         | JPEG        | MAXIMUM     |
+          PRIV        | S1440P        | JPEG        | S1440P      |
+          PRIV        | S1080P        | JPEG        | S1080P      |
+          PRIV        | S720P         | JPEG        | S1080P      |
+          YUV         | PREVIEW       | JPEG        | MAXIMUM     | In-app processing plus still capture.
+          YUV         | S1440P        | JPEG        | MAXIMUM     |
+          YUV         | S1080P        | JPEG        | MAXIMUM     |
+          YUV         | S720P         | JPEG        | MAXIMUM     |
+          YUV         | S1440P        | JPEG        | S1440P      |
+          YUV         | S1080P        | JPEG        | S1080P      |
+          YUV         | S720P         | JPEG        | S1080P      |
+          PRIV        | PREVIEW       | PRIV        | PREVIEW     | Standard recording.
+          PRIV        | S1440P        | PRIV        | S1440P      |
+          PRIV        | S1080P        | PRIV        | S1080P      |
+          PRIV        | S720P         | PRIV        | S720P       |
+          PRIV        | PREVIEW       | YUV         | PREVIEW     | Preview plus in-app processing.
+          PRIV        | S1440P        | YUV         | S1440P      |
+          PRIV        | S1080P        | YUV         | S1080P      |
+          PRIV        | S720P         | YUV         | S720P       |
+
+              - {@code MAXIMUM} size refers to the camera device's maximum output resolution for
+                that format from {@code StreamConfigurationMap#getOutputSizes}. {@code PREVIEW} size
+                refers to the best size match to the device's screen resolution, or to 1080p
+                (@code 1920x1080}, whichever is smaller. Both sizes are guaranteed to be supported.
+
+              - {@code S1440P} refers to {@code 1920x1440 (4:3)} and {@code 2560x1440 (16:9)}.
+                {@code S1080P} refers to {@code 1440x1080 (4:3)} and {@code 1920x1080 (16:9)}.
+                And {@code S720P} refers to {@code 960x720 (4:3)} and {@code 1280x720 (16:9)}.
+
+              - If a combination contains a S1440P, S1080P, or S720P stream,
+                both 4:3 and 16:9 aspect ratio sizes can be queried. For example, for the
+                stream combination of {PRIV, S1440P, JPEG, MAXIMUM}, and if MAXIMUM ==
+                4032 x 3024, the application will be able to query both
+                {PRIV, 1920 x 1440, JPEG, 4032 x 3024} and {PRIV, 2560 x 1440, JPEG, 4032 x 2268}
+                without an exception being thrown.
+
+          * VIDEO_STABILIZATION_MODES: {OFF, PREVIEW}
+          * AE_TARGET_FPS_RANGE: {{*, 30}, {*, 60}}
+          * DYNAMIC_RANGE_PROFILE: {STANDARD, HLG10}
+
+          </details>
+        </entry>
       </static>
     </section>
     <section name="blackLevel">
@@ -13480,6 +13829,25 @@
 
             The number of physical camera IDs must be no less than 2.
           </details>
+          <hal_details>
+            Each physical camera id should uniquely identify a camera lens in the system.
+            So if each camera lens only backs one logical camera, all camera IDs in the system,
+            physical IDs or non-physical IDs, should be unique.
+
+            In rare cases, one camera lens backs two different logical cameras, the
+            physicalIds of both logical cameras should contain a physical camera ID
+            identifying that same camera lens. For example, if the mobile device has 3 rear facing
+            cameras and no front facing cameras, and the 3 rear facing lenses may be modelled as
+            2 logical cameras:
+
+            - "device@1.0/internal/10": physicalIds: "camera0", "camera42"
+            - "device@1.0/internal/11": physicalIds: "camera1", "camera42"
+
+            In this case, the two logical cameras are conflicting devices because they are backed
+            by a common lens.
+
+            Physical camera IDs can be an arbitrary string not containing '\0'.
+          </hal_details>
           <tag id="LOGICALCAMERA" />
         </entry>
         <entry name="sensorSyncType" type="byte" visibility="public"
@@ -13549,6 +13917,60 @@
         </hal_details>
         <tag id="LOGICALCAMERA" />
       </entry>
+      <entry name="activePhysicalSensorCropRegion" type="int32" visibility="public"
+             container="array" typedef="rectangle" aconfig_flag="concert_mode" hal_version="3.9">
+        <array>
+          <size>4</size>
+        </array>
+        <description>The current region of the active physical sensor that will be read out for this
+        capture.</description>
+        <units>Pixel coordinates relative to
+        android.sensor.info.activeArraySize or
+        android.sensor.info.preCorrectionActiveArraySize of the currently
+        android.logicalMultiCamera.activePhysicalId depending on distortion correction capability
+        and mode</units>
+        <details>
+          This capture result matches with android.scaler.cropRegion on non-logical single
+          camera sensor devices. In case of logical cameras that can switch between several
+          physical devices in response to android.control.zoomRatio, this capture result will
+          not behave like android.scaler.cropRegion and android.control.zoomRatio, where the
+          combination of both reflects the effective zoom and crop of the logical camera output.
+          Instead, this capture result value will describe the zoom and crop of the active physical
+          device. Some examples of when the value of this capture result will change include
+          switches between different physical lenses, switches between regular and maximum
+          resolution pixel mode and going through the device digital or optical range.
+          This capture result is similar to android.scaler.cropRegion with respect to distortion
+          correction. When the distortion correction mode is OFF, the coordinate system follows
+          android.sensor.info.preCorrectionActiveArraySize, with (0, 0) being the top-left pixel
+          of the pre-correction active array. When the distortion correction mode is not OFF,
+          the coordinate system follows android.sensor.info.activeArraySize, with (0, 0) being
+          the top-left pixel of the active array.
+
+          For camera devices with the
+          {@link android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR}
+          capability or devices where {@link CameraCharacteristics#getAvailableCaptureRequestKeys}
+          lists {@link CaptureRequest#SENSOR_PIXEL_MODE android.sensor.pixelMode}
+          , the current active physical device
+          android.sensor.info.activeArraySizeMaximumResolution /
+          android.sensor.info.preCorrectionActiveArraySizeMaximumResolution must be used as the
+          coordinate system for requests where android.sensor.pixelMode is set to
+          {@link android.hardware.camera2.CameraMetadata#SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION}.
+        </details>
+        <ndk_details>
+          The data representation is int[4], which maps to (left, top, width, height).
+        </ndk_details>
+        <hal_details>
+          The output streams must maintain square pixels at all
+          times, no matter what the relative aspect ratios of the
+          crop region and the stream are.  Negative values for
+          corner are allowed for raw output if full pixel array is
+          larger than active pixel array. Width and height may be
+          rounded to nearest larger supportable width, especially
+          for raw output, where only a few fixed scales may be
+          possible.
+        </hal_details>
+        <tag id="LOGICALCAMERA" />
+      </entry>
     </dynamic>
     </section>
     <section name="distortionCorrection">
diff --git a/camera/docs/metadata_definitions.xsd b/camera/docs/metadata_definitions.xsd
index 2a16726..d66e527 100644
--- a/camera/docs/metadata_definitions.xsd
+++ b/camera/docs/metadata_definitions.xsd
@@ -253,6 +253,7 @@
                 </restriction>
             </simpleType>
         </attribute>
+        <attribute name="aconfig_flag" type="string" />
     </complexType>
 
     <complexType name="EnumType">
@@ -327,6 +328,7 @@
         </attribute>
         <attribute name="id" type="string" />
         <attribute name="hal_version" type="decimal" default="3.2" />
+        <attribute name="aconfig_flag" type="string" />
     </complexType>
 
     <complexType name="CloneType">
diff --git a/camera/docs/metadata_helpers.py b/camera/docs/metadata_helpers.py
index f3d52a6..37b3d59 100644
--- a/camera/docs/metadata_helpers.py
+++ b/camera/docs/metadata_helpers.py
@@ -189,6 +189,7 @@
     "deviceStateSensorOrientationMap"  : "DeviceStateSensorOrientationMap",
     "dynamicRangeProfiles"   : "DynamicRangeProfiles",
     "colorSpaceProfiles"     : "ColorSpaceProfiles",
+    "versionCode"            : "int32",
   }
 
   if typeName not in typename_to_protobuftype:
@@ -768,7 +769,7 @@
     if entry.enum and not (entry.typedef and entry.typedef.languages.get('java')):
       text += '\n\n<b>Possible values:</b>\n<ul>\n'
       for value in entry.enum.values:
-        if not value.hidden:
+        if not value.hidden and (value.aconfig_flag == entry.aconfig_flag):
           text += '  <li>{@link #%s %s}</li>\n' % ( jenum_value(entry, value ), value.name )
       text += '</ul>\n'
     if entry.range:
@@ -1622,4 +1623,4 @@
   ]
   return [
     val for val in entry.enum.values if '%s_%s'%(csym(entry.name), val.name) not in ignoreList
-  ]
\ No newline at end of file
+  ]
diff --git a/camera/docs/metadata_model.py b/camera/docs/metadata_model.py
index 4428c90..db11f6b 100644
--- a/camera/docs/metadata_model.py
+++ b/camera/docs/metadata_model.py
@@ -985,9 +985,11 @@
     parent: An edge to the parent, always an Enum instance.
     hal_major_version: The major HIDL HAL version this value was first added in
     hal_minor_version: The minor HIDL HAL version this value was first added in
+    aconfig_flag: The aconfig flag name that determines if this enum value is actually enabled
   """
   def __init__(self, name, parent,
-               id=None, deprecated=False, optional=False, visibility=None, notes=None, sdk_notes=None, ndk_notes=None, hal_version='3.2'):
+               id=None, deprecated=False, optional=False, visibility=None, notes=None,
+               sdk_notes=None, ndk_notes=None, hal_version='3.2', aconfig_flag=None):
     self._name = name                    # str, e.g. 'ON' or 'OFF'
     self._id = id                        # int, e.g. '0'
     self._deprecated = deprecated        # bool
@@ -1008,6 +1010,11 @@
       self._hal_major_version = int(hal_version.partition('.')[0])
       self._hal_minor_version = int(hal_version.partition('.')[2])
 
+    self._aconfig_flag = aconfig_flag
+    if self._aconfig_flag is None:
+      if parent is not None and parent.parent is not None:
+        self._aconfig_flag = parent.parent.aconfig_flag
+
   @property
   def id(self):
     return self._id
@@ -1066,6 +1073,10 @@
   def hal_minor_version(self):
     return self._hal_minor_version
 
+  @property
+  def aconfig_flag(self):
+    return self._aconfig_flag
+
   def _get_children(self):
     return None
 
@@ -1080,12 +1091,14 @@
         non-empty id property.
   """
   def __init__(self, parent, values, ids={}, deprecateds=[],
-               optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={}, hal_versions={}):
+               optionals=[], visibilities={}, notes={}, sdk_notes={}, ndk_notes={},
+               hal_versions={}, aconfig_flags={}):
     self._parent = parent
     self._name = None
     self._values =                                                             \
       [ EnumValue(val, self, ids.get(val), val in deprecateds, val in optionals, visibilities.get(val), \
-                  notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val))        \
+                  notes.get(val), sdk_notes.get(val), ndk_notes.get(val), hal_versions.get(val),        \
+                  aconfig_flags.get(val)) \
         for val in values ]
 
   @property
@@ -1135,6 +1148,7 @@
              hwlevels will also include this entry. None means that the
              entry is optional on any hardware level.
     permission_needed: Flags whether the tag needs extra camera permission.
+    aconfig_flag: The aconfig flag name that determines if the entry is actually enabled
     deprecated: Marks an entry as @Deprecated in the Java layer; if within an
                unreleased version this needs to be removed altogether. If applied
                to an entry from an older release, then this means the entry
@@ -1187,6 +1201,7 @@
       enum_notes: A dictionary of value->notes strings.
       enum_ids: A dictionary of value->id strings.
       enum_hal_versions: A dictionary of value->hal version strings
+      enum_aconfig_flags: A dictionary of value->aconfig flag name strings
 
     Args (if the 'deprecated' attribute is true):
       deprecation_description: A string explaining the deprecation, to be added
@@ -1282,6 +1297,10 @@
   def permission_needed(self):
     return self._permission_needed or "false"
 
+  @property
+  def aconfig_flag(self):
+    return self._aconfig_flag
+
   # TODO: optional should just return hwlevel is None
   @property
   def optional(self):
@@ -1401,6 +1420,8 @@
       self._hal_major_version = int(hal_version.partition('.')[0])
       self._hal_minor_version = int(hal_version.partition('.')[2])
 
+    self._aconfig_flag = kwargs.get('aconfig_flag')
+
     # access these via the 'enum' prop
     enum_values = kwargs.get('enum_values')
     enum_deprecateds = kwargs.get('enum_deprecateds')
@@ -1411,6 +1432,7 @@
     enum_ndk_notes = kwargs.get('enum_ndk_notes')  # { value => ndk_notes }
     enum_ids = kwargs.get('enum_ids')  # { value => notes }
     enum_hal_versions = kwargs.get('enum_hal_versions') # { value => hal_versions }
+    enum_aconfig_flags = kwargs.get('enum_aconfig_flags') # { value => aconfig flags }
 
     self._tuple_values = kwargs.get('tuple_values')
 
@@ -1430,7 +1452,8 @@
 
     if kwargs.get('enum', False):
       self._enum = Enum(self, enum_values, enum_ids, enum_deprecateds, enum_optionals,
-                        enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes, enum_hal_versions)
+                        enum_visibilities, enum_notes, enum_sdk_notes, enum_ndk_notes,
+                        enum_hal_versions, enum_aconfig_flags)
     else:
       self._enum = None
 
@@ -1651,7 +1674,8 @@
                     'typedef',
                     'hal_major_version',
                     'hal_minor_version',
-                    'permission_needed'
+                    'permission_needed',
+                    'aconfig_flag'
                    ]
 
     for p in props_common:
diff --git a/camera/docs/metadata_parser_xml.py b/camera/docs/metadata_parser_xml.py
index 5db2443..f8feeec 100755
--- a/camera/docs/metadata_parser_xml.py
+++ b/camera/docs/metadata_parser_xml.py
@@ -187,6 +187,9 @@
     #
     d['permission_needed'] = entry.get('permission_needed')
 
+    # Aconfig flag gating this entry ?
+    d['aconfig_flag'] = entry.get('aconfig_flag')
+
     #
     # Hardware Level (one of limited, legacy, full)
     #
@@ -225,6 +228,7 @@
       enum_ndk_notes = {}
       enum_ids = {}
       enum_hal_versions = {}
+      enum_aconfig_flags = {}
       for value in entry.enum.find_all('value'):
 
         value_body = self._strings_no_nl(value)
@@ -258,6 +262,9 @@
         if value.attrs.get('hal_version') is not None:
           enum_hal_versions[value_body] = value['hal_version']
 
+        if value.attrs.get('aconfig_flag') is not None:
+          enum_aconfig_flags[value_body] = value['aconfig_flag']
+
       d['enum_values'] = enum_values
       d['enum_deprecateds'] = enum_deprecateds
       d['enum_optionals'] = enum_optionals
@@ -267,6 +274,7 @@
       d['enum_ndk_notes'] = enum_ndk_notes
       d['enum_ids'] = enum_ids
       d['enum_hal_versions'] = enum_hal_versions
+      d['enum_aconfig_flags'] = enum_aconfig_flags
       d['enum'] = True
 
     #
diff --git a/camera/docs/metadata_template.mako b/camera/docs/metadata_template.mako
index a673065..f8c4714 100644
--- a/camera/docs/metadata_template.mako
+++ b/camera/docs/metadata_template.mako
@@ -128,6 +128,10 @@
                 permission_needed="true"
           % endif
 
+          % if prop.aconfig_flag:
+                aconfig_flag="${prop.aconfig_flag}"
+          % endif
+
           % if (prop.hal_major_version, prop.hal_minor_version) != (3,2):
                 hal_version="${prop.hal_major_version}.${prop.hal_minor_version}"
           % endif
@@ -165,6 +169,9 @@
                     % if not (value.hal_major_version == prop.hal_major_version and value.hal_minor_version == prop.hal_minor_version):
                              hal_version=${"%d.%d" % (value.hal_major_version, value.hal_minor_version)}
                     % endif
+                    % if not (value.aconfig_flag == prop.aconfig_flag):
+                             aconfig_flag="${value.aconfig_flag}"
+                    % endif
                       >${value.name}
                     % if value.notes is not None:
                              <notes>${value.notes}</notes>
diff --git a/camera/include/system/camera_metadata_tags.h b/camera/include/system/camera_metadata_tags.h
index edf5fe6..e342526 100644
--- a/camera/include/system/camera_metadata_tags.h
+++ b/camera/include/system/camera_metadata_tags.h
@@ -200,6 +200,9 @@
     ANDROID_CONTROL_AUTOFRAMING,                      // enum         | public       | HIDL v3.9
     ANDROID_CONTROL_AUTOFRAMING_AVAILABLE,            // enum         | public       | HIDL v3.9
     ANDROID_CONTROL_AUTOFRAMING_STATE,                // enum         | public       | HIDL v3.9
+    ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE,
+                                                      // float[]      | public       | HIDL v3.9
+    ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE,            // enum         | public       | HIDL v3.9
     ANDROID_CONTROL_END,
 
     ANDROID_DEMOSAIC_MODE =                           // enum         | system       | HIDL v3.2
@@ -219,6 +222,11 @@
     ANDROID_FLASH_COLOR_TEMPERATURE,                  // byte         | system       | HIDL v3.2
     ANDROID_FLASH_MAX_ENERGY,                         // byte         | system       | HIDL v3.2
     ANDROID_FLASH_STATE,                              // enum         | public       | HIDL v3.2
+    ANDROID_FLASH_STRENGTH_LEVEL,                     // int32        | public       | HIDL v3.9
+    ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,          // int32        | public       | HIDL v3.9
+    ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,      // int32        | public       | HIDL v3.9
+    ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL,           // int32        | public       | HIDL v3.9
+    ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,       // int32        | public       | HIDL v3.9
     ANDROID_FLASH_END,
 
     ANDROID_FLASH_INFO_AVAILABLE =                    // enum         | public       | HIDL v3.2
@@ -448,6 +456,8 @@
     ANDROID_STATISTICS_OIS_TIMESTAMPS,                // int64[]      | ndk_public   | HIDL v3.3
     ANDROID_STATISTICS_OIS_X_SHIFTS,                  // float[]      | ndk_public   | HIDL v3.3
     ANDROID_STATISTICS_OIS_Y_SHIFTS,                  // float[]      | ndk_public   | HIDL v3.3
+    ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS,     // int64[]      | ndk_public   | HIDL v3.9
+    ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES,        // float[]      | ndk_public   | HIDL v3.9
     ANDROID_STATISTICS_END,
 
     ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES = 
@@ -486,6 +496,7 @@
     ANDROID_INFO_VERSION,                             // byte         | public       | HIDL v3.3
     ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION, // enum         | system       | HIDL v3.4
     ANDROID_INFO_DEVICE_STATE_ORIENTATIONS,           // int64[]      | ndk_public   | HIDL v3.7
+    ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION, // enum         | fwk_java_public
     ANDROID_INFO_END,
 
     ANDROID_BLACK_LEVEL_LOCK =                        // enum         | public       | HIDL v3.2
@@ -535,6 +546,8 @@
             ANDROID_LOGICAL_MULTI_CAMERA_START,
     ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE,    // enum         | public       | HIDL v3.3
     ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID,  // byte         | public       | HIDL v3.4
+    ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION,
+                                                      // int32[]      | public       | HIDL v3.9
     ANDROID_LOGICAL_MULTI_CAMERA_END,
 
     ANDROID_DISTORTION_CORRECTION_MODE =              // enum         | public       | HIDL v3.3
@@ -630,6 +643,7 @@
     ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH                         , // HIDL v3.2
     ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE                    , // HIDL v3.2
     ANDROID_CONTROL_AE_MODE_ON_EXTERNAL_FLASH                       , // HIDL v3.3
+    ANDROID_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY  , // HIDL v3.9
 } camera_metadata_enum_android_control_ae_mode_t;
 
 // ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER
@@ -848,6 +862,12 @@
     ANDROID_CONTROL_AUTOFRAMING_STATE_CONVERGED                     , // HIDL v3.9
 } camera_metadata_enum_android_control_autoframing_state_t;
 
+// ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE
+typedef enum camera_metadata_enum_android_control_low_light_boost_state {
+    ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE                  , // HIDL v3.9
+    ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE                    , // HIDL v3.9
+} camera_metadata_enum_android_control_low_light_boost_state_t;
+
 
 // ANDROID_DEMOSAIC_MODE
 typedef enum camera_metadata_enum_android_demosaic_mode {
@@ -1295,8 +1315,18 @@
 // ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION
 typedef enum camera_metadata_enum_android_info_supported_buffer_management_version {
     ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5, // HIDL v3.4
+    ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE
+                                                                     , // HIDL v3.9
 } camera_metadata_enum_android_info_supported_buffer_management_version_t;
 
+// ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION
+typedef enum camera_metadata_enum_android_info_session_configuration_query_version {
+    ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION_UPSIDE_DOWN_CAKE
+                                                                      = 34,
+    ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION_VANILLA_ICE_CREAM
+                                                                      = 35,
+} camera_metadata_enum_android_info_session_configuration_query_version_t;
+
 
 // ANDROID_BLACK_LEVEL_LOCK
 typedef enum camera_metadata_enum_android_black_level_lock {
diff --git a/camera/src/camera_metadata_asserts.cpp b/camera/src/camera_metadata_asserts.cpp
index 4e3748f..d1fd162 100644
--- a/camera/src/camera_metadata_asserts.cpp
+++ b/camera/src/camera_metadata_asserts.cpp
@@ -50,6 +50,7 @@
 #include <aidl/android/hardware/camera/metadata/ControlAutoframing.h>
 #include <aidl/android/hardware/camera/metadata/ControlAutoframingAvailable.h>
 #include <aidl/android/hardware/camera/metadata/ControlAutoframingState.h>
+#include <aidl/android/hardware/camera/metadata/ControlLowLightBoostState.h>
 #include <aidl/android/hardware/camera/metadata/DemosaicMode.h>
 #include <aidl/android/hardware/camera/metadata/EdgeMode.h>
 #include <aidl/android/hardware/camera/metadata/FlashMode.h>
@@ -380,6 +381,10 @@
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_CONTROL_AUTOFRAMING_AVAILABLE));
 static_assert(static_cast<int>(ANDROID_CONTROL_AUTOFRAMING_STATE)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_CONTROL_AUTOFRAMING_STATE));
+static_assert(static_cast<int>(ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE));
+static_assert(static_cast<int>(ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE));
 static_assert(static_cast<int>(ANDROID_DEMOSAIC_MODE)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_DEMOSAIC_MODE));
 static_assert(static_cast<int>(ANDROID_EDGE_MODE)
@@ -400,6 +405,16 @@
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_MAX_ENERGY));
 static_assert(static_cast<int>(ANDROID_FLASH_STATE)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_STATE));
+static_assert(static_cast<int>(ANDROID_FLASH_STRENGTH_LEVEL)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_STRENGTH_LEVEL));
+static_assert(static_cast<int>(ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL));
+static_assert(static_cast<int>(ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL));
+static_assert(static_cast<int>(ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL));
+static_assert(static_cast<int>(ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL));
 static_assert(static_cast<int>(ANDROID_FLASH_INFO_AVAILABLE)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_FLASH_INFO_AVAILABLE));
 static_assert(static_cast<int>(ANDROID_FLASH_INFO_CHARGE_DURATION)
@@ -740,6 +755,10 @@
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_STATISTICS_OIS_X_SHIFTS));
 static_assert(static_cast<int>(ANDROID_STATISTICS_OIS_Y_SHIFTS)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_STATISTICS_OIS_Y_SHIFTS));
+static_assert(static_cast<int>(ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS));
+static_assert(static_cast<int>(ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES));
 static_assert(static_cast<int>(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES));
 static_assert(static_cast<int>(ANDROID_STATISTICS_INFO_HISTOGRAM_BUCKET_COUNT)
@@ -832,6 +851,8 @@
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE));
 static_assert(static_cast<int>(ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID));
+static_assert(static_cast<int>(ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION)
+        == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION));
 static_assert(static_cast<int>(ANDROID_DISTORTION_CORRECTION_MODE)
         == static_cast<int>(::aidl::android::hardware::camera::metadata::CameraMetadataTag::ANDROID_DISTORTION_CORRECTION_MODE));
 static_assert(static_cast<int>(ANDROID_DISTORTION_CORRECTION_AVAILABLE_MODES)
@@ -909,6 +930,8 @@
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlAeMode::ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE));
 static_assert(static_cast<int32_t>(ANDROID_CONTROL_AE_MODE_ON_EXTERNAL_FLASH)
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlAeMode::ANDROID_CONTROL_AE_MODE_ON_EXTERNAL_FLASH));
+static_assert(static_cast<int32_t>(ANDROID_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY)
+        == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlAeMode::ANDROID_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY));
 
 static_assert(static_cast<int32_t>(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE)
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlAePrecaptureTrigger::ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE));
@@ -1152,6 +1175,11 @@
 static_assert(static_cast<int32_t>(ANDROID_CONTROL_AUTOFRAMING_STATE_CONVERGED)
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlAutoframingState::ANDROID_CONTROL_AUTOFRAMING_STATE_CONVERGED));
 
+static_assert(static_cast<int32_t>(ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE)
+        == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlLowLightBoostState::ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE));
+static_assert(static_cast<int32_t>(ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE)
+        == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::ControlLowLightBoostState::ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE));
+
 static_assert(static_cast<int32_t>(ANDROID_DEMOSAIC_MODE_FAST)
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::DemosaicMode::ANDROID_DEMOSAIC_MODE_FAST));
 static_assert(static_cast<int32_t>(ANDROID_DEMOSAIC_MODE_HIGH_QUALITY)
@@ -1599,6 +1627,8 @@
 
 static_assert(static_cast<int32_t>(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_HIDL_DEVICE_3_5)
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::InfoSupportedBufferManagementVersion::ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_AIDL_DEVICE));
+static_assert(static_cast<int32_t>(ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE)
+        == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::InfoSupportedBufferManagementVersion::ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE));
 
 static_assert(static_cast<int32_t>(ANDROID_BLACK_LEVEL_LOCK_OFF)
         == static_cast<int32_t>(::aidl::android::hardware::camera::metadata::BlackLevelLock::ANDROID_BLACK_LEVEL_LOCK_OFF));
diff --git a/camera/src/camera_metadata_tag_info.c b/camera/src/camera_metadata_tag_info.c
index 1dac402..0b93365 100644
--- a/camera/src/camera_metadata_tag_info.c
+++ b/camera/src/camera_metadata_tag_info.c
@@ -279,6 +279,11 @@
     { "autoframingAvailable",          TYPE_BYTE   },
     [ ANDROID_CONTROL_AUTOFRAMING_STATE - ANDROID_CONTROL_START ] =
     { "autoframingState",              TYPE_BYTE   },
+    [ ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE - ANDROID_CONTROL_START ] =
+    { "lowLightBoostInfoLuminanceRange",
+                                        TYPE_FLOAT  },
+    [ ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE - ANDROID_CONTROL_START ] =
+    { "lowLightBoostState",            TYPE_BYTE   },
 };
 
 static tag_info_t android_demosaic[ANDROID_DEMOSAIC_END -
@@ -311,6 +316,16 @@
     { "maxEnergy",                     TYPE_BYTE   },
     [ ANDROID_FLASH_STATE - ANDROID_FLASH_START ] =
     { "state",                         TYPE_BYTE   },
+    [ ANDROID_FLASH_STRENGTH_LEVEL - ANDROID_FLASH_START ] =
+    { "strengthLevel",                 TYPE_INT32  },
+    [ ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL - ANDROID_FLASH_START ] =
+    { "singleStrengthMaxLevel",        TYPE_INT32  },
+    [ ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL - ANDROID_FLASH_START ] =
+    { "singleStrengthDefaultLevel",    TYPE_INT32  },
+    [ ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL - ANDROID_FLASH_START ] =
+    { "torchStrengthMaxLevel",         TYPE_INT32  },
+    [ ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL - ANDROID_FLASH_START ] =
+    { "torchStrengthDefaultLevel",     TYPE_INT32  },
 };
 
 static tag_info_t android_flash_info[ANDROID_FLASH_INFO_END -
@@ -736,6 +751,10 @@
     { "oisXShifts",                    TYPE_FLOAT  },
     [ ANDROID_STATISTICS_OIS_Y_SHIFTS - ANDROID_STATISTICS_START ] =
     { "oisYShifts",                    TYPE_FLOAT  },
+    [ ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS - ANDROID_STATISTICS_START ] =
+    { "lensIntrinsicTimestamps",       TYPE_INT64  },
+    [ ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES - ANDROID_STATISTICS_START ] =
+    { "lensIntrinsicSamples",          TYPE_FLOAT  },
 };
 
 static tag_info_t android_statistics_info[ANDROID_STATISTICS_INFO_END -
@@ -799,6 +818,9 @@
                                         TYPE_BYTE   },
     [ ANDROID_INFO_DEVICE_STATE_ORIENTATIONS - ANDROID_INFO_START ] =
     { "deviceStateOrientations",       TYPE_INT64  },
+    [ ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION - ANDROID_INFO_START ] =
+    { "sessionConfigurationQueryVersion",
+                                        TYPE_INT32  },
 };
 
 static tag_info_t android_black_level[ANDROID_BLACK_LEVEL_END -
@@ -877,6 +899,9 @@
     { "sensorSyncType",                TYPE_BYTE   },
     [ ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID - ANDROID_LOGICAL_MULTI_CAMERA_START ] =
     { "activePhysicalId",              TYPE_BYTE   },
+    [ ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION - ANDROID_LOGICAL_MULTI_CAMERA_START ] =
+    { "activePhysicalSensorCropRegion",
+                                        TYPE_INT32  },
 };
 
 static tag_info_t android_distortion_correction[ANDROID_DISTORTION_CORRECTION_END -
@@ -1139,6 +1164,10 @@
                     msg = "ON_EXTERNAL_FLASH";
                     ret = 0;
                     break;
+                case ANDROID_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY:
+                    msg = "ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY";
+                    ret = 0;
+                    break;
                 default:
                     msg = "error: enum value out of range";
             }
@@ -1870,6 +1899,24 @@
             }
             break;
         }
+        case ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE: {
+            break;
+        }
+        case ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE: {
+            switch (value) {
+                case ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE:
+                    msg = "INACTIVE";
+                    ret = 0;
+                    break;
+                case ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE:
+                    msg = "ACTIVE";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
 
         case ANDROID_DEMOSAIC_MODE: {
             switch (value) {
@@ -1975,6 +2022,21 @@
             }
             break;
         }
+        case ANDROID_FLASH_STRENGTH_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL: {
+            break;
+        }
 
         case ANDROID_FLASH_INFO_AVAILABLE: {
             switch (value) {
@@ -3368,6 +3430,12 @@
         case ANDROID_STATISTICS_OIS_Y_SHIFTS: {
             break;
         }
+        case ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS: {
+            break;
+        }
+        case ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES: {
+            break;
+        }
 
         case ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES: {
             break;
@@ -3521,6 +3589,10 @@
                     msg = "HIDL_DEVICE_3_5";
                     ret = 0;
                     break;
+                case ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE:
+                    msg = "SESSION_CONFIGURABLE";
+                    ret = 0;
+                    break;
                 default:
                     msg = "error: enum value out of range";
             }
@@ -3529,6 +3601,21 @@
         case ANDROID_INFO_DEVICE_STATE_ORIENTATIONS: {
             break;
         }
+        case ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION: {
+            switch (value) {
+                case ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION_UPSIDE_DOWN_CAKE:
+                    msg = "UPSIDE_DOWN_CAKE";
+                    ret = 0;
+                    break;
+                case ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION_VANILLA_ICE_CREAM:
+                    msg = "VANILLA_ICE_CREAM";
+                    ret = 0;
+                    break;
+                default:
+                    msg = "error: enum value out of range";
+            }
+            break;
+        }
 
         case ANDROID_BLACK_LEVEL_LOCK: {
             switch (value) {
@@ -3711,6 +3798,9 @@
         case ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID: {
             break;
         }
+        case ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION: {
+            break;
+        }
 
         case ANDROID_DISTORTION_CORRECTION_MODE: {
             switch (value) {
@@ -4121,6 +4211,12 @@
                     ret = 0;
                     break;
                 }
+                enumName = "ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY;
+                    ret = 0;
+                    break;
+                }
             break;
         }
         case ANDROID_CONTROL_AE_REGIONS: {
@@ -4981,6 +5077,24 @@
                 }
             break;
         }
+        case ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE: {
+            break;
+        }
+        case ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE: {
+                enumName = "INACTIVE";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE;
+                    ret = 0;
+                    break;
+                }
+                enumName = "ACTIVE";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE;
+                    ret = 0;
+                    break;
+                }
+            break;
+        }
 
         case ANDROID_DEMOSAIC_MODE: {
                 enumName = "FAST";
@@ -5098,6 +5212,21 @@
                 }
             break;
         }
+        case ANDROID_FLASH_STRENGTH_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_TORCH_STRENGTH_MAX_LEVEL: {
+            break;
+        }
+        case ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL: {
+            break;
+        }
 
         case ANDROID_FLASH_INFO_AVAILABLE: {
                 enumName = "FALSE";
@@ -6683,6 +6812,12 @@
         case ANDROID_STATISTICS_OIS_Y_SHIFTS: {
             break;
         }
+        case ANDROID_STATISTICS_LENS_INTRINSIC_TIMESTAMPS: {
+            break;
+        }
+        case ANDROID_STATISTICS_LENS_INTRINSIC_SAMPLES: {
+            break;
+        }
 
         case ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES: {
             break;
@@ -6847,11 +6982,32 @@
                     ret = 0;
                     break;
                 }
+                enumName = "SESSION_CONFIGURABLE";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION_SESSION_CONFIGURABLE;
+                    ret = 0;
+                    break;
+                }
             break;
         }
         case ANDROID_INFO_DEVICE_STATE_ORIENTATIONS: {
             break;
         }
+        case ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION: {
+                enumName = "UPSIDE_DOWN_CAKE";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION_UPSIDE_DOWN_CAKE;
+                    ret = 0;
+                    break;
+                }
+                enumName = "VANILLA_ICE_CREAM";
+                if (strncmp(name, enumName, size) == 0) {
+                    *value = ANDROID_INFO_SESSION_CONFIGURATION_QUERY_VERSION_VANILLA_ICE_CREAM;
+                    ret = 0;
+                    break;
+                }
+            break;
+        }
 
         case ANDROID_BLACK_LEVEL_LOCK: {
                 enumName = "OFF";
@@ -7034,6 +7190,9 @@
         case ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_ID: {
             break;
         }
+        case ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION: {
+            break;
+        }
 
         case ANDROID_DISTORTION_CORRECTION_MODE: {
                 enumName = "OFF";
@@ -7342,4 +7501,4 @@
 }
 
 
-#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 29
+#define CAMERA_METADATA_ENUM_STRING_MAX_SIZE 39