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, ¶m) != 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, ¶m) != 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, ¶m) < 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 >= 1
+and <= 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 > 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 >= 1
+and <= 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 > 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 > 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 > 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 >= 1
+and <= 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 > 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 >= 1
+and <= 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 > 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 &gt;= 1
+ and &lt;= android.flash.info.torchStrengthMaxLevel.
+ If the application doesn't set the key and
+ android.flash.info.torchStrengthMaxLevel &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 &gt;= 1
+ and &lt;= android.flash.info.singleStrengthMaxLevel.
+ If the application does not set this key and
+ android.flash.info.singleStrengthMaxLevel &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 &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 &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