[automerger skipped] Merge "Setting apex version to the placeholder value" am: 88e1320d94 am: df464b3305 -s ours am: cb6d3eee68 -s ours am: aa08e7c670 -s ours am: cc0a95b654 -s ours
am skip reason: Merged-In I30b5b486bca521ebdeed17dba7afc61f2c529ab8 with SHA-1 8413ae850a is already in history
Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/2148024
Change-Id: I45d4ed63dd3772f52ce70076c1f3c8fa5228b4ff
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 716b550..1f7083b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,3 +11,4 @@
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
media/libmediatranscoding/
services/mediatranscoding/
+ media/libaudioclient/tests/
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index b37803a..151b653 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -598,7 +598,6 @@
status_t VendorTagDescriptor::setAsGlobalVendorTagDescriptor(const sp<VendorTagDescriptor>& desc) {
status_t res = OK;
Mutex::Autolock al(sLock);
- sGlobalVendorTagDescriptor = desc;
vendor_tag_ops_t* opsPtr = NULL;
if (desc != NULL) {
@@ -613,6 +612,9 @@
ALOGE("%s: Could not set vendor tag descriptor, received error %s (%d)."
, __FUNCTION__, strerror(-res), res);
}
+
+ sGlobalVendorTagDescriptor = desc;
+
return res;
}
@@ -631,7 +633,6 @@
const sp<VendorTagDescriptorCache>& cache) {
status_t res = OK;
Mutex::Autolock al(sLock);
- sGlobalVendorTagDescriptorCache = cache;
struct vendor_tag_cache_ops* opsPtr = NULL;
if (cache != NULL) {
@@ -646,6 +647,9 @@
ALOGE("%s: Could not set vendor tag cache, received error %s (%d)."
, __FUNCTION__, strerror(-res), res);
}
+
+ sGlobalVendorTagDescriptorCache = cache;
+
return res;
}
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index 8f51458..6a311f2 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -5,3 +5,4 @@
ioprio rt 4
task_profiles CameraServiceCapacity MaxPerformance
rlimit rtprio 10 10
+ onrestart class_restart cameraWatchdog
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 2e0b678..b277bed 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -1026,7 +1026,8 @@
" Add additional information, such as a timestamp overlay, that is helpful\n"
" in videos captured to illustrate bugs.\n"
"--time-limit TIME\n"
- " Set the maximum recording time, in seconds. Default / maximum is %d.\n"
+ " Set the maximum recording time, in seconds. Default is %d. Set to 0\n"
+ " to remove the time limit.\n"
"--display-id ID\n"
" specify the physical display ID to record. Default is the primary display.\n"
" see \"dumpsys SurfaceFlinger --display-id\" for valid display IDs.\n"
@@ -1113,14 +1114,27 @@
}
break;
case 't':
- gTimeLimitSec = atoi(optarg);
- if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) {
- fprintf(stderr,
- "Time limit %ds outside acceptable range [1,%d]\n",
- gTimeLimitSec, kMaxTimeLimitSec);
+ {
+ char *next;
+ const int64_t timeLimitSec = strtol(optarg, &next, 10);
+ if (next == optarg || (*next != '\0' && *next != ' ')) {
+ fprintf(stderr, "Error parsing time limit argument\n");
return 2;
}
+ if (timeLimitSec > std::numeric_limits<uint32_t>::max() || timeLimitSec < 0) {
+ fprintf(stderr,
+ "Time limit %" PRIi64 "s outside acceptable range [0,%u] seconds\n",
+ timeLimitSec, std::numeric_limits<uint32_t>::max());
+ return 2;
+ }
+ gTimeLimitSec = (timeLimitSec == 0) ?
+ std::numeric_limits<uint32_t>::max() : timeLimitSec;
+ if (gVerbose) {
+ printf("Time limit set to %u seconds\n", gTimeLimitSec);
+ fflush(stdout);
+ }
break;
+ }
case 'u':
gWantInfoScreen = true;
gWantFrameTime = true;
diff --git a/drm/libmediadrm/DrmHalHidl.cpp b/drm/libmediadrm/DrmHalHidl.cpp
index c38dbef..055f20a 100644
--- a/drm/libmediadrm/DrmHalHidl.cpp
+++ b/drm/libmediadrm/DrmHalHidl.cpp
@@ -309,7 +309,7 @@
closeOpenSessions();
Mutex::Autolock autoLock(mLock);
- reportFrameworkMetrics(reportPluginMetrics());
+ if (mInitCheck == OK) reportFrameworkMetrics(reportPluginMetrics());
setListener(NULL);
mInitCheck = NO_INIT;
diff --git a/drm/libmediadrm/DrmMetricsConsumer.cpp b/drm/libmediadrm/DrmMetricsConsumer.cpp
index c06f09b..fd095b7 100644
--- a/drm/libmediadrm/DrmMetricsConsumer.cpp
+++ b/drm/libmediadrm/DrmMetricsConsumer.cpp
@@ -42,7 +42,7 @@
}
return type_names[attribute];
}
-
+
static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
"KEY_EXPIRED", "VENDOR_DEFINED",
"SESSION_RECLAIMED"};
diff --git a/drm/mediadrm/plugins/clearkey/aidl/Android.bp b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
index 2d1f741..397e4c6 100644
--- a/drm/mediadrm/plugins/clearkey/aidl/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/aidl/Android.bp
@@ -69,3 +69,78 @@
"android.hardware.drm-service.clearkey",
],
}
+
+cc_defaults {
+ name: "fuzz_aidl_clearkey_service_defaults",
+
+ srcs: [
+ "CreatePluginFactories.cpp",
+ "CryptoPlugin.cpp",
+ "DrmFactory.cpp",
+ "DrmPlugin.cpp",
+ ],
+
+ relative_install_path: "hw",
+
+ cflags: ["-Wall", "-Werror", "-Wthread-safety"],
+
+ include_dirs: ["frameworks/av/include"],
+
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libcrypto",
+ "liblog",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ "android.hardware.drm-V1-ndk",
+ ],
+
+ static_libs: [
+ "android.hardware.common-V2-ndk",
+ "libclearkeybase_fuzz",
+ ],
+
+ local_include_dirs: ["include"],
+
+ sanitize: {
+ integer_overflow: true,
+ },
+}
+
+cc_fuzz {
+ name: "android.hardware.drm-service.clearkey.aidl_fuzzer",
+ defaults: [
+ "fuzz_aidl_clearkey_service_defaults",
+ ],
+ static_libs: [
+ "libbase",
+ "libbinder_random_parcel",
+ "libcutils",
+ "liblog",
+ "libutils",
+ ],
+ target: {
+ android: {
+ shared_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libbinder_ndk",
+ "libbinder",
+ ],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+ srcs: ["fuzzer.cpp"],
+ fuzz_config: {
+ cc: [
+ "hamzeh@google.com",
+ ],
+ },
+}
\ No newline at end of file
diff --git a/drm/mediadrm/plugins/clearkey/aidl/fuzzer.cpp b/drm/mediadrm/plugins/clearkey/aidl/fuzzer.cpp
new file mode 100644
index 0000000..9ef331f
--- /dev/null
+++ b/drm/mediadrm/plugins/clearkey/aidl/fuzzer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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 <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "CreatePluginFactories.h"
+
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::drm::clearkey::createDrmFactory;
+using ::aidl::android::hardware::drm::clearkey::DrmFactory;
+
+using android::fuzzService;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::shared_ptr<DrmFactory> drmFactory = createDrmFactory();
+ fuzzService(drmFactory->asBinder().get(), FuzzedDataProvider(data, size));
+
+ return 0;
+}
diff --git a/drm/mediadrm/plugins/clearkey/common/Android.bp b/drm/mediadrm/plugins/clearkey/common/Android.bp
index a6a5b28..6913df4 100644
--- a/drm/mediadrm/plugins/clearkey/common/Android.bp
+++ b/drm/mediadrm/plugins/clearkey/common/Android.bp
@@ -97,3 +97,54 @@
integer_overflow: true,
},
}
+
+cc_library_static {
+ name: "libclearkeydevicefiles-protos.common_fuzz",
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["protos/DeviceFiles.proto"],
+}
+
+cc_library_static {
+ name: "libclearkeybase_fuzz",
+
+ srcs: [
+ "AesCtrDecryptor.cpp",
+ "Base64.cpp",
+ "Buffer.cpp",
+ "ClearKeyUUID.cpp",
+ "DeviceFiles.cpp",
+ "InitDataParser.cpp",
+ "JsonWebKey.cpp",
+ "MemoryFileSystem.cpp",
+ "Session.cpp",
+ "SessionLibrary.cpp",
+ "Utils.cpp",
+ ],
+
+ cflags: ["-Wall", "-Werror"],
+
+ include_dirs: ["frameworks/av/include"],
+
+ shared_libs: [
+ "libutils",
+ "libcrypto",
+ ],
+
+ whole_static_libs: [
+ "libjsmn",
+ "libclearkeydevicefiles-protos.common_fuzz",
+ ],
+
+ export_include_dirs: [
+ "include",
+ "include/clearkeydrm",
+ ],
+
+ sanitize: {
+ integer_overflow: true,
+ },
+}
diff --git a/include/media/MmapStreamCallback.h b/include/media/MmapStreamCallback.h
index 31b8eb5..76ee6d7 100644
--- a/include/media/MmapStreamCallback.h
+++ b/include/media/MmapStreamCallback.h
@@ -37,12 +37,9 @@
/**
* The volume to be applied to the use case specified when opening the stream has changed
- * \param[in] channels a channel mask containing all channels the volume should be applied to.
- * \param[in] values the volume values to be applied to each channel. The size of the vector
- * should correspond to the channel count retrieved with
- * audio_channel_count_from_in_mask() or audio_channel_count_from_out_mask()
+ * \param[in] volume the new target volume
*/
- virtual void onVolumeChanged(audio_channel_mask_t channels, Vector<float> values) = 0;
+ virtual void onVolumeChanged(float volume) = 0;
/**
* The device the stream is routed to/from has changed
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index d4025e5..97e0b1d 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -85,8 +85,6 @@
uint32_t mSize; // Number of bytes of frame data
uint32_t mIccSize; // Number of bytes of ICC data
uint32_t mBitDepth; // number of bits per R / G / B channel
-
- // Adding new items must be 64-bit aligned.
};
}; // namespace android
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index d989fa3..3f7cb3c 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -71,4 +71,6 @@
}
]
+ // TODO (b/229286407) Add EncodeDecodeTest and DecodeEditEncodeTest to
+ // platinum-postsubmit once issues in cuttlefish are fixed
}
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index c4a6601..0bd0d88 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -7,11 +7,9 @@
ioprio rt 4
task_profiles ProcessCapacityHigh HighPerformance
onrestart restart vendor.audio-hal
+ onrestart restart vendor.audio-hal-aidl
onrestart restart vendor.audio-hal-4-0-msd
onrestart restart audio_proxy_service
- # Keep the original service names for backward compatibility
- onrestart restart vendor.audio-hal-2-0
- onrestart restart audio-hal-2-0
on property:vts.native_server.on=1
stop audioserver
@@ -20,42 +18,34 @@
on property:init.svc.audioserver=stopped
stop vendor.audio-hal
+ stop vendor.audio-hal-aidl
stop vendor.audio-hal-4-0-msd
stop audio_proxy_service
- # Keep the original service names for backward compatibility
- stop vendor.audio-hal-2-0
- stop audio-hal-2-0
# See b/155364397. Need to have HAL service running for VTS.
# Can't use 'restart' because then HAL service would restart
# audioserver bringing it back into running state.
start vendor.audio-hal
+ start vendor.audio-hal-aidl
start vendor.audio-hal-4-0-msd
start audio_proxy_service
- # Keep the original service names for backward compatibility
- start vendor.audio-hal-2-0
- start audio-hal-2-0
on property:init.svc.audioserver=running
start vendor.audio-hal
+ start vendor.audio-hal-aidl
start vendor.audio-hal-4-0-msd
start audio_proxy_service
- # Keep the original service names for backward compatibility
- start vendor.audio-hal-2-0
- start audio-hal-2-0
on property:sys.audio.restart.hal=1
# See b/159966243. Avoid restart loop between audioserver and HAL.
# Keep the original service names for backward compatibility
stop vendor.audio-hal
+ stop vendor.audio-hal-aidl
stop vendor.audio-hal-4-0-msd
stop audio_proxy_service
- stop vendor.audio-hal-2-0
- stop audio-hal-2-0
start vendor.audio-hal
+ start vendor.audio-hal-aidl
start vendor.audio-hal-4-0-msd
start audio_proxy_service
- start vendor.audio-hal-2-0
- start audio-hal-2-0
# reset the property
setprop sys.audio.restart.hal 0
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index 8087396..18cd1bf 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -69,8 +69,8 @@
DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
.withDefault(new C2StreamPictureSizeInfo::output(0u, 320, 240))
.withFields({
- C2F(mSize, width).inRange(2, 2048, 2),
- C2F(mSize, height).inRange(2, 2048, 2),
+ C2F(mSize, width).inRange(2, 2048),
+ C2F(mSize, height).inRange(2, 2048),
})
.withSetter(SizeSetter)
.build());
@@ -734,7 +734,12 @@
}
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &block);
+ // We always create a graphic block that is width aligned to 16 and height
+ // aligned to 2. We set the correct "crop" value of the image in the call to
+ // createGraphicBuffer() by setting the correct image dimensions.
+ c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16),
+ align(mHeight, 2), format, usage,
+ &block);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
work->result = err;
diff --git a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
index 5c13b0e..da62111 100644
--- a/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
+++ b/media/codec2/hidl/plugin/samples/SampleFilterPlugin.cpp
@@ -807,8 +807,6 @@
// affectedParams
{
C2StreamHdrStaticInfo::output::PARAM_TYPE,
- C2StreamHdr10PlusInfo::output::PARAM_TYPE, // will be deprecated
- C2StreamHdrDynamicMetadataInfo::output::PARAM_TYPE,
C2StreamColorAspectsInfo::output::PARAM_TYPE,
},
};
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 5208be6..cfadc95 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -389,7 +389,7 @@
// read back always as int
float value;
if (v.get(&value)) {
- return (int32_t)value;
+ return (int32_t) (value + 0.5);
}
return C2Value();
}));
@@ -955,7 +955,7 @@
.limitTo(D::ENCODER & (D::CONFIG | D::PARAM)));
add(ConfigMapper(KEY_FLAC_COMPRESSION_LEVEL, C2_PARAMKEY_COMPLEXITY, "value")
.limitTo(D::AUDIO & D::ENCODER));
- add(ConfigMapper("complexity", C2_PARAMKEY_COMPLEXITY, "value")
+ add(ConfigMapper(KEY_COMPLEXITY, C2_PARAMKEY_COMPLEXITY, "value")
.limitTo(D::ENCODER & (D::CONFIG | D::PARAM)));
add(ConfigMapper(KEY_GRID_COLUMNS, C2_PARAMKEY_TILE_LAYOUT, "columns")
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 0c4a8f7..400a81b 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -103,7 +103,23 @@
*
* Available since API level 31.
*/
- AAUDIO_FORMAT_PCM_I32
+ AAUDIO_FORMAT_PCM_I32,
+
+ /**
+ * This format is used for compressed audio wrapped in IEC61937 for HDMI
+ * or S/PDIF passthrough.
+ *
+ * Unlike PCM playback, the Android framework is not able to do format
+ * conversion for IEC61937. In that case, when IEC61937 is requested, sampling
+ * rate and channel count or channel mask must be specified. Otherwise, it may
+ * fail when opening the stream. Apps are able to get the correct configuration
+ * for the playback by calling
+ * <a href="/reference/android/media/AudioManager#getDevices(int)">
+ * AudioManager#getDevices(int)</a>.
+ *
+ * Available since API level 34.
+ */
+ AAUDIO_FORMAT_IEC61937
};
typedef int32_t aaudio_format_t;
@@ -1441,7 +1457,10 @@
/**
* Asynchronous request for the stream to flush.
* Flushing will discard any pending data.
- * This call only works if the stream is pausing or paused. TODO review
+ * This call only works if the stream is OPEN, PAUSED, STOPPED, or FLUSHED.
+ * Calling this function when in other states,
+ * or calling from an AAudio callback function,
+ * will have no effect and an error will be returned.
* Frame counters are not reset by a flush. They may be advanced.
* After this call the state will be in {@link #AAUDIO_STREAM_STATE_FLUSHING} or
* {@link #AAUDIO_STREAM_STATE_FLUSHED}.
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 9f0564f..34ecd25 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -605,13 +605,6 @@
return AAUDIO_ERROR_INVALID_STATE;
}
-aaudio_result_t AudioStreamInternal::updateStateMachine() {
- if (isDataCallbackActive()) {
- return AAUDIO_OK; // state is getting updated by the callback thread read/write call
- }
- return processCommands();
-}
-
void AudioStreamInternal::logTimestamp(AAudioServiceMessage &command) {
static int64_t oldPosition = 0;
static int64_t oldTime = 0;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 2367572..4ea61d2 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -48,7 +48,7 @@
int64_t *framePosition,
int64_t *timeNanoseconds) override;
- virtual aaudio_result_t updateStateMachine() override;
+ virtual aaudio_result_t processCommands() override;
aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -110,8 +110,6 @@
aaudio_result_t drainTimestampsFromService();
- aaudio_result_t processCommands();
-
aaudio_result_t stopCallback_l();
virtual void prepareBuffersForStart() {}
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 31fd011..e6b00d7 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -58,6 +58,7 @@
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_IEC61937:
break; // valid
default:
ALOGD("audioFormat not valid, audio_format_t = 0x%08x", format);
diff --git a/media/libaaudio/src/core/AudioGlobal.cpp b/media/libaaudio/src/core/AudioGlobal.cpp
index 0e5b8be..30f9677 100644
--- a/media/libaaudio/src/core/AudioGlobal.cpp
+++ b/media/libaaudio/src/core/AudioGlobal.cpp
@@ -82,6 +82,7 @@
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_FLOAT);
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I24_PACKED);
AAUDIO_CASE_ENUM(AAUDIO_FORMAT_PCM_I32);
+ AAUDIO_CASE_ENUM(AAUDIO_FORMAT_IEC61937);
}
return "Unrecognized";
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 06f05b0..8a5186a 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -21,7 +21,9 @@
#include <atomic>
#include <stdint.h>
+#include <linux/futex.h>
#include <media/MediaMetricsItem.h>
+#include <sys/syscall.h>
#include <aaudio/AAudio.h>
@@ -362,34 +364,37 @@
}
void AudioStream::setState(aaudio_stream_state_t state) {
- ALOGD("%s(s#%d) from %d to %d", __func__, getId(), mState, state);
- if (state == mState) {
+ aaudio_stream_state_t oldState = mState.load();
+ ALOGD("%s(s#%d) from %d to %d", __func__, getId(), oldState, state);
+ if (state == oldState) {
return; // no change
}
// Track transition to DISCONNECTED state.
if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
android::mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
- .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
+ .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(oldState))
.record();
}
// CLOSED is a final state
- if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ if (oldState == AAUDIO_STREAM_STATE_CLOSED) {
ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
// Once CLOSING, we can only move to CLOSED state.
- } else if (mState == AAUDIO_STREAM_STATE_CLOSING
+ } else if (oldState == AAUDIO_STREAM_STATE_CLOSING
&& state != AAUDIO_STREAM_STATE_CLOSED) {
ALOGW("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
// Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
- } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
+ } else if (oldState == AAUDIO_STREAM_STATE_DISCONNECTED
&& !(state == AAUDIO_STREAM_STATE_CLOSING
|| state == AAUDIO_STREAM_STATE_CLOSED)) {
ALOGW("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);
} else {
- mState = state;
+ mState.store(state);
+ // Wake up a wakeForStateChange thread if it exists.
+ syscall(SYS_futex, &mState, FUTEX_WAKE_PRIVATE, INT_MAX, NULL, NULL, 0);
}
}
@@ -408,9 +413,15 @@
if (durationNanos > timeoutNanoseconds) {
durationNanos = timeoutNanoseconds;
}
- AudioClock::sleepForNanos(durationNanos);
- timeoutNanoseconds -= durationNanos;
+ struct timespec time;
+ time.tv_sec = durationNanos / AAUDIO_NANOS_PER_SECOND;
+ // Add the fractional nanoseconds.
+ time.tv_nsec = durationNanos - (time.tv_sec * AAUDIO_NANOS_PER_SECOND);
+ // Sleep for durationNanos. If mState changes from the callback
+ // thread, this thread will wake up earlier.
+ syscall(SYS_futex, &mState, FUTEX_WAIT_PRIVATE, currentState, &time, NULL, 0);
+ timeoutNanoseconds -= durationNanos;
aaudio_result_t result = updateStateMachine();
if (result != AAUDIO_OK) {
return result;
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 5fb4528..8dd5538 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -100,10 +100,17 @@
int64_t *timeNanoseconds) = 0;
/**
- * Update state machine.()
- * @return
+ * Update state machine.
+ * @return result of the operation.
*/
- virtual aaudio_result_t updateStateMachine() = 0;
+ aaudio_result_t updateStateMachine() {
+ if (isDataCallbackActive()) {
+ return AAUDIO_OK; // state is getting updated by the callback thread read/write call
+ }
+ return processCommands();
+ };
+
+ virtual aaudio_result_t processCommands() = 0;
// =========== End ABSTRACT methods ===========================
@@ -184,7 +191,7 @@
// ============== Queries ===========================
aaudio_stream_state_t getState() const {
- return mState;
+ return mState.load();
}
virtual int32_t getBufferSize() const {
@@ -674,6 +681,8 @@
const android::sp<MyPlayerBase> mPlayerBase;
+ std::atomic<aaudio_stream_state_t> mState{AAUDIO_STREAM_STATE_UNINITIALIZED};
+
// These do not change after open().
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
aaudio_channel_mask_t mChannelMask = AAUDIO_UNSPECIFIED;
@@ -682,7 +691,6 @@
aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
bool mSharingModeMatchRequired = false; // must match sharing mode requested
audio_format_t mFormat = AUDIO_FORMAT_DEFAULT;
- aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
int32_t mFramesPerBurst = 0;
int32_t mBufferCapacity = 0;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index a100aa9..04b4325 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -192,6 +192,11 @@
allowMMap = false;
}
+ if (getFormat() == AUDIO_FORMAT_IEC61937) {
+ ALOGD("%s IEC61937 format is selected, do not allow MMAP in this case.", __func__);
+ allowMMap = false;
+ }
+
if (!allowMMap && !allowLegacy) {
ALOGE("%s() no backend available: neither MMAP nor legacy path are allowed", __func__);
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index dd11169..f32ef65 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -127,7 +127,7 @@
mCallbackEnabled.store(false);
}
- if (updateStateMachine() != AAUDIO_OK) {
+ if (processCommands() != AAUDIO_OK) {
forceDisconnect();
mCallbackEnabled.store(false);
}
@@ -192,7 +192,7 @@
mCallbackEnabled.store(false);
}
- if (updateStateMachine() != AAUDIO_OK) {
+ if (processCommands() != AAUDIO_OK) {
forceDisconnect();
mCallbackEnabled.store(false);
}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 1e39e0f..9a136a7 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -364,8 +364,7 @@
return checkForDisconnectRequest(false);
}
-aaudio_result_t AudioStreamRecord::updateStateMachine()
-{
+aaudio_result_t AudioStreamRecord::processCommands() {
aaudio_result_t result = AAUDIO_OK;
aaudio_wrapping_frames_t position;
status_t err;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 5ce73f9..252ff3c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -58,7 +58,7 @@
int64_t getFramesWritten() override;
- aaudio_result_t updateStateMachine() override;
+ aaudio_result_t processCommands() override;
aaudio_direction_t getDirection() const override {
return AAUDIO_DIRECTION_INPUT;
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 6f1dc92..09caa5c 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -378,8 +378,7 @@
return checkForDisconnectRequest(false);;
}
-aaudio_result_t AudioStreamTrack::updateStateMachine()
-{
+aaudio_result_t AudioStreamTrack::processCommands() {
status_t err;
aaudio_wrapping_frames_t position;
switch (getState()) {
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 0f4d72b..1f877b5 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -79,7 +79,7 @@
return AAUDIO_DIRECTION_OUTPUT;
}
- aaudio_result_t updateStateMachine() override;
+ aaudio_result_t processCommands() override;
int64_t incrementClientFrameCounter(int32_t frames) override {
return incrementFramesWritten(frames);
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 872faca..a197ced 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -140,6 +140,9 @@
case AAUDIO_FORMAT_PCM_I32:
androidFormat = AUDIO_FORMAT_PCM_32_BIT;
break;
+ case AAUDIO_FORMAT_IEC61937:
+ androidFormat = AUDIO_FORMAT_IEC61937;
+ break;
default:
androidFormat = AUDIO_FORMAT_INVALID;
ALOGE("%s() 0x%08X unrecognized", __func__, aaudioFormat);
@@ -166,6 +169,9 @@
case AUDIO_FORMAT_PCM_32_BIT:
aaudioFormat = AAUDIO_FORMAT_PCM_I32;
break;
+ case AUDIO_FORMAT_IEC61937:
+ aaudioFormat = AAUDIO_FORMAT_IEC61937;
+ break;
default:
aaudioFormat = AAUDIO_FORMAT_INVALID;
ALOGE("%s() 0x%08X unrecognized", __func__, androidFormat);
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 4133bd0..1521882 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -1932,7 +1932,7 @@
case media::AudioPortType::SESSION:
legacy.session = VALUE_OR_RETURN(
aidl2legacy_int32_t_audio_port_config_session_ext(
- VALUE_OR_RETURN(UNION_GET(aidl, session))));
+ VALUE_OR_RETURN(UNION_GET(aidlSys, session))));
return legacy;
}
@@ -1966,9 +1966,9 @@
return OK;
}
case AUDIO_PORT_TYPE_SESSION:
- UNION_SET(*aidl, session, VALUE_OR_RETURN_STATUS(
+ UNION_SET(*aidl, unspecified, false);
+ UNION_SET(*aidlSys, session, VALUE_OR_RETURN_STATUS(
legacy2aidl_audio_port_config_session_ext_int32_t(legacy.session)));
- UNION_SET(*aidlSys, unspecified, false);
return OK;
}
LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
@@ -2816,7 +2816,7 @@
case media::AudioPortType::SESSION:
legacy.session = VALUE_OR_RETURN(
aidl2legacy_int32_t_audio_port_session_ext(
- VALUE_OR_RETURN(UNION_GET(aidl, session))));
+ VALUE_OR_RETURN(UNION_GET(aidlSys, session))));
return legacy;
}
@@ -2852,9 +2852,9 @@
return OK;
}
case AUDIO_PORT_TYPE_SESSION:
- UNION_SET(*aidl, session, VALUE_OR_RETURN_STATUS(
+ UNION_SET(*aidl, unspecified, false);
+ UNION_SET(*aidlSys, session, VALUE_OR_RETURN_STATUS(
legacy2aidl_audio_port_session_ext_int32_t(legacy.session)));
- UNION_SET(*aidlSys, unspecified, false);
return OK;
}
LOG_ALWAYS_FATAL("Shouldn't get here"); // with -Werror,-Wswitch may compile-time fail
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index dccef0e..79dae27 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -302,12 +302,26 @@
double_loadable: true,
vendor_available: true,
srcs: [
+ "aidl/android/media/EffectConfig.aidl",
"aidl/android/media/IEffect.aidl",
"aidl/android/media/IEffectClient.aidl",
],
imports: [
+ "android.media.audio.common.types-V1",
"shared-file-region-aidl",
],
+ backend: {
+ cpp: {
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.media",
+ ],
+ },
+ java: {
+ sdk_version: "module_current",
+ },
+ },
}
aidl_interface {
diff --git a/media/libaudioclient/AudioEffect.cpp b/media/libaudioclient/AudioEffect.cpp
index d447f0c..2870c4c 100644
--- a/media/libaudioclient/AudioEffect.cpp
+++ b/media/libaudioclient/AudioEffect.cpp
@@ -527,6 +527,36 @@
return status;
}
+status_t AudioEffect::getConfigs(
+ audio_config_base_t *inputCfg, audio_config_base_t *outputCfg)
+{
+ if (mProbe) {
+ return INVALID_OPERATION;
+ }
+ if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
+ return mStatus;
+ }
+ if (inputCfg == NULL || outputCfg == NULL) {
+ return BAD_VALUE;
+ }
+ status_t status;
+ media::EffectConfig cfg;
+ Status bs = mIEffect->getConfig(&cfg, &status);
+ if (!bs.isOk()) {
+ status = statusTFromBinderStatus(bs);
+ ALOGW("%s received status %d from binder transaction", __func__, status);
+ return status;
+ }
+ if (status == NO_ERROR) {
+ *inputCfg = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioConfigBase_audio_config_base_t(
+ cfg.inputCfg, cfg.isOnInputStream));
+ *outputCfg = VALUE_OR_RETURN_STATUS(aidl2legacy_AudioConfigBase_audio_config_base_t(
+ cfg.outputCfg, cfg.isOnInputStream));
+ } else {
+ ALOGW("%s received status %d from the effect", __func__, status);
+ }
+ return status;
+}
// -------------------------------------------------------------------------
@@ -538,7 +568,6 @@
if (cb != nullptr) {
cb->onError(mStatus);
}
- mIEffect.clear();
}
// -------------------------------------------------------------------------
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 15203d6..69d73ad 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -146,39 +146,6 @@
audio_channel_mask_t channelMask,
const AttributionSourceState& client,
size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames,
- audio_session_t sessionId,
- transfer_type transferType,
- audio_input_flags_t flags,
- const audio_attributes_t* pAttributes,
- audio_port_handle_t selectedDeviceId,
- audio_microphone_direction_t selectedMicDirection,
- float microphoneFieldDimension)
- : mActive(false),
- mStatus(NO_INIT),
- mClientAttributionSource(client),
- mSessionId(AUDIO_SESSION_ALLOCATE),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mProxy(NULL)
-{
- uid_t uid = VALUE_OR_FATAL(aidl2legacy_int32_t_uid_t(mClientAttributionSource.uid));
- pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientAttributionSource.pid));
- (void)set(inputSource, sampleRate, format, channelMask, frameCount, callback, user,
- notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
- uid, pid, pAttributes, selectedDeviceId, selectedMicDirection,
- microphoneFieldDimension);
-}
-
-AudioRecord::AudioRecord(
- audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const AttributionSourceState& client,
- size_t frameCount,
const wp<IAudioRecordCallback>& callback,
uint32_t notificationFrames,
audio_session_t sessionId,
@@ -255,37 +222,6 @@
mDeviceCallback.clear();
}
}
-namespace {
-class LegacyCallbackWrapper : public AudioRecord::IAudioRecordCallback {
- const AudioRecord::legacy_callback_t mCallback;
- void* const mData;
-
- public:
- LegacyCallbackWrapper(AudioRecord::legacy_callback_t callback, void* user)
- : mCallback(callback), mData(user) {}
-
- size_t onMoreData(const AudioRecord::Buffer& buffer) override {
- AudioRecord::Buffer copy = buffer;
- mCallback(AudioRecord::EVENT_MORE_DATA, mData, ©);
- return copy.size();
- }
-
- void onOverrun() override { mCallback(AudioRecord::EVENT_OVERRUN, mData, nullptr); }
-
- void onMarker(uint32_t markerPosition) override {
- mCallback(AudioRecord::EVENT_MARKER, mData, &markerPosition);
- }
-
- void onNewPos(uint32_t newPos) override {
- mCallback(AudioRecord::EVENT_NEW_POS, mData, &newPos);
- }
-
- void onNewIAudioRecord() override {
- mCallback(AudioRecord::EVENT_NEW_IAUDIORECORD, mData, nullptr);
- }
-};
-} // namespace
-
status_t AudioRecord::set(
audio_source_t inputSource,
uint32_t sampleRate,
@@ -479,37 +415,6 @@
return status;
}
-status_t AudioRecord::set(
- audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames,
- bool threadCanCallJava,
- audio_session_t sessionId,
- transfer_type transferType,
- audio_input_flags_t flags,
- uid_t uid,
- pid_t pid,
- const audio_attributes_t* pAttributes,
- audio_port_handle_t selectedDeviceId,
- audio_microphone_direction_t selectedMicDirection,
- float microphoneFieldDimension,
- int32_t maxSharedAudioHistoryMs)
-{
- if (callback != nullptr) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- return set(inputSource, sampleRate, format, channelMask, frameCount, mLegacyCallbackWrapper,
- notificationFrames, threadCanCallJava, sessionId, transferType, flags, uid, pid,
- pAttributes, selectedDeviceId, selectedMicDirection, microphoneFieldDimension,
- maxSharedAudioHistoryMs);
-}
// -------------------------------------------------------------------------
status_t AudioRecord::start(AudioSystem::sync_event_t event, audio_session_t triggerSession)
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index bacca3a..965c40f 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -2384,7 +2384,7 @@
status_t AudioSystem::getDirectProfilesForAttributes(const audio_attributes_t* attr,
std::vector<audio_profile>* audioProfiles) {
- if (attr == nullptr) {
+ if (attr == nullptr || audioProfiles == nullptr) {
return BAD_VALUE;
}
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 6ab8339..ffbb32f 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -325,45 +325,6 @@
}
};
}
-
-AudioTrack::AudioTrack(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void* user,
- int32_t notificationFrames,
- audio_session_t sessionId,
- transfer_type transferType,
- const audio_offload_info_t *offloadInfo,
- const AttributionSourceState& attributionSource,
- const audio_attributes_t* pAttributes,
- bool doNotReconnect,
- float maxRequiredSpeed,
- audio_port_handle_t selectedDeviceId)
- : mStatus(NO_INIT),
- mState(STATE_STOPPED),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mAudioTrackCallback(new AudioTrackCallback())
-{
- mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
- if (callback != nullptr) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- mSetParams = std::unique_ptr<SetParams>{new SetParams{
- streamType, sampleRate, format, channelMask, frameCount, flags, mLegacyCallbackWrapper,
- notificationFrames, 0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId,
- transferType, offloadInfo, attributionSource, pAttributes, doNotReconnect,
- maxRequiredSpeed, selectedDeviceId}};
-}
-
AudioTrack::AudioTrack(
audio_stream_type_t streamType,
uint32_t sampleRate,
@@ -397,44 +358,6 @@
doNotReconnect, maxRequiredSpeed, AUDIO_PORT_HANDLE_NONE}};
}
-AudioTrack::AudioTrack(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const sp<IMemory>& sharedBuffer,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void* user,
- int32_t notificationFrames,
- audio_session_t sessionId,
- transfer_type transferType,
- const audio_offload_info_t *offloadInfo,
- const AttributionSourceState& attributionSource,
- const audio_attributes_t* pAttributes,
- bool doNotReconnect,
- float maxRequiredSpeed)
- : mStatus(NO_INIT),
- mState(STATE_STOPPED),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT),
- mPausedPosition(0),
- mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE),
- mAudioTrackCallback(new AudioTrackCallback())
-{
- mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
- if (callback) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- mSetParams = std::unique_ptr<SetParams>{new SetParams{
- streamType, sampleRate, format, channelMask, 0 /*frameCount*/, flags,
- mLegacyCallbackWrapper, notificationFrames, sharedBuffer, false /*threadCanCallJava*/,
- sessionId, transferType, offloadInfo, attributionSource, pAttributes, doNotReconnect,
- maxRequiredSpeed, AUDIO_PORT_HANDLE_NONE}};
-}
-
void AudioTrack::onFirstRef() {
if (mSetParams) {
set(*mSetParams);
@@ -496,38 +419,6 @@
mDeviceCallback.clear();
}
}
-
-status_t AudioTrack::set(
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void * user,
- int32_t notificationFrames,
- const sp<IMemory>& sharedBuffer,
- bool threadCanCallJava,
- audio_session_t sessionId,
- transfer_type transferType,
- const audio_offload_info_t *offloadInfo,
- const AttributionSourceState& attributionSource,
- const audio_attributes_t* pAttributes,
- bool doNotReconnect,
- float maxRequiredSpeed,
- audio_port_handle_t selectedDeviceId)
-{
- if (callback) {
- mLegacyCallbackWrapper = sp<LegacyCallbackWrapper>::make(callback, user);
- } else if (user) {
- LOG_ALWAYS_FATAL("Callback data provided without callback pointer!");
- }
- return set(streamType, sampleRate,format, channelMask, frameCount, flags,
- mLegacyCallbackWrapper, notificationFrames, sharedBuffer, threadCanCallJava,
- sessionId, transferType, offloadInfo, attributionSource, pAttributes,
- doNotReconnect, maxRequiredSpeed, selectedDeviceId);
-}
status_t AudioTrack::set(
audio_stream_type_t streamType,
uint32_t sampleRate,
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 3751f80..51080ef 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -4,12 +4,33 @@
"name": "audio_aidl_conversion_tests"
},
{
+ "name": "audio_aidl_status_tests"
+ },
+ {
"name": "CtsNativeMediaAAudioTestCases",
"options" : [
{
"include-filter": "android.nativemedia.aaudio.AAudioTests#AAudioBasic.*"
}
]
+ },
+ {
+ "name": "audiorecord_tests"
+ },
+ {
+ "name": "audioeffect_tests"
+ },
+ {
+ "name": "audiorouting_tests"
+ },
+ {
+ "name": "audioclient_serialization_tests"
+ },
+ {
+ "name": "trackplayerbase_tests"
+ },
+ {
+ "name": "audiosystem_tests"
}
]
}
diff --git a/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl b/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl
index 2cdf4f6..d9c6df4 100644
--- a/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioPortExtSys.aidl
@@ -31,4 +31,6 @@
AudioPortDeviceExtSys device;
/** System-only parameters when the port is an audio mix. */
AudioPortMixExtSys mix;
+ /** Framework audio session identifier. */
+ int session;
}
diff --git a/media/libaudioclient/aidl/android/media/EffectConfig.aidl b/media/libaudioclient/aidl/android/media/EffectConfig.aidl
new file mode 100644
index 0000000..5f62b73
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/EffectConfig.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package android.media;
+
+import android.media.audio.common.AudioConfigBase;
+
+/**
+ * Describes configuration of an audio effect. Input and output
+ * audio configurations are described separately because the effect
+ * can perform transformations on channel layouts, for example.
+ *
+ * {@hide}
+ */
+parcelable EffectConfig {
+ /** Configuration of the audio input of the effect. */
+ AudioConfigBase inputCfg;
+ /** Configuration of the audio output of the effect. */
+ AudioConfigBase outputCfg;
+ /**
+ * Specifies whether the effect is instantiated on an input stream,
+ * e.g. on the input from a microphone.
+ */
+ boolean isOnInputStream;
+}
diff --git a/media/libaudioclient/aidl/android/media/IEffect.aidl b/media/libaudioclient/aidl/android/media/IEffect.aidl
index 6ec0405..b7f70a6 100644
--- a/media/libaudioclient/aidl/android/media/IEffect.aidl
+++ b/media/libaudioclient/aidl/android/media/IEffect.aidl
@@ -16,6 +16,7 @@
package android.media;
+import android.media.EffectConfig;
import android.media.SharedFileRegion;
/**
@@ -63,6 +64,14 @@
*/
SharedFileRegion getCblk();
+ /**
+ * Provides audio configurations for effect's input and output,
+ * see EffectConfig parcelable for more details.
+ *
+ * @return a status_t code.
+ */
+ int getConfig(out EffectConfig config);
+
// When adding a new method, please review and update
// Effects.cpp AudioFlinger::EffectHandle::onTransact()
// Effects.cpp IEFFECT_BINDER_METHOD_MACRO_LIST
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index 036e72e..5536bcb 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -84,13 +84,15 @@
};
template <typename T, typename X, typename FUNC>
-std::vector<T> getFlags(const xsdc_enum_range<X> &range, const FUNC &func,
- const std::string &findString = {}) {
+std::vector<T> getFlags(const xsdc_enum_range<X>& range, const FUNC& func,
+ const std::string& findString = {},
+ const std::set<X>& excludedValues = {}) {
std::vector<T> vec;
for (const auto &xsdEnumVal : range) {
T enumVal;
std::string enumString = toString(xsdEnumVal);
if (enumString.find(findString) != std::string::npos &&
+ (excludedValues.find(xsdEnumVal) == excludedValues.end()) &&
func(enumString.c_str(), &enumVal)) {
vec.push_back(enumVal);
}
@@ -102,13 +104,29 @@
getFlags<audio_stream_type_t, xsd::AudioStreamType, decltype(audio_stream_type_from_string)>(
xsdc_enum_range<xsd::AudioStreamType>{}, audio_stream_type_from_string);
+/**
+ * AudioFormat - AUDIO_FORMAT_HE_AAC_V1 and AUDIO_FORMAT_HE_AAC_V2
+ * are excluded from kFormats[] in order to avoid the abort triggered
+ * for these two types of AudioFormat in
+ * AidlConversion::legacy2aidl_audio_format_t_AudioFormatDescription()
+ */
static const std::vector<audio_format_t> kFormats =
- getFlags<audio_format_t, xsd::AudioFormat, decltype(audio_format_from_string)>(
- xsdc_enum_range<xsd::AudioFormat>{}, audio_format_from_string);
+ getFlags<audio_format_t, xsd::AudioFormat, decltype(audio_format_from_string)>(
+ xsdc_enum_range<xsd::AudioFormat>{}, audio_format_from_string, {},
+ {xsd::AudioFormat::AUDIO_FORMAT_HE_AAC_V1,
+ xsd::AudioFormat::AUDIO_FORMAT_HE_AAC_V2});
+/**
+ * AudioChannelMask - AUDIO_CHANNEL_IN_6
+ * is excluded from kChannelMasks[] in order to avoid the abort triggered
+ * for this type of AudioChannelMask in
+ * AidlConversion::legacy2aidl_audio_channel_mask_t_AudioChannelLayout()
+ */
static const std::vector<audio_channel_mask_t> kChannelMasks =
- getFlags<audio_channel_mask_t, xsd::AudioChannelMask, decltype(audio_channel_mask_from_string)>(
- xsdc_enum_range<xsd::AudioChannelMask>{}, audio_channel_mask_from_string);
+ getFlags<audio_channel_mask_t, xsd::AudioChannelMask,
+ decltype(audio_channel_mask_from_string)>(
+ xsdc_enum_range<xsd::AudioChannelMask>{}, audio_channel_mask_from_string, {},
+ {xsd::AudioChannelMask::AUDIO_CHANNEL_IN_6});
static const std::vector<audio_usage_t> kUsages =
getFlags<audio_usage_t, xsd::AudioUsage, decltype(audio_usage_from_string)>(
@@ -126,9 +144,17 @@
getFlags<audio_gain_mode_t, xsd::AudioGainMode, decltype(audio_gain_mode_from_string)>(
xsdc_enum_range<xsd::AudioGainMode>{}, audio_gain_mode_from_string);
+/**
+ * AudioDevice - AUDIO_DEVICE_IN_AMBIENT and AUDIO_DEVICE_IN_COMMUNICATION
+ * are excluded from kDevices[] in order to avoid the abort triggered
+ * for these two types of AudioDevice in
+ * AidlConversion::aidl2legacy_AudioDeviceDescription_audio_devices_t()
+ */
static const std::vector<audio_devices_t> kDevices =
- getFlags<audio_devices_t, xsd::AudioDevice, decltype(audio_device_from_string)>(
- xsdc_enum_range<xsd::AudioDevice>{}, audio_device_from_string);
+ getFlags<audio_devices_t, xsd::AudioDevice, decltype(audio_device_from_string)>(
+ xsdc_enum_range<xsd::AudioDevice>{}, audio_device_from_string, {},
+ {xsd::AudioDevice::AUDIO_DEVICE_IN_AMBIENT,
+ xsd::AudioDevice::AUDIO_DEVICE_IN_COMMUNICATION});
static const std::vector<audio_input_flags_t> kInputFlags =
getFlags<audio_input_flags_t, xsd::AudioInOutFlag, decltype(audio_input_flag_from_string)>(
diff --git a/media/libaudioclient/include/media/AudioEffect.h b/media/libaudioclient/include/media/AudioEffect.h
index ca35543..72c050b 100644
--- a/media/libaudioclient/include/media/AudioEffect.h
+++ b/media/libaudioclient/include/media/AudioEffect.h
@@ -578,6 +578,25 @@
uint32_t *replySize,
void *replyData);
+ /* Retrieves the configuration of the effect.
+ *
+ * Parameters:
+ * inputCfg: pointer to audio_config_base_t structure receiving input
+ * configuration of the effect
+ * outputCfg: pointer to audio_config_base_t structure receiving output
+ * configuration of the effect
+ *
+ * Channel masks of the returned configs are "input" or "output" depending
+ * on the direction of the stream that the effect is attached to.
+ *
+ * Returned status (from utils/Errors.h) can be:
+ * - NO_ERROR: successful operation.
+ * - INVALID_OPERATION: the AudioEffect was not successfully initialized.
+ * - BAD_VALUE: null config pointers
+ * - DEAD_OBJECT: the effect engine has been deleted.
+ */
+ virtual status_t getConfigs(audio_config_base_t *inputCfg,
+ audio_config_base_t *outputCfg);
/*
* Utility functions.
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index cb05dd9..5a1ff65 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -46,27 +46,6 @@
{
public:
- /* Events used by AudioRecord callback function (legacy_callback_t).
- * Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*.
- */
- enum event_type {
- EVENT_MORE_DATA = 0, // Request to read available data from buffer.
- // If this event is delivered but the callback handler
- // does not want to read the available data, the handler must
- // explicitly ignore the event by setting frameCount to zero.
- EVENT_OVERRUN = 1, // Buffer overrun occurred.
- EVENT_MARKER = 2, // Record head is at the specified marker position
- // (See setMarkerPosition()).
- EVENT_NEW_POS = 3, // Record head is at a new position
- // (See setPositionUpdatePeriod()).
- EVENT_NEW_IAUDIORECORD = 4, // IAudioRecord was re-created, either due to re-routing and
- // voluntary invalidation by mediaserver, or mediaserver crash.
- };
-
- /* Client should declare a Buffer and pass address to obtainBuffer()
- * and releaseBuffer(). See also legacy_callback_t for EVENT_MORE_DATA.
- */
-
class Buffer
{
friend AudioRecord;
@@ -122,7 +101,6 @@
* - EVENT_NEW_IAUDIORECORD: unused.
*/
- typedef void (*legacy_callback_t)(int event, void* user, void *info);
class IAudioRecordCallback : public virtual RefBase {
friend AudioRecord;
@@ -226,24 +204,6 @@
float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT);
- AudioRecord(audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const android::content::AttributionSourceState& client,
- size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
- const audio_attributes_t* pAttributes = nullptr,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
- audio_microphone_direction_t
- selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
- float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT);
-
/* Terminates the AudioRecord and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioRecord.
*/
@@ -286,27 +246,6 @@
float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT,
int32_t maxSharedAudioHistoryMs = 0);
- status_t set(audio_source_t inputSource,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- legacy_callback_t callback,
- void* user,
- uint32_t notificationFrames = 0,
- bool threadCanCallJava = false,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
- uid_t uid = AUDIO_UID_INVALID,
- pid_t pid = -1,
- const audio_attributes_t* pAttributes = nullptr,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE,
- audio_microphone_direction_t
- selectedMicDirection = MIC_DIRECTION_UNSPECIFIED,
- float selectedMicFieldDimension = MIC_FIELD_DIMENSION_DEFAULT,
- int32_t maxSharedAudioHistoryMs = 0);
-
/* Result of constructing the AudioRecord. This must be checked for successful initialization
* before using any AudioRecord API (except for set()), because using
* an uninitialized AudioRecord produces undefined results.
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 9f540e6..32576c2 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -148,7 +148,6 @@
* - EVENT_NEW_TIMESTAMP: pointer to const AudioTimestamp.
*/
- typedef void (*legacy_callback_t)(int event, void* user, void* info);
class IAudioTrackCallback : public virtual RefBase {
friend AudioTrack;
protected:
@@ -343,26 +342,6 @@
float maxRequiredSpeed = 1.0f,
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
-
- AudioTrack( audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t cbf,
- void* user = nullptr,
- int32_t notificationFrames = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = nullptr,
- const AttributionSourceState& attributionSource =
- AttributionSourceState(),
- const audio_attributes_t* pAttributes = nullptr,
- bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
-
/* Creates an audio track and registers it with AudioFlinger.
* With this constructor, the track is configured for static buffer mode.
* Data to be rendered is passed in a shared memory buffer
@@ -391,25 +370,6 @@
bool doNotReconnect = false,
float maxRequiredSpeed = 1.0f);
-
- AudioTrack( audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- const sp<IMemory>& sharedBuffer,
- audio_output_flags_t flags,
- legacy_callback_t cbf,
- void* user = nullptr,
- int32_t notificationFrames = 0,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = nullptr,
- const AttributionSourceState& attributionSource =
- AttributionSourceState(),
- const audio_attributes_t* pAttributes = nullptr,
- bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f);
-
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioTrack.
*/
@@ -490,28 +450,8 @@
}
void onFirstRef() override;
public:
- status_t set(audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- size_t frameCount,
- audio_output_flags_t flags,
- legacy_callback_t callback,
- void * user = nullptr,
- int32_t notificationFrames = 0,
- const sp<IMemory>& sharedBuffer = 0,
- bool threadCanCallJava = false,
- audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
- transfer_type transferType = TRANSFER_DEFAULT,
- const audio_offload_info_t *offloadInfo = nullptr,
- const AttributionSourceState& attributionSource =
- AttributionSourceState(),
- const audio_attributes_t* pAttributes = nullptr,
- bool doNotReconnect = false,
- float maxRequiredSpeed = 1.0f,
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE);
-
- // FIXME(b/169889714): Vendor code depends on the old method signature at link time
+ typedef void (*legacy_callback_t)(int event, void* user, void* info);
+ // FIXME(b/169889714): Vendor code depends on the old method signature at link time
status_t set(audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
@@ -1471,7 +1411,7 @@
audio_session_t mSessionId;
int mAuxEffectId;
- audio_port_handle_t mPortId; // Id from Audio Policy Manager
+ audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE; // Id from Audio Policy Manager
/**
* mPlayerIId is the player id of the AudioTrack used by AudioManager.
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 891293e..e861932 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -93,3 +93,113 @@
],
data: ["record_test_input_*.txt"],
}
+
+cc_defaults {
+ name: "libaudioclient_gtests_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "capture_state_listener-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ "libbase",
+ "libbinder",
+ "libcgrouprc",
+ "libcutils",
+ "libdl",
+ "liblog",
+ "libmedia",
+ "libmediametrics",
+ "libmediautils",
+ "libmedia_helper",
+ "libnblog",
+ "libprocessgroup",
+ "libshmemcompat",
+ "libstagefright_foundation",
+ "libutils",
+ "libxml2",
+ "mediametricsservice-aidl-cpp",
+ "packagemanager_aidl-cpp",
+ "shared-file-region-aidl-cpp",
+ ],
+ static_libs: [
+ "android.hardware.audio.common@7.0-enums",
+ "android.media.audio.common.types-V1-cpp",
+ "audioclient-types-aidl-cpp",
+ "audioflinger-aidl-cpp",
+ "audiopolicy-aidl-cpp",
+ "audiopolicy-types-aidl-cpp",
+ "av-types-aidl-cpp",
+ "effect-aidl-cpp",
+ "libaudioclient",
+ "libaudioclient_aidl_conversion",
+ "libaudiofoundation",
+ "libaudiomanager",
+ "libaudiopolicy",
+ "libaudioutils",
+ ],
+ data: ["bbb*.raw"],
+ test_config_template: "audio_test_template.xml",
+ test_suites: ["device-tests"],
+}
+
+cc_test {
+ name: "audiorecord_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiorecord_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audiotrack_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiotrack_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audioeffect_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audioeffect_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audiorouting_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiorouting_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "audioclient_serialization_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audioclient_serialization_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
+
+cc_test {
+ name: "trackplayerbase_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: ["trackplayerbase_tests.cpp"],
+}
+
+cc_test {
+ name: "audiosystem_tests",
+ defaults: ["libaudioclient_gtests_defaults"],
+ srcs: [
+ "audiosystem_tests.cpp",
+ "audio_test_utils.cpp",
+ ],
+}
diff --git a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
index 997f62a..9e663bc 100644
--- a/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
+++ b/media/libaudioclient/tests/audio_aidl_legacy_conversion_tests.cpp
@@ -16,22 +16,29 @@
#include <gtest/gtest.h>
-#include <media/AudioCommonTypes.h>
#include <media/AidlConversion.h>
+#include <media/AudioCommonTypes.h>
using namespace android;
using namespace android::aidl_utils;
+using android::media::AudioDirectMode;
using media::audio::common::AudioChannelLayout;
using media::audio::common::AudioDeviceDescription;
using media::audio::common::AudioDeviceType;
+using media::audio::common::AudioEncapsulationMetadataType;
+using media::audio::common::AudioEncapsulationType;
using media::audio::common::AudioFormatDescription;
using media::audio::common::AudioFormatType;
+using media::audio::common::AudioGainMode;
+using media::audio::common::AudioStandard;
+using media::audio::common::ExtraAudioDescriptor;
using media::audio::common::PcmType;
namespace {
-template<typename T> size_t hash(const T& t) {
+template <typename T>
+size_t hash(const T& t) {
return std::hash<T>{}(t);
}
@@ -52,10 +59,8 @@
return AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
// Use channels that exist both for input and output,
// but doesn't form a known layout mask.
- AudioChannelLayout::CHANNEL_FRONT_LEFT |
- AudioChannelLayout::CHANNEL_FRONT_RIGHT |
- AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT |
- AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT);
+ AudioChannelLayout::CHANNEL_FRONT_LEFT | AudioChannelLayout::CHANNEL_FRONT_RIGHT |
+ AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT | AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT);
}
AudioChannelLayout make_ACL_ChannelIndex2() {
@@ -74,7 +79,7 @@
}
AudioDeviceDescription make_AudioDeviceDescription(AudioDeviceType type,
- const std::string& connection = "") {
+ const std::string& connection = "") {
AudioDeviceDescription result;
result.type = type;
result.connection = connection;
@@ -95,12 +100,12 @@
AudioDeviceDescription make_ADD_WiredHeadset() {
return make_AudioDeviceDescription(AudioDeviceType::OUT_HEADSET,
- AudioDeviceDescription::CONNECTION_ANALOG());
+ AudioDeviceDescription::CONNECTION_ANALOG());
}
AudioDeviceDescription make_ADD_BtScoHeadset() {
return make_AudioDeviceDescription(AudioDeviceType::OUT_HEADSET,
- AudioDeviceDescription::CONNECTION_BT_SCO());
+ AudioDeviceDescription::CONNECTION_BT_SCO());
}
AudioFormatDescription make_AudioFormatDescription(AudioFormatType type) {
@@ -121,8 +126,7 @@
return result;
}
-AudioFormatDescription make_AudioFormatDescription(PcmType transport,
- const std::string& encoding) {
+AudioFormatDescription make_AudioFormatDescription(PcmType transport, const std::string& encoding) {
auto result = make_AudioFormatDescription(encoding);
result.pcm = transport;
return result;
@@ -154,6 +158,22 @@
return afd;
}
+android::media::TrackSecondaryOutputInfo make_TrackSecondaryOutputInfo() {
+ android::media::TrackSecondaryOutputInfo result;
+ result.portId = 1;
+ result.secondaryOutputIds = {0, 5, 7};
+ return result;
+}
+
+ExtraAudioDescriptor make_ExtraAudioDescriptor(AudioStandard audioStandard,
+ AudioEncapsulationType audioEncapsulationType) {
+ ExtraAudioDescriptor result;
+ result.standard = audioStandard;
+ result.audioDescriptor = {0xb4, 0xaf, 0x98, 0x1a};
+ result.encapsulationType = audioEncapsulationType;
+ return result;
+}
+
} // namespace
// Verify that two independently constructed ADDs/AFDs have the same hash.
@@ -163,7 +183,8 @@
// is identical to the same format description constructed by the framework.
class HashIdentityTest : public ::testing::Test {
public:
- template<typename T> void verifyHashIdentity(const std::vector<std::function<T()>>& valueGens) {
+ template <typename T>
+ void verifyHashIdentity(const std::vector<std::function<T()>>& valueGens) {
for (size_t i = 0; i < valueGens.size(); ++i) {
for (size_t j = 0; j < valueGens.size(); ++j) {
if (i == j) {
@@ -177,27 +198,25 @@
};
TEST_F(HashIdentityTest, AudioChannelLayoutHashIdentity) {
- verifyHashIdentity<AudioChannelLayout>({
- make_ACL_None, make_ACL_Invalid, make_ACL_Stereo,
- make_ACL_LayoutArbitrary, make_ACL_ChannelIndex2,
- make_ACL_ChannelIndexArbitrary, make_ACL_VoiceCall});
+ verifyHashIdentity<AudioChannelLayout>({make_ACL_None, make_ACL_Invalid, make_ACL_Stereo,
+ make_ACL_LayoutArbitrary, make_ACL_ChannelIndex2,
+ make_ACL_ChannelIndexArbitrary, make_ACL_VoiceCall});
}
TEST_F(HashIdentityTest, AudioDeviceDescriptionHashIdentity) {
- verifyHashIdentity<AudioDeviceDescription>({
- make_ADD_None, make_ADD_DefaultIn, make_ADD_DefaultOut, make_ADD_WiredHeadset,
- make_ADD_BtScoHeadset});
+ verifyHashIdentity<AudioDeviceDescription>({make_ADD_None, make_ADD_DefaultIn,
+ make_ADD_DefaultOut, make_ADD_WiredHeadset,
+ make_ADD_BtScoHeadset});
}
TEST_F(HashIdentityTest, AudioFormatDescriptionHashIdentity) {
- verifyHashIdentity<AudioFormatDescription>({
- make_AFD_Default, make_AFD_Invalid, make_AFD_Pcm16Bit, make_AFD_Bitstream,
- make_AFD_Encap, make_AFD_Encap_with_Enc});
+ verifyHashIdentity<AudioFormatDescription>({make_AFD_Default, make_AFD_Invalid,
+ make_AFD_Pcm16Bit, make_AFD_Bitstream,
+ make_AFD_Encap, make_AFD_Encap_with_Enc});
}
using ChannelLayoutParam = std::tuple<AudioChannelLayout, bool /*isInput*/>;
-class AudioChannelLayoutRoundTripTest :
- public testing::TestWithParam<ChannelLayoutParam> {};
+class AudioChannelLayoutRoundTripTest : public testing::TestWithParam<ChannelLayoutParam> {};
TEST_P(AudioChannelLayoutRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = std::get<0>(GetParam());
const bool isInput = std::get<1>(GetParam());
@@ -207,21 +226,82 @@
ASSERT_TRUE(convBack.ok());
EXPECT_EQ(initial, convBack.value());
}
-INSTANTIATE_TEST_SUITE_P(AudioChannelLayoutRoundTrip,
- AudioChannelLayoutRoundTripTest,
+
+INSTANTIATE_TEST_SUITE_P(
+ AudioChannelLayoutRoundTrip, AudioChannelLayoutRoundTripTest,
testing::Combine(
testing::Values(AudioChannelLayout{}, make_ACL_Invalid(), make_ACL_Stereo(),
- make_ACL_LayoutArbitrary(), make_ACL_ChannelIndex2(),
- make_ACL_ChannelIndexArbitrary()),
+ make_ACL_LayoutArbitrary(), make_ACL_ChannelIndex2(),
+ make_ACL_ChannelIndexArbitrary(),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BACK_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BACK_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BACK_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_LOW_FREQUENCY),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_SIDE_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_SIDE_RIGHT)),
testing::Values(false, true)));
-INSTANTIATE_TEST_SUITE_P(AudioChannelVoiceRoundTrip,
- AudioChannelLayoutRoundTripTest,
- // In legacy constants the voice call is only defined for input.
- testing::Combine(testing::Values(make_ACL_VoiceCall()), testing::Values(true)));
+INSTANTIATE_TEST_SUITE_P(AudioChannelVoiceRoundTrip, AudioChannelLayoutRoundTripTest,
+ // In legacy constants the voice call is only defined for input.
+ testing::Combine(testing::Values(make_ACL_VoiceCall()),
+ testing::Values(true)));
+
+INSTANTIATE_TEST_SUITE_P(
+ OutAudioChannelLayoutLayoutRoundTrip, AudioChannelLayoutRoundTripTest,
+ testing::Combine(
+ testing::Values(AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_LEFT_OF_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_RIGHT_OF_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_SIDE_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_SIDE_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_FRONT_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_FRONT_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_FRONT_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_BACK_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_BACK_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_TOP_BACK_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BOTTOM_FRONT_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BOTTOM_FRONT_CENTER),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_BOTTOM_FRONT_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_LOW_FREQUENCY_2),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_WIDE_LEFT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_FRONT_WIDE_RIGHT),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_HAPTIC_A),
+ AudioChannelLayout::make<AudioChannelLayout::Tag::layoutMask>(
+ AudioChannelLayout::CHANNEL_HAPTIC_B)),
+ testing::Values(false)));
using ChannelLayoutEdgeCaseParam = std::tuple<int /*legacy*/, bool /*isInput*/, bool /*isValid*/>;
-class AudioChannelLayoutEdgeCaseTest :
- public testing::TestWithParam<ChannelLayoutEdgeCaseParam> {};
+class AudioChannelLayoutEdgeCaseTest : public testing::TestWithParam<ChannelLayoutEdgeCaseParam> {};
TEST_P(AudioChannelLayoutEdgeCaseTest, Legacy2Aidl) {
const audio_channel_mask_t legacy = static_cast<audio_channel_mask_t>(std::get<0>(GetParam()));
const bool isInput = std::get<1>(GetParam());
@@ -229,8 +309,8 @@
auto conv = legacy2aidl_audio_channel_mask_t_AudioChannelLayout(legacy, isInput);
EXPECT_EQ(isValid, conv.ok());
}
-INSTANTIATE_TEST_SUITE_P(AudioChannelLayoutEdgeCase,
- AudioChannelLayoutEdgeCaseTest,
+INSTANTIATE_TEST_SUITE_P(
+ AudioChannelLayoutEdgeCase, AudioChannelLayoutEdgeCaseTest,
testing::Values(
// Valid legacy input masks.
std::make_tuple(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO, true, true),
@@ -240,25 +320,26 @@
std::make_tuple(
// This has the same numerical representation as Mask 'A' below
AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT, false, true),
+ AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT,
+ false, true),
std::make_tuple(
// This has the same numerical representation as Mask 'B' below
AUDIO_CHANNEL_OUT_FRONT_CENTER | AUDIO_CHANNEL_OUT_LOW_FREQUENCY |
- AUDIO_CHANNEL_OUT_TOP_BACK_LEFT, false, true),
+ AUDIO_CHANNEL_OUT_TOP_BACK_LEFT,
+ false, true),
// Invalid legacy input masks.
std::make_tuple(AUDIO_CHANNEL_IN_6, true, false),
- std::make_tuple(
- AUDIO_CHANNEL_IN_6 | AUDIO_CHANNEL_IN_FRONT_PROCESSED, true, false),
- std::make_tuple(
- AUDIO_CHANNEL_IN_PRESSURE | AUDIO_CHANNEL_IN_X_AXIS |
- AUDIO_CHANNEL_IN_Y_AXIS | AUDIO_CHANNEL_IN_Z_AXIS, true, false),
+ std::make_tuple(AUDIO_CHANNEL_IN_6 | AUDIO_CHANNEL_IN_FRONT_PROCESSED, true, false),
+ std::make_tuple(AUDIO_CHANNEL_IN_PRESSURE | AUDIO_CHANNEL_IN_X_AXIS |
+ AUDIO_CHANNEL_IN_Y_AXIS | AUDIO_CHANNEL_IN_Z_AXIS,
+ true, false),
std::make_tuple( // Mask 'A'
AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_VOICE_UPLINK, true, false),
std::make_tuple( // Mask 'B'
AUDIO_CHANNEL_IN_STEREO | AUDIO_CHANNEL_IN_VOICE_DNLINK, true, false)));
-class AudioDeviceDescriptionRoundTripTest :
- public testing::TestWithParam<AudioDeviceDescription> {};
+class AudioDeviceDescriptionRoundTripTest : public testing::TestWithParam<AudioDeviceDescription> {
+};
TEST_P(AudioDeviceDescriptionRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = GetParam();
auto conv = aidl2legacy_AudioDeviceDescription_audio_devices_t(initial);
@@ -267,13 +348,13 @@
ASSERT_TRUE(convBack.ok());
EXPECT_EQ(initial, convBack.value());
}
-INSTANTIATE_TEST_SUITE_P(AudioDeviceDescriptionRoundTrip,
- AudioDeviceDescriptionRoundTripTest,
- testing::Values(AudioDeviceDescription{}, make_ADD_DefaultIn(),
- make_ADD_DefaultOut(), make_ADD_WiredHeadset(), make_ADD_BtScoHeadset()));
+INSTANTIATE_TEST_SUITE_P(AudioDeviceDescriptionRoundTrip, AudioDeviceDescriptionRoundTripTest,
+ testing::Values(AudioDeviceDescription{}, make_ADD_DefaultIn(),
+ make_ADD_DefaultOut(), make_ADD_WiredHeadset(),
+ make_ADD_BtScoHeadset()));
-class AudioFormatDescriptionRoundTripTest :
- public testing::TestWithParam<AudioFormatDescription> {};
+class AudioFormatDescriptionRoundTripTest : public testing::TestWithParam<AudioFormatDescription> {
+};
TEST_P(AudioFormatDescriptionRoundTripTest, Aidl2Legacy2Aidl) {
const auto initial = GetParam();
auto conv = aidl2legacy_AudioFormatDescription_audio_format_t(initial);
@@ -282,6 +363,140 @@
ASSERT_TRUE(convBack.ok());
EXPECT_EQ(initial, convBack.value());
}
-INSTANTIATE_TEST_SUITE_P(AudioFormatDescriptionRoundTrip,
- AudioFormatDescriptionRoundTripTest,
- testing::Values(make_AFD_Invalid(), AudioFormatDescription{}, make_AFD_Pcm16Bit()));
+INSTANTIATE_TEST_SUITE_P(AudioFormatDescriptionRoundTrip, AudioFormatDescriptionRoundTripTest,
+ testing::Values(make_AFD_Invalid(), AudioFormatDescription{},
+ make_AFD_Pcm16Bit()));
+
+class AudioDirectModeRoundTripTest : public testing::TestWithParam<AudioDirectMode> {};
+TEST_P(AudioDirectModeRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv = aidl2legacy_AudioDirectMode_audio_direct_mode_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_direct_mode_t_AudioDirectMode(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioDirectMode, AudioDirectModeRoundTripTest,
+ testing::Values(AudioDirectMode::NONE, AudioDirectMode::OFFLOAD,
+ AudioDirectMode::OFFLOAD_GAPLESS,
+ AudioDirectMode::BITSTREAM));
+
+class AudioStandardRoundTripTest : public testing::TestWithParam<AudioStandard> {};
+TEST_P(AudioStandardRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv = aidl2legacy_AudioStandard_audio_standard_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_standard_t_AudioStandard(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioStandard, AudioStandardRoundTripTest,
+ testing::Values(AudioStandard::NONE, AudioStandard::EDID));
+
+class AudioEncapsulationMetadataTypeRoundTripTest
+ : public testing::TestWithParam<AudioEncapsulationMetadataType> {};
+TEST_P(AudioEncapsulationMetadataTypeRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv =
+ aidl2legacy_AudioEncapsulationMetadataType_audio_encapsulation_metadata_type_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_encapsulation_metadata_type_t_AudioEncapsulationMetadataType(
+ conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioEncapsulationMetadataType,
+ AudioEncapsulationMetadataTypeRoundTripTest,
+ testing::Values(AudioEncapsulationMetadataType::NONE,
+ AudioEncapsulationMetadataType::FRAMEWORK_TUNER,
+ AudioEncapsulationMetadataType::DVB_AD_DESCRIPTOR));
+
+class AudioGainModeRoundTripTest : public testing::TestWithParam<AudioGainMode> {};
+TEST_P(AudioGainModeRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = GetParam();
+ auto conv = aidl2legacy_AudioGainMode_audio_gain_mode_t(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_gain_mode_t_AudioGainMode(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+INSTANTIATE_TEST_SUITE_P(AudioGainMode, AudioGainModeRoundTripTest,
+ testing::Values(AudioGainMode::JOINT, AudioGainMode::CHANNELS,
+ AudioGainMode::RAMP));
+
+TEST(AudioTrackSecondaryOutputInfoRoundTripTest, Aidl2Legacy2Aidl) {
+ const auto initial = make_TrackSecondaryOutputInfo();
+ auto conv = aidl2legacy_TrackSecondaryOutputInfo_TrackSecondaryOutputInfoPair(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+using ExtraAudioDescriptorParam = std::tuple<AudioStandard, AudioEncapsulationType>;
+class ExtraAudioDescriptorRoundTripTest : public testing::TestWithParam<ExtraAudioDescriptorParam> {
+};
+TEST_P(ExtraAudioDescriptorRoundTripTest, Aidl2Legacy2Aidl) {
+ ExtraAudioDescriptor initial =
+ make_ExtraAudioDescriptor(std::get<0>(GetParam()), std::get<1>(GetParam()));
+ auto conv = aidl2legacy_ExtraAudioDescriptor_audio_extra_audio_descriptor(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_extra_audio_descriptor_ExtraAudioDescriptor(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ExtraAudioDescriptor, ExtraAudioDescriptorRoundTripTest,
+ testing::Values(std::make_tuple(AudioStandard::NONE, AudioEncapsulationType::NONE),
+ std::make_tuple(AudioStandard::EDID, AudioEncapsulationType::NONE),
+ std::make_tuple(AudioStandard::EDID, AudioEncapsulationType::IEC61937)));
+
+TEST(AudioPortSessionExtRoundTripTest, Aidl2Legacy2Aidl) {
+ const int32_t initial = 7;
+ auto conv = aidl2legacy_int32_t_audio_port_session_ext(initial);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = legacy2aidl_audio_port_session_ext_int32_t(conv.value());
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial, convBack.value());
+}
+
+class AudioGainTest : public testing::TestWithParam<bool> {};
+TEST_P(AudioGainTest, Legacy2Aidl2Legacy) {
+ audio_port_v7 port;
+ port.num_gains = 2;
+ port.gains[0] = {.mode = AUDIO_GAIN_MODE_JOINT,
+ .channel_mask = AUDIO_CHANNEL_IN_STEREO,
+ .min_value = -3200,
+ .max_value = 600,
+ .default_value = 0,
+ .step_value = 100,
+ .min_ramp_ms = 10,
+ .max_ramp_ms = 20};
+ port.gains[1] = {.mode = AUDIO_GAIN_MODE_JOINT,
+ .channel_mask = AUDIO_CHANNEL_IN_MONO,
+ .min_value = -8800,
+ .max_value = 4000,
+ .default_value = 0,
+ .step_value = 100,
+ .min_ramp_ms = 192,
+ .max_ramp_ms = 224};
+
+ const auto isInput = GetParam();
+ for (int i = 0; i < port.num_gains; i++) {
+ auto initial = port.gains[i];
+ auto conv = legacy2aidl_audio_gain_AudioGain(initial, isInput);
+ ASSERT_TRUE(conv.ok());
+ auto convBack = aidl2legacy_AudioGain_audio_gain(conv.value(), isInput);
+ ASSERT_TRUE(convBack.ok());
+ EXPECT_EQ(initial.mode, convBack.value().mode);
+ EXPECT_EQ(initial.channel_mask, convBack.value().channel_mask);
+ EXPECT_EQ(initial.min_value, convBack.value().min_value);
+ EXPECT_EQ(initial.max_value, convBack.value().max_value);
+ EXPECT_EQ(initial.default_value, convBack.value().default_value);
+ EXPECT_EQ(initial.step_value, convBack.value().step_value);
+ EXPECT_EQ(initial.min_ramp_ms, convBack.value().min_ramp_ms);
+ EXPECT_EQ(initial.max_ramp_ms, convBack.value().max_ramp_ms);
+ }
+}
+INSTANTIATE_TEST_SUITE_P(AudioGain, AudioGainTest, testing::Values(true, false));
diff --git a/media/libaudioclient/tests/audio_aidl_status_tests.cpp b/media/libaudioclient/tests/audio_aidl_status_tests.cpp
index 5517091..8a7e6c1 100644
--- a/media/libaudioclient/tests/audio_aidl_status_tests.cpp
+++ b/media/libaudioclient/tests/audio_aidl_status_tests.cpp
@@ -37,25 +37,10 @@
// Special status values are preserved on round trip.
TEST(audio_aidl_status_tests, statusRoundTripSpecialValues) {
- for (status_t status : {
- OK,
- UNKNOWN_ERROR,
- NO_MEMORY,
- INVALID_OPERATION,
- BAD_VALUE,
- BAD_TYPE,
- NAME_NOT_FOUND,
- PERMISSION_DENIED,
- NO_INIT,
- ALREADY_EXISTS,
- DEAD_OBJECT,
- FAILED_TRANSACTION,
- BAD_INDEX,
- NOT_ENOUGH_DATA,
- WOULD_BLOCK,
- TIMED_OUT,
- UNKNOWN_TRANSACTION,
- FDS_NOT_ALLOWED}) {
+ for (status_t status :
+ {OK, UNKNOWN_ERROR, NO_MEMORY, INVALID_OPERATION, BAD_VALUE, BAD_TYPE, NAME_NOT_FOUND,
+ PERMISSION_DENIED, NO_INIT, ALREADY_EXISTS, DEAD_OBJECT, FAILED_TRANSACTION, BAD_INDEX,
+ NOT_ENOUGH_DATA, WOULD_BLOCK, TIMED_OUT, UNKNOWN_TRANSACTION, FDS_NOT_ALLOWED}) {
ASSERT_EQ(status, statusTFromBinderStatus(binderStatusFromStatusT(status)));
}
}
@@ -63,47 +48,29 @@
// Binder exceptions show as an error (not fixed at this time); these come fromExceptionCode().
TEST(audio_aidl_status_tests, binderStatusExceptions) {
for (int exceptionCode : {
- //Status::EX_NONE,
- Status::EX_SECURITY,
- Status::EX_BAD_PARCELABLE,
- Status::EX_ILLEGAL_ARGUMENT,
- Status::EX_NULL_POINTER,
- Status::EX_ILLEGAL_STATE,
- Status::EX_NETWORK_MAIN_THREAD,
- Status::EX_UNSUPPORTED_OPERATION,
- //Status::EX_SERVICE_SPECIFIC, -- tested fromServiceSpecificError()
- Status::EX_PARCELABLE,
- // This is special and Java specific; see Parcel.java.
- Status::EX_HAS_REPLY_HEADER,
- // This is special, and indicates to C++ binder proxies that the
- // transaction has failed at a low level.
- //Status::EX_TRANSACTION_FAILED, -- tested fromStatusT().
- }) {
+ // Status::EX_NONE,
+ Status::EX_SECURITY, Status::EX_BAD_PARCELABLE, Status::EX_ILLEGAL_ARGUMENT,
+ Status::EX_NULL_POINTER, Status::EX_ILLEGAL_STATE, Status::EX_NETWORK_MAIN_THREAD,
+ Status::EX_UNSUPPORTED_OPERATION,
+ // Status::EX_SERVICE_SPECIFIC, -- tested fromServiceSpecificError()
+ Status::EX_PARCELABLE,
+ // This is special and Java specific; see Parcel.java.
+ Status::EX_HAS_REPLY_HEADER,
+ // This is special, and indicates to C++ binder proxies that the
+ // transaction has failed at a low level.
+ // Status::EX_TRANSACTION_FAILED, -- tested fromStatusT().
+ }) {
ASSERT_NE(OK, statusTFromBinderStatus(Status::fromExceptionCode(exceptionCode)));
}
}
// Binder transaction errors show exactly in status_t; these come fromStatusT().
TEST(audio_aidl_status_tests, binderStatusTransactionError) {
- for (status_t status : {
- OK, // Note: fromStatusT does check if this is 0, so this is no error.
- UNKNOWN_ERROR,
- NO_MEMORY,
- INVALID_OPERATION,
- BAD_VALUE,
- BAD_TYPE,
- NAME_NOT_FOUND,
- PERMISSION_DENIED,
- NO_INIT,
- ALREADY_EXISTS,
- DEAD_OBJECT,
- FAILED_TRANSACTION,
- BAD_INDEX,
- NOT_ENOUGH_DATA,
- WOULD_BLOCK,
- TIMED_OUT,
- UNKNOWN_TRANSACTION,
- FDS_NOT_ALLOWED}) {
+ for (status_t status :
+ {OK, // Note: fromStatusT does check if this is 0, so this is no error.
+ UNKNOWN_ERROR, NO_MEMORY, INVALID_OPERATION, BAD_VALUE, BAD_TYPE, NAME_NOT_FOUND,
+ PERMISSION_DENIED, NO_INIT, ALREADY_EXISTS, DEAD_OBJECT, FAILED_TRANSACTION, BAD_INDEX,
+ NOT_ENOUGH_DATA, WOULD_BLOCK, TIMED_OUT, UNKNOWN_TRANSACTION, FDS_NOT_ALLOWED}) {
ASSERT_EQ(status, statusTFromBinderStatus(Status::fromStatusT(status)));
}
}
diff --git a/media/libaudioclient/tests/audio_test_template.xml b/media/libaudioclient/tests/audio_test_template.xml
new file mode 100644
index 0000000..ed0cb21
--- /dev/null
+++ b/media/libaudioclient/tests/audio_test_template.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Unit test configuration for {MODULE}">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="{MODULE}" value="/data/local/tmp/{MODULE}" />
+
+ <!-- Files used for audio testing -->
+ <option name="push-file" key="bbb_1ch_8kHz_s16le.raw" value="/data/local/tmp/bbb_1ch_8kHz_s16le.raw" />
+ <option name="push-file" key="bbb_2ch_24kHz_s16le.raw" value="/data/local/tmp/bbb_2ch_24kHz_s16le.raw" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="{MODULE}" />
+ </test>
+</configuration>
diff --git a/media/libaudioclient/tests/audio_test_utils.cpp b/media/libaudioclient/tests/audio_test_utils.cpp
new file mode 100644
index 0000000..44f0f50
--- /dev/null
+++ b/media/libaudioclient/tests/audio_test_utils.cpp
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioTestUtils"
+
+#include <system/audio_config.h>
+#include <utils/Log.h>
+
+#include "audio_test_utils.h"
+
+template <class T>
+constexpr void (*xmlDeleter)(T* t);
+template <>
+constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
+template <>
+constexpr auto xmlDeleter<xmlChar> = [](xmlChar* s) { xmlFree(s); };
+
+/** @return a unique_ptr with the correct deleter for the libxml2 object. */
+template <class T>
+constexpr auto make_xmlUnique(T* t) {
+ // Wrap deleter in lambda to enable empty base optimization
+ auto deleter = [](T* t) { xmlDeleter<T>(t); };
+ return std::unique_ptr<T, decltype(deleter)>{t, deleter};
+}
+
+// Generates a random string.
+void CreateRandomFile(int& fd) {
+ std::string filename = "/data/local/tmp/record-XXXXXX";
+ fd = mkstemp(filename.data());
+}
+
+void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
+ audio_port_handle_t deviceId) {
+ std::unique_lock<std::mutex> lock{mMutex};
+ ALOGD("%s audioIo=%d deviceId=%d", __func__, audioIo, deviceId);
+ mAudioIo = audioIo;
+ mDeviceId = deviceId;
+ mCondition.notify_all();
+}
+
+status_t OnAudioDeviceUpdateNotifier::waitForAudioDeviceCb() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ if (mAudioIo == AUDIO_IO_HANDLE_NONE) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(500));
+ if (mAudioIo == AUDIO_IO_HANDLE_NONE) return TIMED_OUT;
+ }
+ return OK;
+}
+
+AudioPlayback::AudioPlayback(uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, audio_output_flags_t flags,
+ audio_session_t sessionId, AudioTrack::transfer_type transferType,
+ audio_attributes_t* attributes, audio_offload_info_t* info)
+ : mSampleRate(sampleRate),
+ mFormat(format),
+ mChannelMask(channelMask),
+ mFlags(flags),
+ mSessionId(sessionId),
+ mTransferType(transferType),
+ mAttributes(attributes),
+ mOffloadInfo(info) {
+ mStopPlaying = false;
+ mBytesUsedSoFar = 0;
+ mState = PLAY_NO_INIT;
+ mMemCapacity = 0;
+ mMemoryDealer = nullptr;
+ mMemory = nullptr;
+}
+
+AudioPlayback::~AudioPlayback() {
+ stop();
+}
+
+status_t AudioPlayback::create() {
+ if (mState != PLAY_NO_INIT) return INVALID_OPERATION;
+ std::string packageName{"AudioPlayback"};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ if (mTransferType == AudioTrack::TRANSFER_OBTAIN) {
+ mTrack = new AudioTrack(attributionSource);
+ mTrack->set(AUDIO_STREAM_MUSIC, mSampleRate, mFormat, mChannelMask, 0 /* frameCount */,
+ mFlags, nullptr /* callback */, 0 /* notificationFrames */,
+ nullptr /* sharedBuffer */, false /*canCallJava */, mSessionId, mTransferType,
+ mOffloadInfo, attributionSource, mAttributes);
+ } else if (mTransferType == AudioTrack::TRANSFER_SHARED) {
+ mTrack = new AudioTrack(AUDIO_STREAM_MUSIC, mSampleRate, mFormat, mChannelMask, mMemory,
+ mFlags, wp<AudioTrack::IAudioTrackCallback>::fromExisting(this), 0,
+ mSessionId, mTransferType, nullptr, attributionSource, mAttributes);
+ } else {
+ ALOGE("Test application is not handling transfer type %s",
+ AudioTrack::convertTransferToText(mTransferType));
+ return INVALID_OPERATION;
+ }
+ mTrack->setCallerName(packageName);
+ status_t status = mTrack->initCheck();
+ if (NO_ERROR == status) mState = PLAY_READY;
+ return status;
+}
+
+status_t AudioPlayback::loadResource(const char* name) {
+ status_t status = OK;
+ FILE* fp = fopen(name, "rbe");
+ struct stat buf {};
+ if (fp && !fstat(fileno(fp), &buf)) {
+ mMemCapacity = buf.st_size;
+ mMemoryDealer = new MemoryDealer(mMemCapacity, "AudioPlayback");
+ if (nullptr == mMemoryDealer.get()) {
+ ALOGE("couldn't get MemoryDealer!");
+ fclose(fp);
+ return NO_MEMORY;
+ }
+ mMemory = mMemoryDealer->allocate(mMemCapacity);
+ if (nullptr == mMemory.get()) {
+ ALOGE("couldn't get IMemory!");
+ fclose(fp);
+ return NO_MEMORY;
+ }
+ uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mMemory->unsecurePointer()));
+ fread(ipBuffer, sizeof(uint8_t), mMemCapacity, fp);
+ } else {
+ ALOGE("unable to open input file %s", name);
+ status = NAME_NOT_FOUND;
+ }
+ if (fp) fclose(fp);
+ return status;
+}
+
+sp<AudioTrack> AudioPlayback::getAudioTrackHandle() {
+ return (PLAY_NO_INIT != mState) ? mTrack : nullptr;
+}
+
+status_t AudioPlayback::start() {
+ status_t status;
+ if (PLAY_READY != mState) {
+ return INVALID_OPERATION;
+ } else {
+ status = mTrack->start();
+ if (OK == status) {
+ mState = PLAY_STARTED;
+ LOG_FATAL_IF(false != mTrack->stopped());
+ }
+ }
+ return status;
+}
+
+void AudioPlayback::onBufferEnd() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mStopPlaying = true;
+ mCondition.notify_all();
+}
+
+status_t AudioPlayback::fillBuffer() {
+ if (PLAY_STARTED != mState && PLAY_STOPPED != mState) return INVALID_OPERATION;
+ int retry = 25;
+ uint8_t* ipBuffer = static_cast<uint8_t*>(static_cast<void*>(mMemory->unsecurePointer()));
+ size_t nonContig = 0;
+ size_t bytesAvailable = mMemCapacity - mBytesUsedSoFar;
+ while (bytesAvailable > 0) {
+ AudioTrack::Buffer trackBuffer;
+ trackBuffer.frameCount = mTrack->frameCount() * 2;
+ status_t status = mTrack->obtainBuffer(&trackBuffer, retry, &nonContig);
+ if (OK == status) {
+ size_t bytesToCopy = std::min(bytesAvailable, trackBuffer.size());
+ if (bytesToCopy > 0) {
+ memcpy(trackBuffer.data(), ipBuffer + mBytesUsedSoFar, bytesToCopy);
+ }
+ mTrack->releaseBuffer(&trackBuffer);
+ mBytesUsedSoFar += bytesToCopy;
+ bytesAvailable = mMemCapacity - mBytesUsedSoFar;
+ if (bytesAvailable == 0) {
+ stop();
+ }
+ } else if (WOULD_BLOCK == status) {
+ if (mStopPlaying)
+ return OK;
+ else
+ return TIMED_OUT;
+ }
+ }
+ return OK;
+}
+
+status_t AudioPlayback::waitForConsumption(bool testSeek) {
+ if (PLAY_STARTED != mState) return INVALID_OPERATION;
+ // in static buffer mode, lets not play clips with duration > 30 sec
+ int retry = 30;
+ // Total number of frames in the input file.
+ size_t totalFrameCount = mMemCapacity / mTrack->frameSize();
+ while (!mStopPlaying && retry > 0) {
+ // Get the total numbers of frames played.
+ uint32_t currPosition;
+ mTrack->getPosition(&currPosition);
+ if (testSeek && (currPosition > totalFrameCount * 0.6)) {
+ testSeek = false;
+ if (!mTrack->hasStarted()) return BAD_VALUE;
+ mTrack->pauseAndWait(std::chrono::seconds(2));
+ if (mTrack->hasStarted()) return BAD_VALUE;
+ mTrack->reload();
+ mTrack->getPosition(&currPosition);
+ if (currPosition != 0) return BAD_VALUE;
+ mTrack->start();
+ while (currPosition < totalFrameCount * 0.3) {
+ mTrack->getPosition(&currPosition);
+ }
+ mTrack->pauseAndWait(std::chrono::seconds(2));
+ uint32_t setPosition = totalFrameCount * 0.9;
+ mTrack->setPosition(setPosition);
+ uint32_t bufferPosition;
+ mTrack->getBufferPosition(&bufferPosition);
+ if (bufferPosition != setPosition) return BAD_VALUE;
+ mTrack->start();
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(300));
+ retry--;
+ }
+ if (!mStopPlaying) return TIMED_OUT;
+ return OK;
+}
+
+status_t AudioPlayback::onProcess(bool testSeek) {
+ if (mTransferType == AudioTrack::TRANSFER_SHARED)
+ return waitForConsumption(testSeek);
+ else if (mTransferType == AudioTrack::TRANSFER_OBTAIN)
+ return fillBuffer();
+ else
+ return INVALID_OPERATION;
+}
+
+void AudioPlayback::stop() {
+ std::unique_lock<std::mutex> lock{mMutex};
+ mStopPlaying = true;
+ if (mState != PLAY_STOPPED) {
+ int32_t msec = 0;
+ (void)mTrack->pendingDuration(&msec);
+ mTrack->stopAndJoinCallbacks();
+ LOG_FATAL_IF(true != mTrack->stopped());
+ mState = PLAY_STOPPED;
+ if (msec > 0) {
+ ALOGD("deleting recycled track, waiting for data drain (%d msec)", msec);
+ usleep(msec * 1000LL);
+ }
+ }
+}
+
+// hold pcm data sent by AudioRecord
+RawBuffer::RawBuffer(int64_t ptsPipeline, int64_t ptsManual, int32_t capacity)
+ : mData(capacity > 0 ? new uint8_t[capacity] : nullptr),
+ mPtsPipeline(ptsPipeline),
+ mPtsManual(ptsManual),
+ mCapacity(capacity) {}
+
+// Simple AudioCapture
+size_t AudioCapture::onMoreData(const AudioRecord::Buffer& buffer) {
+ if (mState != REC_STARTED) {
+ ALOGE("Unexpected Callback from audiorecord, not reading data");
+ return 0;
+ }
+
+ // no more frames to read
+ if (mNumFramesReceived > mNumFramesToRecord || mStopRecording) {
+ mStopRecording = true;
+ return 0;
+ }
+
+ int64_t timeUs = 0, position = 0, timeNs = 0;
+ ExtendedTimestamp ts;
+ ExtendedTimestamp::Location location;
+ const int32_t usPerSec = 1000000;
+
+ if (mRecord->getTimestamp(&ts) == OK &&
+ ts.getBestTimestamp(&position, &timeNs, ExtendedTimestamp::TIMEBASE_MONOTONIC, &location) ==
+ OK) {
+ // Use audio timestamp.
+ timeUs = timeNs / 1000 -
+ (position - mNumFramesReceived + mNumFramesLost) * usPerSec / mSampleRate;
+ } else {
+ // This should not happen in normal case.
+ ALOGW("Failed to get audio timestamp, fallback to use systemclock");
+ timeUs = systemTime() / 1000LL;
+ // Estimate the real sampling time of the 1st sample in this buffer
+ // from AudioRecord's latency. (Apply this adjustment first so that
+ // the start time logic is not affected.)
+ timeUs -= mRecord->latency() * 1000LL;
+ }
+
+ ALOGV("dataCallbackTimestamp: %" PRId64 " us", timeUs);
+
+ const size_t frameSize = mRecord->frameSize();
+ uint64_t numLostBytes = (uint64_t)mRecord->getInputFramesLost() * frameSize;
+ if (numLostBytes > 0) {
+ ALOGW("Lost audio record data: %" PRIu64 " bytes", numLostBytes);
+ }
+ std::deque<RawBuffer> tmpQueue;
+ while (numLostBytes > 0) {
+ uint64_t bufferSize = numLostBytes;
+ if (numLostBytes > mMaxBytesPerCallback) {
+ numLostBytes -= mMaxBytesPerCallback;
+ bufferSize = mMaxBytesPerCallback;
+ } else {
+ numLostBytes = 0;
+ }
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer emptyBuffer{timeUs, timestampUs, static_cast<int32_t>(bufferSize)};
+ memset(emptyBuffer.mData.get(), 0, bufferSize);
+ mNumFramesLost += bufferSize / frameSize;
+ mNumFramesReceived += bufferSize / frameSize;
+ tmpQueue.push_back(std::move(emptyBuffer));
+ }
+
+ if (buffer.size() == 0) {
+ ALOGW("Nothing is available from AudioRecord callback buffer");
+ } else {
+ const size_t bufferSize = buffer.size();
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer audioBuffer{timeUs, timestampUs, static_cast<int32_t>(bufferSize)};
+ memcpy(audioBuffer.mData.get(), buffer.data(), bufferSize);
+ mNumFramesReceived += bufferSize / frameSize;
+ tmpQueue.push_back(std::move(audioBuffer));
+ }
+
+ if (tmpQueue.size() > 0) {
+ std::unique_lock<std::mutex> lock{mMutex};
+ for (auto it = tmpQueue.begin(); it != tmpQueue.end(); it++)
+ mBuffersReceived.push_back(std::move(*it));
+ mCondition.notify_all();
+ }
+ return buffer.size();
+}
+
+void AudioCapture::onOverrun() {
+ ALOGV("received event overrun");
+ mBufferOverrun = true;
+}
+
+void AudioCapture::onMarker(uint32_t markerPosition) {
+ ALOGV("received Callback at position %d", markerPosition);
+ mReceivedCbMarkerAtPosition = markerPosition;
+}
+
+void AudioCapture::onNewPos(uint32_t markerPosition) {
+ ALOGV("received Callback at position %d", markerPosition);
+ mReceivedCbMarkerCount++;
+}
+
+void AudioCapture::onNewIAudioRecord() {
+ ALOGV("IAudioRecord is re-created");
+}
+
+AudioCapture::AudioCapture(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask, audio_input_flags_t flags,
+ audio_session_t sessionId, AudioRecord::transfer_type transferType)
+ : mInputSource(inputSource),
+ mSampleRate(sampleRate),
+ mFormat(format),
+ mChannelMask(channelMask),
+ mFlags(flags),
+ mSessionId(sessionId),
+ mTransferType(transferType) {
+ mFrameCount = 0;
+ mNotificationFrames = 0;
+ mNumFramesToRecord = 0;
+ mNumFramesReceived = 0;
+ mNumFramesLost = 0;
+ mBufferOverrun = false;
+ mMarkerPosition = 0;
+ mMarkerPeriod = 0;
+ mReceivedCbMarkerAtPosition = -1;
+ mReceivedCbMarkerCount = 0;
+ mState = REC_NO_INIT;
+ mStopRecording = false;
+#if RECORD_TO_FILE
+ CreateRandomFile(mOutFileFd);
+#endif
+}
+
+AudioCapture::~AudioCapture() {
+ if (mOutFileFd > 0) close(mOutFileFd);
+ stop();
+}
+
+status_t AudioCapture::create() {
+ if (mState != REC_NO_INIT) return INVALID_OPERATION;
+ // get Min Frame Count
+ size_t minFrameCount;
+ status_t status =
+ AudioRecord::getMinFrameCount(&minFrameCount, mSampleRate, mFormat, mChannelMask);
+ if (NO_ERROR != status) return status;
+ // Limit notificationFrames basing on client bufferSize
+ const int samplesPerFrame = audio_channel_count_from_in_mask(mChannelMask);
+ const int bytesPerSample = audio_bytes_per_sample(mFormat);
+ mNotificationFrames = mMaxBytesPerCallback / (samplesPerFrame * bytesPerSample);
+ // select frameCount to be at least minFrameCount
+ mFrameCount = 2 * mNotificationFrames;
+ while (mFrameCount < minFrameCount) {
+ mFrameCount += mNotificationFrames;
+ }
+ if (mFlags & AUDIO_INPUT_FLAG_FAST) {
+ ALOGW("Overriding all previous computations");
+ mFrameCount = 0;
+ mNotificationFrames = 0;
+ }
+ mNumFramesToRecord = (mSampleRate * 0.25); // record .25 sec
+ std::string packageName{"AudioCapture"};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ if (mTransferType == AudioRecord::TRANSFER_OBTAIN) {
+ if (mSampleRate == 48000) { // test all available constructors
+ mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
+ attributionSource, mFrameCount, nullptr /* callback */,
+ mNotificationFrames, mSessionId, mTransferType, mFlags);
+ } else {
+ mRecord = new AudioRecord(attributionSource);
+ status = mRecord->set(mInputSource, mSampleRate, mFormat, mChannelMask, mFrameCount,
+ nullptr /* callback */, 0 /* notificationFrames */,
+ false /* canCallJava */, mSessionId, mTransferType, mFlags,
+ attributionSource.uid, attributionSource.pid);
+ }
+ if (NO_ERROR != status) return status;
+ } else if (mTransferType == AudioRecord::TRANSFER_CALLBACK) {
+ mRecord = new AudioRecord(mInputSource, mSampleRate, mFormat, mChannelMask,
+ attributionSource, mFrameCount, this, mNotificationFrames,
+ mSessionId, mTransferType, mFlags);
+ } else {
+ ALOGE("Test application is not handling transfer type %s",
+ AudioRecord::convertTransferToText(mTransferType));
+ return NO_INIT;
+ }
+ mRecord->setCallerName(packageName);
+ status = mRecord->initCheck();
+ if (NO_ERROR == status) mState = REC_READY;
+ if (mFlags & AUDIO_INPUT_FLAG_FAST) {
+ mFrameCount = mRecord->frameCount();
+ mNotificationFrames = mRecord->getNotificationPeriodInFrames();
+ mMaxBytesPerCallback = mNotificationFrames * samplesPerFrame * bytesPerSample;
+ }
+ return status;
+}
+
+sp<AudioRecord> AudioCapture::getAudioRecordHandle() {
+ return (REC_NO_INIT == mState) ? nullptr : mRecord;
+}
+
+status_t AudioCapture::start(AudioSystem::sync_event_t event, audio_session_t triggerSession) {
+ status_t status;
+ if (REC_READY != mState) {
+ return INVALID_OPERATION;
+ } else {
+ status = mRecord->start(event, triggerSession);
+ if (OK == status) {
+ mState = REC_STARTED;
+ LOG_FATAL_IF(false != mRecord->stopped());
+ }
+ }
+ return status;
+}
+
+status_t AudioCapture::stop() {
+ status_t status = OK;
+ mStopRecording = true;
+ if (mState != REC_STOPPED) {
+ if (mInputSource != AUDIO_SOURCE_DEFAULT) {
+ bool state = false;
+ status = AudioSystem::isSourceActive(mInputSource, &state);
+ if (status == OK && !state) status = BAD_VALUE;
+ }
+ mRecord->stopAndJoinCallbacks();
+ mState = REC_STOPPED;
+ LOG_FATAL_IF(true != mRecord->stopped());
+ }
+ return status;
+}
+
+status_t AudioCapture::obtainBuffer(RawBuffer& buffer) {
+ if (REC_STARTED != mState && REC_STOPPED != mState) return INVALID_OPERATION;
+ int retry = 25;
+ AudioRecord::Buffer recordBuffer;
+ recordBuffer.frameCount = mNotificationFrames;
+ size_t nonContig = 0;
+ status_t status = mRecord->obtainBuffer(&recordBuffer, retry, &nonContig);
+ if (OK == status) {
+ const int64_t timestampUs =
+ ((1000000LL * mNumFramesReceived) + (mRecord->getSampleRate() >> 1)) /
+ mRecord->getSampleRate();
+ RawBuffer buff{-1, timestampUs, static_cast<int32_t>(recordBuffer.size())};
+ memcpy(buff.mData.get(), recordBuffer.data(), recordBuffer.size());
+ buffer = std::move(buff);
+ mNumFramesReceived += recordBuffer.size() / mRecord->frameSize();
+ mRecord->releaseBuffer(&recordBuffer);
+ if (mNumFramesReceived > mNumFramesToRecord) {
+ stop();
+ }
+ } else if (status == WOULD_BLOCK) {
+ if (mStopRecording)
+ return WOULD_BLOCK;
+ else
+ return TIMED_OUT;
+ }
+ return OK;
+}
+
+status_t AudioCapture::obtainBufferCb(RawBuffer& buffer) {
+ if (REC_STARTED != mState) return INVALID_OPERATION;
+ int retry = 10;
+ std::unique_lock<std::mutex> lock{mMutex};
+ while (mBuffersReceived.empty() && !mStopRecording && retry > 0) {
+ mCondition.wait_for(lock, std::chrono::milliseconds(100));
+ retry--;
+ }
+ if (!mBuffersReceived.empty()) {
+ auto it = mBuffersReceived.begin();
+ buffer = std::move(*it);
+ mBuffersReceived.erase(it);
+ } else {
+ if (retry == 0) return TIMED_OUT;
+ if (mStopRecording)
+ return WOULD_BLOCK;
+ else
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+status_t AudioCapture::audioProcess() {
+ RawBuffer buffer;
+ while (true) {
+ status_t status;
+ if (mTransferType == AudioRecord::TRANSFER_CALLBACK)
+ status = obtainBufferCb(buffer);
+ else
+ status = obtainBuffer(buffer);
+ switch (status) {
+ case OK:
+ if (mOutFileFd > 0) {
+ const char* ptr =
+ static_cast<const char*>(static_cast<void*>(buffer.mData.get()));
+ write(mOutFileFd, ptr, buffer.mCapacity);
+ }
+ break;
+ case WOULD_BLOCK:
+ return OK;
+ case TIMED_OUT: // "recorder application timed out from receiving buffers"
+ case NO_INIT: // "recorder not initialized"
+ case INVALID_OPERATION: // "recorder not started"
+ case UNKNOWN_ERROR: // "Unknown error"
+ default:
+ return status;
+ }
+ }
+}
+
+status_t listAudioPorts(std::vector<audio_port_v7>& portsVec) {
+ int attempts = 5;
+ status_t status;
+ unsigned int generation1, generation;
+ unsigned int numPorts = 0;
+ do {
+ if (attempts-- < 0) {
+ status = TIMED_OUT;
+ break;
+ }
+ status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts,
+ nullptr, &generation1);
+ if (status != NO_ERROR) {
+ ALOGE("AudioSystem::listAudioPorts returned error %d", status);
+ break;
+ }
+ portsVec.resize(numPorts);
+ status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts,
+ portsVec.data(), &generation);
+ } while (generation1 != generation && status == NO_ERROR);
+ if (status != NO_ERROR) {
+ numPorts = 0;
+ portsVec.clear();
+ }
+ return status;
+}
+
+status_t getPortById(const audio_port_handle_t portId, audio_port_v7& port) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ if (status != OK) return status;
+ for (auto i = 0; i < ports.size(); i++) {
+ if (ports[i].id == portId) {
+ port = ports[i];
+ return OK;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
+ audio_devices_t deviceType, audio_port_v7& port) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ if (status != OK) return status;
+ for (auto i = 0; i < ports.size(); i++) {
+ if (ports[i].role == role && ports[i].type == type &&
+ ports[i].ext.device.type == deviceType) {
+ port = ports[i];
+ return OK;
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec) {
+ int attempts = 5;
+ status_t status;
+ unsigned int generation1, generation;
+ unsigned int numPatches = 0;
+ do {
+ if (attempts-- < 0) {
+ status = TIMED_OUT;
+ break;
+ }
+ status = AudioSystem::listAudioPatches(&numPatches, nullptr, &generation1);
+ if (status != NO_ERROR) {
+ ALOGE("AudioSystem::listAudioPatches returned error %d", status);
+ break;
+ }
+ patchesVec.resize(numPatches);
+ status = AudioSystem::listAudioPatches(&numPatches, patchesVec.data(), &generation);
+ } while (generation1 != generation && status == NO_ERROR);
+ if (status != NO_ERROR) {
+ numPatches = 0;
+ patchesVec.clear();
+ }
+ return status;
+}
+
+status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch) {
+ std::vector<struct audio_patch> patches;
+ status_t status = listAudioPatches(patches);
+ if (status != OK) return status;
+
+ for (auto i = 0; i < patches.size(); i++) {
+ for (auto j = 0; j < patches[i].num_sources; j++) {
+ if (patches[i].sources[j].type == AUDIO_PORT_TYPE_MIX &&
+ patches[i].sources[j].ext.mix.handle == audioIo) {
+ patch = patches[i];
+ return OK;
+ }
+ }
+ }
+ return BAD_VALUE;
+}
+
+status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch) {
+ std::vector<struct audio_patch> patches;
+ status_t status = listAudioPatches(patches);
+ if (status != OK) return status;
+
+ for (auto i = 0; i < patches.size(); i++) {
+ for (auto j = 0; j < patches[i].num_sinks; j++) {
+ if (patches[i].sinks[j].type == AUDIO_PORT_TYPE_MIX &&
+ patches[i].sinks[j].ext.mix.handle == audioIo) {
+ patch = patches[i];
+ return OK;
+ }
+ }
+ }
+ return BAD_VALUE;
+}
+
+bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch) {
+ for (auto j = 0; j < patch.num_sinks; j++) {
+ if (patch.sinks[j].type == AUDIO_PORT_TYPE_DEVICE && patch.sinks[j].id == deviceId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool patchContainsInputDevice(audio_port_handle_t deviceId, audio_patch patch) {
+ for (auto j = 0; j < patch.num_sources; j++) {
+ if (patch.sources[j].type == AUDIO_PORT_TYPE_DEVICE && patch.sources[j].id == deviceId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool checkPatchPlayback(audio_io_handle_t audioIo, audio_port_handle_t deviceId) {
+ struct audio_patch patch;
+ if (getPatchForOutputMix(audioIo, patch) == OK) {
+ return patchContainsOutputDevice(deviceId, patch);
+ }
+ return false;
+}
+
+bool checkPatchCapture(audio_io_handle_t audioIo, audio_port_handle_t deviceId) {
+ struct audio_patch patch;
+ if (getPatchForInputMix(audioIo, patch) == OK) {
+ return patchContainsInputDevice(deviceId, patch);
+ }
+ return false;
+}
+
+std::string dumpPortConfig(const audio_port_config& port) {
+ std::ostringstream result;
+ std::string deviceInfo;
+ if (port.type == AUDIO_PORT_TYPE_DEVICE) {
+ if (port.ext.device.type & AUDIO_DEVICE_BIT_IN) {
+ InputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ } else {
+ OutputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ }
+ deviceInfo += std::string(", address = ") + port.ext.device.address;
+ }
+ result << "audio_port_handle_t = " << port.id << ", "
+ << "Role = " << (port.role == AUDIO_PORT_ROLE_SOURCE ? "source" : "sink") << ", "
+ << "Type = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix") << ", "
+ << "deviceInfo = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? deviceInfo : "") << ", "
+ << "config_mask = 0x" << std::hex << port.config_mask << std::dec << ", ";
+ if (port.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ result << "sample rate = " << port.sample_rate << ", ";
+ }
+ if (port.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ result << "channel mask = " << port.channel_mask << ", ";
+ }
+ if (port.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ result << "format = " << port.format << ", ";
+ }
+ result << "input flags = " << port.flags.input << ", ";
+ result << "output flags = " << port.flags.output << ", ";
+ result << "mix io handle = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? 0 : port.ext.mix.handle)
+ << "\n";
+ return result.str();
+}
+
+std::string dumpPatch(const audio_patch& patch) {
+ std::ostringstream result;
+ result << "----------------- Dumping Patch ------------ \n";
+ result << "Patch Handle: " << patch.id << ", sources: " << patch.num_sources
+ << ", sink: " << patch.num_sinks << "\n";
+ audio_port_v7 port;
+ for (uint32_t i = 0; i < patch.num_sources; i++) {
+ result << "----------------- Dumping Source Port Config @ index " << i
+ << " ------------ \n";
+ result << dumpPortConfig(patch.sources[i]);
+ result << "----------------- Dumping Source Port for id " << patch.sources[i].id
+ << " ------------ \n";
+ getPortById(patch.sources[i].id, port);
+ result << dumpPort(port);
+ }
+ for (uint32_t i = 0; i < patch.num_sinks; i++) {
+ result << "----------------- Dumping Sink Port Config @ index " << i << " ------------ \n";
+ result << dumpPortConfig(patch.sinks[i]);
+ result << "----------------- Dumping Sink Port for id " << patch.sinks[i].id
+ << " ------------ \n";
+ getPortById(patch.sinks[i].id, port);
+ result << dumpPort(port);
+ }
+ return result.str();
+}
+
+std::string dumpPort(const audio_port_v7& port) {
+ std::ostringstream result;
+ std::string deviceInfo;
+ if (port.type == AUDIO_PORT_TYPE_DEVICE) {
+ if (port.ext.device.type & AUDIO_DEVICE_BIT_IN) {
+ InputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ } else {
+ OutputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
+ }
+ deviceInfo += std::string(", address = ") + port.ext.device.address;
+ }
+ result << "audio_port_handle_t = " << port.id << ", "
+ << "Role = " << (port.role == AUDIO_PORT_ROLE_SOURCE ? "source" : "sink") << ", "
+ << "Type = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix") << ", "
+ << "deviceInfo = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? deviceInfo : "") << ", "
+ << "Name = " << port.name << ", "
+ << "num profiles = " << port.num_audio_profiles << ", "
+ << "mix io handle = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? 0 : port.ext.mix.handle)
+ << ", ";
+ for (int i = 0; i < port.num_audio_profiles; i++) {
+ result << "AudioProfile = " << i << " {";
+ result << "format = " << port.audio_profiles[i].format << ", ";
+ result << "samplerates = ";
+ for (int j = 0; j < port.audio_profiles[i].num_sample_rates; j++) {
+ result << port.audio_profiles[i].sample_rates[j] << ", ";
+ }
+ result << "channelmasks = ";
+ for (int j = 0; j < port.audio_profiles[i].num_channel_masks; j++) {
+ result << "0x" << std::hex << port.audio_profiles[i].channel_masks[j] << std::dec
+ << ", ";
+ }
+ result << "} ";
+ }
+ result << dumpPortConfig(port.active_config);
+ return result.str();
+}
+
+std::string getXmlAttribute(const xmlNode* cur, const char* attribute) {
+ auto charPtr = make_xmlUnique(xmlGetProp(cur, reinterpret_cast<const xmlChar*>(attribute)));
+ if (charPtr == NULL) {
+ return "";
+ }
+ std::string value(reinterpret_cast<const char*>(charPtr.get()));
+ return value;
+}
+
+status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
+ std::vector<MixPort>& mixPorts,
+ std::vector<Route>& routes) {
+ std::string path = audio_find_readable_configuration_file("audio_policy_configuration.xml");
+ if (path.length() == 0) return UNKNOWN_ERROR;
+ auto doc = make_xmlUnique(xmlParseFile(path.c_str()));
+ if (doc == nullptr) return UNKNOWN_ERROR;
+ xmlNode* root = xmlDocGetRootElement(doc.get());
+ if (root == nullptr) return UNKNOWN_ERROR;
+ if (xmlXIncludeProcess(doc.get()) < 0) return UNKNOWN_ERROR;
+ mixPorts.clear();
+ if (!xmlStrcmp(root->name, reinterpret_cast<const xmlChar*>("audioPolicyConfiguration"))) {
+ std::string raw{getXmlAttribute(root, "version")};
+ for (auto* child = root->xmlChildrenNode; child != nullptr; child = child->next) {
+ if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>("modules"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr; child = child->next) {
+ if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>("module"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("mixPorts"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("mixPort"))) {
+ MixPort mixPort;
+ xmlNode* root = child;
+ mixPort.name = getXmlAttribute(root, "name");
+ mixPort.role = getXmlAttribute(root, "role");
+ mixPort.flags = getXmlAttribute(root, "flags");
+ if (mixPort.role == "source") mixPorts.push_back(mixPort);
+ }
+ }
+ } else if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(
+ "attachedDevices"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("item"))) {
+ auto xmlValue = make_xmlUnique(xmlNodeListGetString(
+ child->doc, child->xmlChildrenNode, 1));
+ if (xmlValue == nullptr) {
+ raw = "";
+ } else {
+ raw = reinterpret_cast<const char*>(xmlValue.get());
+ }
+ std::string& value = raw;
+ attachedDevices.push_back(std::move(value));
+ }
+ }
+ } else if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("routes"))) {
+ xmlNode* root = child;
+ for (auto* child = root->xmlChildrenNode; child != nullptr;
+ child = child->next) {
+ if (!xmlStrcmp(child->name,
+ reinterpret_cast<const xmlChar*>("route"))) {
+ Route route;
+ xmlNode* root = child;
+ route.name = getXmlAttribute(root, "name");
+ route.sources = getXmlAttribute(root, "sources");
+ route.sink = getXmlAttribute(root, "sink");
+ routes.push_back(route);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return OK;
+}
diff --git a/media/libaudioclient/tests/audio_test_utils.h b/media/libaudioclient/tests/audio_test_utils.h
new file mode 100644
index 0000000..f35b65d
--- /dev/null
+++ b/media/libaudioclient/tests/audio_test_utils.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_TEST_UTILS_H_
+#define AUDIO_TEST_UTILS_H_
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <atomic>
+#include <chrono>
+#include <cinttypes>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include <binder/MemoryDealer.h>
+#include <libxml/parser.h>
+#include <libxml/xinclude.h>
+#include <media/AidlConversion.h>
+#include <media/AudioRecord.h>
+#include <media/AudioTrack.h>
+
+#define RECORD_TO_FILE 0
+
+using namespace android;
+
+struct MixPort {
+ std::string name;
+ std::string role;
+ std::string flags;
+};
+
+struct Route {
+ std::string name;
+ std::string sources;
+ std::string sink;
+};
+
+status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
+ std::vector<MixPort>& mixPorts,
+ std::vector<Route>& routes);
+void CreateRandomFile(int& fd);
+status_t listAudioPorts(std::vector<audio_port_v7>& portsVec);
+status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec);
+status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
+ audio_devices_t deviceType, audio_port_v7& port);
+status_t getPatchForOutputMix(audio_io_handle_t audioIo, audio_patch& patch);
+status_t getPatchForInputMix(audio_io_handle_t audioIo, audio_patch& patch);
+bool patchContainsOutputDevice(audio_port_handle_t deviceId, audio_patch patch);
+bool patchContainsInputDevice(audio_port_handle_t deviceId, audio_patch patch);
+bool checkPatchPlayback(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+bool checkPatchCapture(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+std::string dumpPort(const audio_port_v7& port);
+std::string dumpPortConfig(const audio_port_config& port);
+std::string dumpPatch(const audio_patch& patch);
+
+class OnAudioDeviceUpdateNotifier : public AudioSystem::AudioDeviceCallback {
+ public:
+ audio_io_handle_t mAudioIo = AUDIO_IO_HANDLE_NONE;
+ audio_port_handle_t mDeviceId = AUDIO_PORT_HANDLE_NONE;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+
+ void onAudioDeviceUpdate(audio_io_handle_t audioIo, audio_port_handle_t deviceId);
+ status_t waitForAudioDeviceCb();
+};
+
+// Simple AudioPlayback class.
+class AudioPlayback : public AudioTrack::IAudioTrackCallback {
+ friend sp<AudioPlayback>;
+ AudioPlayback(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
+ audio_session_t sessionId = AUDIO_SESSION_NONE,
+ AudioTrack::transfer_type transferType = AudioTrack::TRANSFER_SHARED,
+ audio_attributes_t* attributes = nullptr, audio_offload_info_t* info = nullptr);
+
+ public:
+ status_t loadResource(const char* name);
+ status_t create();
+ sp<AudioTrack> getAudioTrackHandle();
+ status_t start();
+ status_t waitForConsumption(bool testSeek = false);
+ status_t fillBuffer();
+ status_t onProcess(bool testSeek = false);
+ virtual void onBufferEnd() override;
+ void stop();
+
+ bool mStopPlaying;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+
+ enum State {
+ PLAY_NO_INIT,
+ PLAY_READY,
+ PLAY_STARTED,
+ PLAY_STOPPED,
+ };
+
+ private:
+ ~AudioPlayback();
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_output_flags_t mFlags;
+ const audio_session_t mSessionId;
+ const AudioTrack::transfer_type mTransferType;
+ const audio_attributes_t* mAttributes;
+ const audio_offload_info_t* mOffloadInfo;
+
+ size_t mBytesUsedSoFar;
+ State mState;
+ size_t mMemCapacity;
+ sp<MemoryDealer> mMemoryDealer;
+ sp<IMemory> mMemory;
+
+ sp<AudioTrack> mTrack;
+};
+
+// hold pcm data sent by AudioRecord
+class RawBuffer {
+ public:
+ RawBuffer(int64_t ptsPipeline = -1, int64_t ptsManual = -1, int32_t capacity = 0);
+
+ std::unique_ptr<uint8_t[]> mData;
+ int64_t mPtsPipeline;
+ int64_t mPtsManual;
+ int32_t mCapacity;
+};
+
+// Simple AudioCapture
+class AudioCapture : public AudioRecord::IAudioRecordCallback {
+ public:
+ AudioCapture(audio_source_t inputSource, uint32_t sampleRate, audio_format_t format,
+ audio_channel_mask_t channelMask,
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE,
+ audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
+ AudioRecord::transfer_type transferType = AudioRecord::TRANSFER_CALLBACK);
+ ~AudioCapture();
+ size_t onMoreData(const AudioRecord::Buffer& buffer) override;
+ void onOverrun() override;
+ void onMarker(uint32_t markerPosition) override;
+ void onNewPos(uint32_t newPos) override;
+ void onNewIAudioRecord() override;
+ status_t create();
+ sp<AudioRecord> getAudioRecordHandle();
+ status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
+ audio_session_t triggerSession = AUDIO_SESSION_NONE);
+ status_t obtainBufferCb(RawBuffer& buffer);
+ status_t obtainBuffer(RawBuffer& buffer);
+ status_t audioProcess();
+ status_t stop();
+
+ uint32_t mFrameCount;
+ uint32_t mNotificationFrames;
+ int64_t mNumFramesToRecord;
+ int64_t mNumFramesReceived;
+ int64_t mNumFramesLost;
+ uint32_t mMarkerPosition;
+ uint32_t mMarkerPeriod;
+ uint32_t mReceivedCbMarkerAtPosition;
+ uint32_t mReceivedCbMarkerCount;
+ bool mBufferOverrun;
+
+ enum State {
+ REC_NO_INIT,
+ REC_READY,
+ REC_STARTED,
+ REC_STOPPED,
+ };
+
+ private:
+ const audio_source_t mInputSource;
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_input_flags_t mFlags;
+ const audio_session_t mSessionId;
+ const AudioRecord::transfer_type mTransferType;
+
+ size_t mMaxBytesPerCallback = 2048;
+ sp<AudioRecord> mRecord;
+ State mState;
+ bool mStopRecording;
+ int mOutFileFd = -1;
+
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ std::deque<RawBuffer> mBuffersReceived;
+};
+
+#endif // AUDIO_TEST_UTILS_H_
diff --git a/media/libaudioclient/tests/audioclient_serialization_tests.cpp b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
new file mode 100644
index 0000000..93baefd6
--- /dev/null
+++ b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioClientSerializationUnitTests"
+
+#include <cstdint>
+#include <cstdlib>
+#include <ctime>
+
+#include <gtest/gtest.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <xsdc/XsdcSupport.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+template <typename T, typename X, typename FUNC>
+std::vector<T> getFlags(const xsdc_enum_range<X>& range, const FUNC& func,
+ const std::string& findString = {}) {
+ std::vector<T> vec;
+ for (const auto& xsdEnumVal : range) {
+ T enumVal;
+ std::string enumString = toString(xsdEnumVal);
+ if (enumString.find(findString) != std::string::npos &&
+ func(enumString.c_str(), &enumVal)) {
+ vec.push_back(enumVal);
+ }
+ }
+ return vec;
+}
+
+static const std::vector<audio_usage_t> kUsages =
+ getFlags<audio_usage_t, xsd::AudioUsage, decltype(audio_usage_from_string)>(
+ xsdc_enum_range<xsd::AudioUsage>{}, audio_usage_from_string);
+
+static const std::vector<audio_content_type_t> kContentType =
+ getFlags<audio_content_type_t, xsd::AudioContentType,
+ decltype(audio_content_type_from_string)>(xsdc_enum_range<xsd::AudioContentType>{},
+ audio_content_type_from_string);
+
+static const std::vector<audio_source_t> kInputSources =
+ getFlags<audio_source_t, xsd::AudioSource, decltype(audio_source_from_string)>(
+ xsdc_enum_range<xsd::AudioSource>{}, audio_source_from_string);
+
+static const std::vector<audio_stream_type_t> kStreamtypes =
+ getFlags<audio_stream_type_t, xsd::AudioStreamType,
+ decltype(audio_stream_type_from_string)>(xsdc_enum_range<xsd::AudioStreamType>{},
+ audio_stream_type_from_string);
+
+static const std::vector<uint32_t> kMixMatchRules = {
+ RULE_MATCH_ATTRIBUTE_USAGE,
+ RULE_EXCLUDE_ATTRIBUTE_USAGE,
+ RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET,
+ RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET,
+ RULE_MATCH_UID,
+ RULE_EXCLUDE_UID,
+ RULE_MATCH_USERID,
+ RULE_EXCLUDE_USERID,
+};
+
+// Generates a random string.
+std::string CreateRandomString(size_t n) {
+ std::string data =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ srand(static_cast<unsigned int>(time(0)));
+ std::string s(n, ' ');
+ for (size_t i = 0; i < n; ++i) {
+ s[i] = data[rand() % data.size()];
+ }
+ return s;
+}
+
+class FillAudioAttributes {
+ public:
+ void fillAudioAttributes(audio_attributes_t& attr);
+
+ unsigned int mSeed;
+};
+
+void FillAudioAttributes::fillAudioAttributes(audio_attributes_t& attr) {
+ attr.content_type = kContentType[rand() % kContentType.size()];
+ attr.usage = kUsages[rand() % kUsages.size()];
+ attr.source = kInputSources[rand() % kInputSources.size()];
+ // attr.flags -> [0, (1 << (CAPTURE_PRIVATE + 1) - 1)]
+ attr.flags = static_cast<audio_flags_mask_t>(rand() & 0x3fff);
+ sprintf(attr.tags, "%s",
+ CreateRandomString((int)rand() % (AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1)).c_str());
+}
+
+class SerializationTest : public FillAudioAttributes, public ::testing::Test {
+ void SetUp() override {
+ mSeed = static_cast<unsigned int>(time(0));
+ srand(mSeed);
+ }
+};
+
+// UNIT TESTS
+TEST_F(SerializationTest, AudioProductStrategyBinderization) {
+ for (int j = 0; j < 512; j++) {
+ const std::string name{"Test APSBinderization for seed::" + std::to_string(mSeed)};
+ std::vector<AudioAttributes> audioattributesvector;
+ for (auto i = 0; i < 16; i++) {
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ AudioAttributes audioattributes{static_cast<volume_group_t>(rand()),
+ kStreamtypes[rand() % kStreamtypes.size()], attributes};
+ audioattributesvector.push_back(audioattributes);
+ }
+ product_strategy_t psId = static_cast<product_strategy_t>(rand());
+ AudioProductStrategy aps{name, audioattributesvector, psId};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, aps.writeToParcel(&p)) << name;
+
+ AudioProductStrategy apsCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, apsCopy.readFromParcel(&p)) << name;
+ EXPECT_EQ(apsCopy.getName(), name) << name;
+ EXPECT_EQ(apsCopy.getId(), psId) << name;
+ auto avec = apsCopy.getAudioAttributes();
+ EXPECT_EQ(avec.size(), audioattributesvector.size()) << name;
+ for (int i = 0; i < audioattributesvector.size(); i++) {
+ EXPECT_EQ(avec[i].getGroupId(), audioattributesvector[i].getGroupId()) << name;
+ EXPECT_EQ(avec[i].getStreamType(), audioattributesvector[i].getStreamType()) << name;
+ EXPECT_TRUE(avec[i].getAttributes() == audioattributesvector[i].getAttributes())
+ << name;
+ }
+ }
+}
+
+TEST_F(SerializationTest, AudioVolumeGroupBinderization) {
+ for (int j = 0; j < 512; j++) {
+ const std::string name{"Test AVGBinderization for seed::" + std::to_string(mSeed)};
+ volume_group_t groupId = static_cast<volume_group_t>(rand());
+ std::vector<audio_attributes_t> attributesvector;
+ for (auto i = 0; i < 16; i++) {
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ attributesvector.push_back(attributes);
+ }
+ std::vector<audio_stream_type_t> streamsvector;
+ for (auto i = 0; i < 8; i++) {
+ streamsvector.push_back(kStreamtypes[rand() % kStreamtypes.size()]);
+ }
+ AudioVolumeGroup avg{name, groupId, attributesvector, streamsvector};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, avg.writeToParcel(&p));
+
+ AudioVolumeGroup avgCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, avgCopy.readFromParcel(&p)) << name;
+ EXPECT_EQ(avgCopy.getName(), name) << name;
+ EXPECT_EQ(avgCopy.getId(), groupId) << name;
+ auto avec = avgCopy.getAudioAttributes();
+ EXPECT_EQ(avec.size(), attributesvector.size()) << name;
+ for (int i = 0; i < avec.size(); i++) {
+ EXPECT_TRUE(avec[i] == attributesvector[i]) << name;
+ }
+ StreamTypeVector svec = avgCopy.getStreamTypes();
+ EXPECT_EQ(svec.size(), streamsvector.size()) << name;
+ for (int i = 0; i < svec.size(); i++) {
+ EXPECT_EQ(svec[i], streamsvector[i]) << name;
+ }
+ }
+}
+
+TEST_F(SerializationTest, AudioMixBinderization) {
+ for (int j = 0; j < 512; j++) {
+ const std::string msg{"Test AMBinderization for seed::" + std::to_string(mSeed)};
+ Vector<AudioMixMatchCriterion> criteria;
+ for (int i = 0; i < 16; i++) {
+ AudioMixMatchCriterion ammc{kUsages[rand() % kUsages.size()],
+ kInputSources[rand() % kInputSources.size()],
+ kMixMatchRules[rand() % kMixMatchRules.size()]};
+ criteria.add(ammc);
+ }
+ audio_config_t config{};
+ config.sample_rate = 48000;
+ config.channel_mask = AUDIO_CHANNEL_IN_MONO;
+ config.format = AUDIO_FORMAT_PCM_16_BIT;
+ config.offload_info = AUDIO_INFO_INITIALIZER;
+ config.frame_count = 4800;
+ AudioMix am{criteria,
+ static_cast<uint32_t>(rand()),
+ config,
+ static_cast<uint32_t>(rand()),
+ String8(msg.c_str()),
+ static_cast<uint32_t>(rand())};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, am.writeToParcel(&p)) << msg;
+
+ AudioMix amCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, amCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(amCopy.mMixType, am.mMixType) << msg;
+ EXPECT_EQ(amCopy.mFormat.sample_rate, am.mFormat.sample_rate) << msg;
+ EXPECT_EQ(amCopy.mFormat.channel_mask, am.mFormat.channel_mask) << msg;
+ EXPECT_EQ(amCopy.mFormat.format, am.mFormat.format) << msg;
+ EXPECT_EQ(amCopy.mRouteFlags, am.mRouteFlags) << msg;
+ EXPECT_EQ(amCopy.mDeviceAddress, am.mDeviceAddress) << msg;
+ EXPECT_EQ(amCopy.mCbFlags, am.mCbFlags) << msg;
+ EXPECT_EQ(amCopy.mCriteria.size(), am.mCriteria.size()) << msg;
+ for (auto i = 0; i < amCopy.mCriteria.size(); i++) {
+ EXPECT_EQ(amCopy.mCriteria[i].mRule, am.mCriteria[i].mRule) << msg;
+ EXPECT_EQ(amCopy.mCriteria[i].mValue.mUserId, am.mCriteria[i].mValue.mUserId) << msg;
+ }
+ }
+}
+
+using MMCTestParams = std::tuple<audio_usage_t, audio_source_t, uint32_t>;
+
+class MMCParameterizedTest : public FillAudioAttributes,
+ public ::testing::TestWithParam<MMCTestParams> {
+ public:
+ MMCParameterizedTest()
+ : mAudioUsage(std::get<0>(GetParam())),
+ mAudioSource(std::get<1>(GetParam())),
+ mAudioMixMatchRules(std::get<2>(GetParam())){};
+
+ const audio_usage_t mAudioUsage;
+ const audio_source_t mAudioSource;
+ const uint32_t mAudioMixMatchRules;
+
+ void SetUp() override {
+ mSeed = static_cast<unsigned int>(time(0));
+ srand(mSeed);
+ }
+};
+
+TEST_P(MMCParameterizedTest, AudioMixMatchCriterionBinderization) {
+ const std::string msg{"Test AMMCBinderization for seed::" + std::to_string(mSeed)};
+ AudioMixMatchCriterion ammc{mAudioUsage, mAudioSource, mAudioMixMatchRules};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, ammc.writeToParcel(&p)) << msg;
+
+ AudioMixMatchCriterion ammcCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, ammcCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(ammcCopy.mRule, ammc.mRule) << msg;
+ EXPECT_EQ(ammcCopy.mValue.mUserId, ammc.mValue.mUserId) << msg;
+}
+
+// audioUsage, audioSource, audioMixMatchRules
+INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, MMCParameterizedTest,
+ ::testing::Combine(testing::ValuesIn(kUsages),
+ testing::ValuesIn(kInputSources),
+ testing::ValuesIn(kMixMatchRules)));
+
+using AudioAttributesTestParams = std::tuple<audio_stream_type_t>;
+
+class AudioAttributesParameterizedTest
+ : public FillAudioAttributes,
+ public ::testing::TestWithParam<AudioAttributesTestParams> {
+ public:
+ AudioAttributesParameterizedTest() : mAudioStream(std::get<0>(GetParam())){};
+
+ const audio_stream_type_t mAudioStream;
+
+ void SetUp() override {
+ mSeed = static_cast<unsigned int>(time(0));
+ srand(mSeed);
+ }
+};
+
+TEST_P(AudioAttributesParameterizedTest, AudioAttributesBinderization) {
+ const std::string msg{"Test AABinderization for seed::" + std::to_string(mSeed)};
+ volume_group_t groupId = static_cast<volume_group_t>(rand());
+ audio_stream_type_t stream = mAudioStream;
+ audio_attributes_t attributes;
+ fillAudioAttributes(attributes);
+ AudioAttributes audioattributes{groupId, stream, attributes};
+
+ Parcel p;
+ EXPECT_EQ(NO_ERROR, audioattributes.writeToParcel(&p)) << msg;
+
+ AudioAttributes audioattributesCopy;
+ p.setDataPosition(0);
+ EXPECT_EQ(NO_ERROR, audioattributesCopy.readFromParcel(&p)) << msg;
+ EXPECT_EQ(audioattributesCopy.getGroupId(), audioattributes.getGroupId()) << msg;
+ EXPECT_EQ(audioattributesCopy.getStreamType(), audioattributes.getStreamType()) << msg;
+ EXPECT_TRUE(audioattributesCopy.getAttributes() == attributes) << msg;
+}
+
+// audioStream
+INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, AudioAttributesParameterizedTest,
+ ::testing::Combine(testing::ValuesIn(kStreamtypes)));
diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp
new file mode 100644
index 0000000..93fe306
--- /dev/null
+++ b/media/libaudioclient/tests/audioeffect_tests.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioEffectUnitTests"
+
+#include <gtest/gtest.h>
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_visualizer.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+static constexpr int kDefaultInputEffectPriority = -1;
+static constexpr int kDefaultOutputEffectPriority = 0;
+
+static const char* gPackageName = "AudioEffectTest";
+
+bool isEffectExistsOnAudioSession(const effect_uuid_t* type, int priority,
+ audio_session_t sessionId) {
+ std::string packageName{gPackageName};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ sp<AudioEffect> effect = new AudioEffect(attributionSource);
+ effect->set(type, nullptr /* uid */, priority, nullptr /* callback */, sessionId);
+ return effect->initCheck() == ALREADY_EXISTS;
+}
+
+bool isEffectDefaultOnRecord(const effect_uuid_t* type, const sp<AudioRecord>& audioRecord) {
+ effect_descriptor_t descriptors[AudioEffect::kMaxPreProcessing];
+ uint32_t numEffects = AudioEffect::kMaxPreProcessing;
+ status_t ret = AudioEffect::queryDefaultPreProcessing(audioRecord->getSessionId(), descriptors,
+ &numEffects);
+ if (ret != OK) {
+ return false;
+ }
+ for (int i = 0; i < numEffects; i++) {
+ if (memcmp(&descriptors[i].type, type, sizeof(effect_uuid_t)) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void listEffectsAvailable(std::vector<effect_descriptor_t>& descriptors) {
+ uint32_t numEffects = 0;
+ if (NO_ERROR == AudioEffect::queryNumberEffects(&numEffects)) {
+ for (auto i = 0; i < numEffects; i++) {
+ effect_descriptor_t des;
+ if (NO_ERROR == AudioEffect::queryEffect(i, &des)) descriptors.push_back(des);
+ }
+ }
+}
+
+bool isPreprocessing(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC);
+}
+
+bool isInsert(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT);
+}
+
+bool isAux(effect_descriptor_t& descriptor) {
+ return ((descriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY);
+}
+
+bool isFastCompatible(effect_descriptor_t& descriptor) {
+ return !(((descriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0) &&
+ ((descriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0));
+}
+
+// UNIT TESTS
+TEST(AudioEffectTest, getEffectDescriptor) {
+ effect_uuid_t randomType = {
+ 0x81781c08, 0x93dd, 0x11ec, 0xb909, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
+ effect_uuid_t randomUuid = {
+ 0x653730e1, 0x1be1, 0x438e, 0xa35a, {0xfc, 0x9b, 0xa1, 0x2a, 0x5e, 0xc9}};
+ effect_uuid_t empty = EFFECT_UUID_INITIALIZER;
+
+ effect_descriptor_t descriptor;
+ EXPECT_EQ(NAME_NOT_FOUND, AudioEffect::getEffectDescriptor(&randomUuid, &randomType,
+ EFFECT_FLAG_TYPE_MASK, &descriptor));
+
+ std::vector<effect_descriptor_t> descriptors;
+ listEffectsAvailable(descriptors);
+
+ for (auto i = 0; i < descriptors.size(); i++) {
+ EXPECT_EQ(NO_ERROR,
+ AudioEffect::getEffectDescriptor(&descriptors[i].uuid, &descriptors[i].type,
+ EFFECT_FLAG_TYPE_MASK, &descriptor));
+ EXPECT_EQ(0, memcmp(&descriptor, &descriptors[i], sizeof(effect_uuid_t)));
+ }
+ // negative tests
+ if (descriptors.size() > 0) {
+ EXPECT_EQ(BAD_VALUE,
+ AudioEffect::getEffectDescriptor(&descriptors[0].uuid, &descriptors[0].type,
+ EFFECT_FLAG_TYPE_MASK, nullptr));
+ }
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(nullptr, nullptr,
+ EFFECT_FLAG_TYPE_PRE_PROC, &descriptor));
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(&empty, &randomType,
+ EFFECT_FLAG_TYPE_MASK, nullptr));
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(nullptr, &randomType,
+ EFFECT_FLAG_TYPE_POST_PROC, &descriptor));
+ EXPECT_EQ(BAD_VALUE, AudioEffect::getEffectDescriptor(&randomUuid, nullptr,
+ EFFECT_FLAG_TYPE_INSERT, &descriptor));
+}
+
+TEST(AudioEffectTest, DISABLED_GetSetParameterForEffect) {
+ std::string packageName{gPackageName};
+ AttributionSourceState attributionSource;
+ attributionSource.packageName = packageName;
+ attributionSource.uid = VALUE_OR_FATAL(legacy2aidl_uid_t_int32_t(getuid()));
+ attributionSource.pid = VALUE_OR_FATAL(legacy2aidl_pid_t_int32_t(getpid()));
+ attributionSource.token = sp<BBinder>::make();
+ sp<AudioEffect> visualizer = new AudioEffect(attributionSource);
+ ASSERT_NE(visualizer, nullptr) << "effect not created";
+ visualizer->set(SL_IID_VISUALIZATION);
+ status_t status = visualizer->initCheck();
+ ASSERT_TRUE(status == NO_ERROR || status == ALREADY_EXISTS) << "Init check error";
+ ASSERT_EQ(NO_ERROR, visualizer->setEnabled(true)) << "visualizer not enabled";
+
+ uint32_t buf32[3][sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t* vis_none = (effect_param_t*)(buf32[0]);
+ effect_param_t* vis_rms = (effect_param_t*)(buf32[1]);
+ effect_param_t* vis_tmp = (effect_param_t*)(buf32[2]);
+
+ // Visualizer::setMeasurementMode()
+ vis_none->psize = sizeof(uint32_t);
+ vis_none->vsize = sizeof(uint32_t);
+ *(int32_t*)vis_none->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t*)vis_none->data + 1) = MEASUREMENT_MODE_NONE;
+ EXPECT_EQ(NO_ERROR, visualizer->setParameter(vis_none))
+ << "setMeasurementMode doesn't report success";
+
+ // Visualizer::getMeasurementMode()
+ vis_tmp->psize = sizeof(uint32_t);
+ vis_tmp->vsize = sizeof(uint32_t);
+ *(int32_t*)vis_tmp->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t*)vis_tmp->data + 1) = 23;
+ EXPECT_EQ(NO_ERROR, visualizer->getParameter(vis_tmp))
+ << "getMeasurementMode doesn't report success";
+ EXPECT_EQ(*((int32_t*)vis_tmp->data + 1), *((int32_t*)vis_none->data + 1))
+ << "target mode does not match set mode";
+
+ // Visualizer::setMeasurementModeDeferred()
+ vis_rms->psize = sizeof(uint32_t);
+ vis_rms->vsize = sizeof(uint32_t);
+ *(int32_t*)vis_rms->data = VISUALIZER_PARAM_MEASUREMENT_MODE;
+ *((int32_t*)vis_rms->data + 1) = MEASUREMENT_MODE_PEAK_RMS;
+ EXPECT_EQ(NO_ERROR, visualizer->setParameterDeferred(vis_rms))
+ << "setMeasurementModeDeferred doesn't report success";
+
+ *((int32_t*)vis_tmp->data + 1) = 23;
+ EXPECT_EQ(NO_ERROR, visualizer->getParameter(vis_tmp))
+ << "getMeasurementMode doesn't report success";
+ EXPECT_EQ(*((int32_t*)vis_tmp->data + 1), *((int32_t*)vis_none->data + 1))
+ << "target mode does not match set mode";
+
+ // setParameterCommit
+ EXPECT_EQ(NO_ERROR, visualizer->setParameterCommit())
+ << "setMeasurementModeCommit does not report success";
+
+ // validate Params
+ *((int32_t*)vis_tmp->data + 1) = 23;
+ EXPECT_EQ(NO_ERROR, visualizer->getParameter(vis_tmp))
+ << "getMeasurementMode doesn't report success";
+ EXPECT_EQ(*((int32_t*)vis_tmp->data + 1), *((int32_t*)vis_rms->data + 1))
+ << "target mode does not match set mode";
+}
+
+TEST(AudioEffectTest, ManageSourceDefaultEffects) {
+ int32_t selectedEffect = -1;
+
+ const uint32_t sampleRate = 44100;
+ const audio_format_t format = AUDIO_FORMAT_PCM_16_BIT;
+ const audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_STEREO;
+ sp<AudioCapture> capture = nullptr;
+
+ std::vector<effect_descriptor_t> descriptors;
+ listEffectsAvailable(descriptors);
+ for (auto i = 0; i < descriptors.size(); i++) {
+ if (isPreprocessing(descriptors[i])) {
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ if (!isEffectDefaultOnRecord(&descriptors[i].type, capture->getAudioRecordHandle())) {
+ selectedEffect = i;
+ break;
+ }
+ }
+ }
+ if (selectedEffect == -1) GTEST_SKIP() << " expected at least one preprocessing effect";
+ effect_uuid_t selectedEffectType = descriptors[selectedEffect].type;
+
+ char type[512];
+ AudioEffect::guidToString(&selectedEffectType, type, sizeof(type));
+
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ EXPECT_FALSE(isEffectDefaultOnRecord(&selectedEffectType, capture->getAudioRecordHandle()))
+ << "Effect should not have been default on record. " << type;
+ EXPECT_FALSE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
+ << "Effect should not have been added. " << type;
+ EXPECT_EQ(OK, capture->audioProcess());
+ EXPECT_EQ(OK, capture->stop());
+
+ String16 name{gPackageName};
+ audio_unique_id_t effectId;
+ status_t status = AudioEffect::addSourceDefaultEffect(
+ type, name, nullptr, kDefaultInputEffectPriority, AUDIO_SOURCE_MIC, &effectId);
+ EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << type;
+
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ EXPECT_TRUE(isEffectDefaultOnRecord(&selectedEffectType, capture->getAudioRecordHandle()))
+ << "Effect should have been default on record. " << type;
+ EXPECT_TRUE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
+ << "Effect should have been added. " << type;
+ EXPECT_EQ(OK, capture->audioProcess());
+ EXPECT_EQ(OK, capture->stop());
+
+ status = AudioEffect::removeSourceDefaultEffect(effectId);
+ EXPECT_EQ(NO_ERROR, status);
+ capture = new AudioCapture(AUDIO_SOURCE_MIC, sampleRate, format, channelMask);
+ ASSERT_NE(capture, nullptr) << "Unable to create Record Application";
+ EXPECT_EQ(NO_ERROR, capture->create());
+ EXPECT_EQ(NO_ERROR, capture->start());
+ EXPECT_FALSE(isEffectDefaultOnRecord(&selectedEffectType, capture->getAudioRecordHandle()))
+ << "Effect should not have been default on record. " << type;
+ EXPECT_FALSE(isEffectExistsOnAudioSession(&selectedEffectType, kDefaultInputEffectPriority - 1,
+ capture->getAudioRecordHandle()->getSessionId()))
+ << "Effect should not have been added. " << type;
+ EXPECT_EQ(OK, capture->audioProcess());
+ EXPECT_EQ(OK, capture->stop());
+}
+
+TEST(AudioEffectTest, ManageStreamDefaultEffects) {
+ int32_t selectedEffect = -1;
+
+ std::vector<effect_descriptor_t> descriptors;
+ listEffectsAvailable(descriptors);
+ for (auto i = 0; i < descriptors.size(); i++) {
+ if (isAux(descriptors[i])) {
+ selectedEffect = i;
+ break;
+ }
+ }
+ if (selectedEffect == -1) GTEST_SKIP() << " expected at least one Aux effect";
+ effect_uuid_t* selectedEffectType = &descriptors[selectedEffect].type;
+
+ char type[512];
+ AudioEffect::guidToString(selectedEffectType, type, sizeof(type));
+ // create track
+ audio_attributes_t attributes;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ auto playback = sp<AudioPlayback>::make(
+ 44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+ EXPECT_FALSE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
+ << "Effect should not have been added. " << type;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ playback->stop();
+ playback.clear();
+
+ String16 name{gPackageName};
+ audio_unique_id_t id;
+ status_t status = AudioEffect::addStreamDefaultEffect(
+ type, name, nullptr, kDefaultOutputEffectPriority, AUDIO_USAGE_MEDIA, &id);
+ EXPECT_EQ(NO_ERROR, status) << "Adding default effect failed: " << type;
+
+ playback = sp<AudioPlayback>::make(
+ 44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ float level = 0.2f, levelGot;
+ playback->getAudioTrackHandle()->setAuxEffectSendLevel(level);
+ EXPECT_EQ(NO_ERROR, playback->start());
+ EXPECT_TRUE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
+ << "Effect should have been added. " << type;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ playback->getAudioTrackHandle()->getAuxEffectSendLevel(&levelGot);
+ EXPECT_EQ(level, levelGot);
+ playback->stop();
+ playback.clear();
+
+ status = AudioEffect::removeStreamDefaultEffect(id);
+ EXPECT_EQ(NO_ERROR, status);
+ playback = sp<AudioPlayback>::make(
+ 44100 /*sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(NO_ERROR, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, playback->create());
+ EXPECT_EQ(NO_ERROR, playback->start());
+ EXPECT_FALSE(isEffectExistsOnAudioSession(selectedEffectType, kDefaultOutputEffectPriority - 1,
+ playback->getAudioTrackHandle()->getSessionId()))
+ << "Effect should not have been added. " << type;
+ EXPECT_EQ(NO_ERROR, playback->waitForConsumption());
+ playback->stop();
+ playback.clear();
+}
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
new file mode 100644
index 0000000..8c63a6d
--- /dev/null
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioRecordTest"
+
+#include <gtest/gtest.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+class AudioRecordTest : public ::testing::Test {
+ public:
+ virtual void SetUp() override {
+ mAC = new AudioCapture(AUDIO_SOURCE_DEFAULT, 44100, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_IN_FRONT);
+ ASSERT_NE(nullptr, mAC);
+ ASSERT_EQ(OK, mAC->create()) << "record creation failed";
+ }
+
+ virtual void TearDown() override {
+ if (mAC) ASSERT_EQ(OK, mAC->stop());
+ }
+
+ sp<AudioCapture> mAC;
+};
+
+class AudioRecordCreateTest
+ : public ::testing::TestWithParam<
+ std::tuple<uint32_t, audio_format_t, audio_channel_mask_t, audio_input_flags_t,
+ audio_session_t, audio_source_t>> {
+ public:
+ AudioRecordCreateTest()
+ : mSampleRate(std::get<0>(GetParam())),
+ mFormat(std::get<1>(GetParam())),
+ mChannelMask(std::get<2>(GetParam())),
+ mFlags(std::get<3>(GetParam())),
+ mSessionId(std::get<4>(GetParam())),
+ mInputSource(std::get<5>(GetParam())){};
+
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_input_flags_t mFlags;
+ const audio_session_t mSessionId;
+ const audio_source_t mInputSource;
+ const AudioRecord::transfer_type mTransferType = AudioRecord::TRANSFER_OBTAIN;
+
+ sp<AudioCapture> mAC;
+
+ virtual void SetUp() override {
+ mAC = new AudioCapture(mInputSource, mSampleRate, mFormat, mChannelMask, mFlags, mSessionId,
+ mTransferType);
+ ASSERT_NE(nullptr, mAC);
+ ASSERT_EQ(OK, mAC->create()) << "record creation failed";
+ }
+
+ virtual void TearDown() override {
+ if (mAC) ASSERT_EQ(OK, mAC->stop());
+ }
+};
+
+TEST_F(AudioRecordTest, TestSimpleRecord) {
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+}
+
+TEST_F(AudioRecordTest, TestAudioCbNotifier) {
+ EXPECT_EQ(BAD_VALUE, mAC->getAudioRecordHandle()->addAudioDeviceCallback(nullptr));
+ sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
+ sp<OnAudioDeviceUpdateNotifier> cbOld = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(INVALID_OPERATION, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->addAudioDeviceCallback(cb));
+ EXPECT_EQ(OK, mAC->start()) << "record creation failed";
+ EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
+ EXPECT_EQ(AUDIO_IO_HANDLE_NONE, cbOld->mAudioIo);
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, cbOld->mDeviceId);
+ EXPECT_NE(AUDIO_IO_HANDLE_NONE, cb->mAudioIo);
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, cb->mDeviceId);
+ EXPECT_EQ(BAD_VALUE, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(nullptr));
+ EXPECT_EQ(INVALID_OPERATION, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->removeAudioDeviceCallback(cb));
+ mAC->stop();
+}
+
+TEST_F(AudioRecordTest, TestEventRecordTrackPause) {
+ const auto playback = sp<AudioPlayback>::make(
+ 8000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_1ch_8kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, playback->create()) << "AudioTrack Creation failed";
+ audio_session_t audioTrackSession = playback->getAudioTrackHandle()->getSessionId();
+ EXPECT_EQ(OK, mAC->start(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE, audioTrackSession))
+ << "record creation failed";
+ EXPECT_EQ(OK, playback->start());
+ RawBuffer buffer;
+ status_t status = mAC->obtainBufferCb(buffer);
+ EXPECT_EQ(status, TIMED_OUT) << "Not expecting any callbacks until track sends Sync event";
+ playback->getAudioTrackHandle()->pause();
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ playback->stop();
+}
+
+TEST_F(AudioRecordTest, TestEventRecordTrackStop) {
+ const auto playback = sp<AudioPlayback>::make(
+ 8000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_1ch_8kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, playback->create()) << "AudioTrack Creation failed";
+ audio_session_t audioTrackSession = playback->getAudioTrackHandle()->getSessionId();
+ EXPECT_EQ(OK, mAC->start(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE, audioTrackSession))
+ << "record creation failed";
+ EXPECT_EQ(OK, playback->start());
+ RawBuffer buffer;
+ status_t status = mAC->obtainBufferCb(buffer);
+ EXPECT_EQ(status, TIMED_OUT) << "Not expecting any callbacks until track sends Sync event";
+ playback->stop();
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+}
+
+TEST_F(AudioRecordTest, TestGetSetMarker) {
+ mAC->mMarkerPosition = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setMarkerPosition(mAC->mMarkerPosition))
+ << "setMarkerPosition() failed";
+ uint32_t marker;
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getMarkerPosition(&marker))
+ << "getMarkerPosition() failed";
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ EXPECT_EQ(marker, mAC->mMarkerPosition)
+ << "configured marker and received marker are different";
+ EXPECT_EQ(mAC->mReceivedCbMarkerAtPosition, mAC->mMarkerPosition)
+ << "configured marker and received cb marker are different";
+}
+
+TEST_F(AudioRecordTest, TestGetSetMarkerPeriodical) {
+ mAC->mMarkerPeriod = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->mMarkerPeriod))
+ << "setPositionUpdatePeriod() failed";
+ uint32_t marker;
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPositionUpdatePeriod(&marker))
+ << "getPositionUpdatePeriod() failed";
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ EXPECT_EQ(marker, mAC->mMarkerPeriod) << "configured marker and received marker are different";
+ EXPECT_EQ(mAC->mReceivedCbMarkerCount, mAC->mNumFramesToRecord / mAC->mMarkerPeriod)
+ << "configured marker and received cb marker are different";
+}
+
+TEST_F(AudioRecordTest, TestGetPosition) {
+ uint32_t position;
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPosition(&position)) << "getPosition() failed";
+ EXPECT_EQ(0, position);
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+ EXPECT_EQ(OK, mAC->stop());
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPosition(&position)) << "getPosition() failed";
+}
+
+// TODO: Add checkPatchCapture(), verify the information of patch via dumpPort() and dumpPatch()
+TEST_P(AudioRecordCreateTest, TestCreateRecord) {
+ EXPECT_EQ(mFormat, mAC->getAudioRecordHandle()->format());
+ EXPECT_EQ(audio_channel_count_from_in_mask(mChannelMask),
+ mAC->getAudioRecordHandle()->channelCount());
+ if (mAC->mFrameCount != 0)
+ EXPECT_LE(mAC->mFrameCount, mAC->getAudioRecordHandle()->frameCount());
+ EXPECT_EQ(mInputSource, mAC->getAudioRecordHandle()->inputSource());
+ if (mSampleRate != 0) EXPECT_EQ(mSampleRate, mAC->getAudioRecordHandle()->getSampleRate());
+ if (mSessionId != AUDIO_SESSION_NONE)
+ EXPECT_EQ(mSessionId, mAC->getAudioRecordHandle()->getSessionId());
+ if (mTransferType != AudioRecord::TRANSFER_CALLBACK) {
+ uint32_t marker;
+ mAC->mMarkerPosition = (mAC->mNotificationFrames << 3) + (mAC->mNotificationFrames >> 1);
+ EXPECT_EQ(INVALID_OPERATION,
+ mAC->getAudioRecordHandle()->setMarkerPosition(mAC->mMarkerPosition));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getMarkerPosition(&marker));
+ EXPECT_EQ(INVALID_OPERATION,
+ mAC->getAudioRecordHandle()->setPositionUpdatePeriod(mAC->mMarkerPosition));
+ EXPECT_EQ(OK, mAC->getAudioRecordHandle()->getPositionUpdatePeriod(&marker));
+ }
+ EXPECT_EQ(OK, mAC->start()) << "start recording failed";
+ EXPECT_EQ(OK, mAC->audioProcess()) << "audioProcess failed";
+}
+
+// for port primary input
+INSTANTIATE_TEST_SUITE_P(AudioRecordPrimaryInput, AudioRecordCreateTest,
+ ::testing::Combine(::testing::Values(8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000),
+ ::testing::Values(AUDIO_FORMAT_PCM_8_24_BIT),
+ ::testing::Values(AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_FRONT_BACK),
+ ::testing::Values(AUDIO_INPUT_FLAG_NONE),
+ ::testing::Values(AUDIO_SESSION_NONE),
+ ::testing::Values(AUDIO_SOURCE_DEFAULT)));
+
+// for port fast input
+INSTANTIATE_TEST_SUITE_P(AudioRecordFastInput, AudioRecordCreateTest,
+ ::testing::Combine(::testing::Values(8000, 11025, 12000, 16000, 22050,
+ 24000, 32000, 44100, 48000),
+ ::testing::Values(AUDIO_FORMAT_PCM_8_24_BIT),
+ ::testing::Values(AUDIO_CHANNEL_IN_MONO,
+ AUDIO_CHANNEL_IN_STEREO,
+ AUDIO_CHANNEL_IN_FRONT_BACK),
+ ::testing::Values(AUDIO_INPUT_FLAG_FAST),
+ ::testing::Values(AUDIO_SESSION_NONE),
+ ::testing::Values(AUDIO_SOURCE_DEFAULT)));
+
+// misc
+INSTANTIATE_TEST_SUITE_P(AudioRecordMiscInput, AudioRecordCreateTest,
+ ::testing::Combine(::testing::Values(48000),
+ ::testing::Values(AUDIO_FORMAT_PCM_16_BIT),
+ ::testing::Values(AUDIO_CHANNEL_IN_MONO),
+ ::testing::Values(AUDIO_INPUT_FLAG_NONE),
+ ::testing::Values(AUDIO_SESSION_NONE),
+ ::testing::Values(AUDIO_SOURCE_MIC,
+ AUDIO_SOURCE_CAMCORDER,
+ AUDIO_SOURCE_VOICE_RECOGNITION,
+ AUDIO_SOURCE_VOICE_COMMUNICATION,
+ AUDIO_SOURCE_UNPROCESSED)));
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
new file mode 100644
index 0000000..445633b
--- /dev/null
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+// UNIT TEST
+TEST(AudioTrackTest, TestPerformanceMode) {
+ std::vector<std::string> attachedDevices;
+ std::vector<MixPort> mixPorts;
+ std::vector<Route> routes;
+ EXPECT_EQ(OK, parse_audio_policy_configuration_xml(attachedDevices, mixPorts, routes));
+ std::string output_flags_string[] = {"AUDIO_OUTPUT_FLAG_FAST", "AUDIO_OUTPUT_FLAG_DEEP_BUFFER"};
+ audio_output_flags_t output_flags[] = {AUDIO_OUTPUT_FLAG_FAST, AUDIO_OUTPUT_FLAG_DEEP_BUFFER};
+ audio_flags_mask_t flags[] = {AUDIO_FLAG_LOW_LATENCY, AUDIO_FLAG_DEEP_BUFFER};
+ bool hasFlag = false;
+ for (int i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+ hasFlag = false;
+ for (int j = 0; j < mixPorts.size() && !hasFlag; j++) {
+ MixPort port = mixPorts[j];
+ if (port.role == "source" && port.flags.find(output_flags_string[i]) != -1) {
+ for (int k = 0; k < routes.size() && !hasFlag; k++) {
+ if (routes[k].sources.find(port.name) != -1 &&
+ std::find(attachedDevices.begin(), attachedDevices.end(), routes[k].sink) !=
+ attachedDevices.end()) {
+ hasFlag = true;
+ std::cerr << "found port with flag " << output_flags_string[i] << "@ "
+ << " port :: name : " << port.name << " role : " << port.role
+ << " port :: flags : " << port.flags
+ << " connected via route name : " << routes[k].name
+ << " route sources : " << routes[k].sources
+ << " route sink : " << routes[k].sink << std::endl;
+ }
+ }
+ }
+ }
+ if (!hasFlag) continue;
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ attributes.flags = flags[i];
+ sp<AudioPlayback> ap = sp<AudioPlayback>::make(
+ 0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN,
+ &attributes);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cb));
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess());
+ EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
+ EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
+ EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
+ audio_patch patch;
+ EXPECT_EQ(OK, getPatchForOutputMix(cb->mAudioIo, patch));
+ for (auto j = 0; j < patch.num_sources; j++) {
+ if (patch.sources[j].type == AUDIO_PORT_TYPE_MIX &&
+ patch.sources[j].ext.mix.handle == cb->mAudioIo) {
+ if ((patch.sources[j].flags.output & output_flags[i]) == 0) {
+ ADD_FAILURE() << "expected output flag " << output_flags[i] << " is absent";
+ std::cerr << dumpPortConfig(patch.sources[j]);
+ }
+ }
+ }
+ ap->stop();
+ ap->getAudioTrackHandle()->removeAudioDeviceCallback(cb);
+ }
+}
+
+TEST(AudioTrackTest, TestRemoteSubmix) {
+ std::vector<std::string> attachedDevices;
+ std::vector<MixPort> mixPorts;
+ std::vector<Route> routes;
+ EXPECT_EQ(OK, parse_audio_policy_configuration_xml(attachedDevices, mixPorts, routes));
+ bool hasFlag = false;
+ for (int j = 0; j < attachedDevices.size() && !hasFlag; j++) {
+ if (attachedDevices[j].find("Remote Submix") != -1) hasFlag = true;
+ }
+ if (!hasFlag) GTEST_SKIP() << " Device does not have Remote Submix port.";
+ sp<AudioCapture> capture = new AudioCapture(AUDIO_SOURCE_REMOTE_SUBMIX, 48000,
+ AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO);
+ ASSERT_NE(nullptr, capture);
+ ASSERT_EQ(OK, capture->create()) << "record creation failed";
+
+ sp<AudioPlayback> playback = sp<AudioPlayback>::make(
+ 48000 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_NONE, AUDIO_SESSION_NONE);
+ ASSERT_NE(nullptr, playback);
+ ASSERT_EQ(OK, playback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ ASSERT_EQ(OK, playback->create()) << "track creation failed";
+
+ audio_port_v7 port;
+ status_t status = getPortByAttributes(AUDIO_PORT_ROLE_SOURCE, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, port);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ EXPECT_EQ(OK, capture->start()) << "start recording failed";
+ EXPECT_EQ(port.id, capture->getAudioRecordHandle()->getRoutedDeviceId())
+ << "Capture NOT routed on expected port";
+
+ status = getPortByAttributes(AUDIO_PORT_ROLE_SINK, AUDIO_PORT_TYPE_DEVICE,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, port);
+ EXPECT_EQ(OK, status) << "Could not find port";
+
+ EXPECT_EQ(OK, playback->start()) << "audio track start failed";
+ EXPECT_EQ(OK, playback->onProcess());
+ ASSERT_EQ(port.id, playback->getAudioTrackHandle()->getRoutedDeviceId())
+ << "Playback NOT routed on expected port";
+ capture->stop();
+ playback->stop();
+}
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
new file mode 100644
index 0000000..aed847c
--- /dev/null
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioSystemTest"
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+#include <media/IAudioFlinger.h>
+#include <utils/Log.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+void anyPatchContainsInputDevice(audio_port_handle_t deviceId, bool& res) {
+ std::vector<struct audio_patch> patches;
+ status_t status = listAudioPatches(patches);
+ ASSERT_EQ(OK, status);
+ res = false;
+ for (const auto& patch : patches) {
+ if (patchContainsInputDevice(deviceId, patch)) {
+ res = true;
+ return;
+ }
+ }
+}
+
+class AudioSystemTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ mAF = AudioSystem::get_audio_flinger();
+ ASSERT_NE(mAF, nullptr) << "Permission denied";
+ }
+
+ void TearDown() override {
+ if (mPlayback) {
+ mPlayback->stop();
+ mPlayback->getAudioTrackHandle()->removeAudioDeviceCallback(mCbPlayback);
+ mPlayback.clear();
+ }
+ if (mCapture) {
+ mCapture->stop();
+ mCapture->getAudioRecordHandle()->removeAudioDeviceCallback(mCbRecord);
+ mCapture.clear();
+ }
+ }
+
+ void createPlaybackSession(void);
+ void createRecordSession(void);
+
+ sp<IAudioFlinger> mAF;
+ sp<AudioPlayback> mPlayback;
+ sp<OnAudioDeviceUpdateNotifier> mCbPlayback;
+ sp<AudioCapture> mCapture;
+ sp<OnAudioDeviceUpdateNotifier> mCbRecord;
+};
+
+void AudioSystemTest::createPlaybackSession(void) {
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ mPlayback = sp<AudioPlayback>::make(48000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ AUDIO_OUTPUT_FLAG_FAST, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_SHARED, &attributes);
+ ASSERT_NE(nullptr, mPlayback);
+ ASSERT_EQ(NO_ERROR, mPlayback->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"));
+ EXPECT_EQ(NO_ERROR, mPlayback->create());
+ mCbPlayback = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, mPlayback->getAudioTrackHandle()->addAudioDeviceCallback(mCbPlayback));
+ EXPECT_EQ(NO_ERROR, mPlayback->start());
+ EXPECT_EQ(OK, mPlayback->onProcess());
+ EXPECT_EQ(OK, mCbPlayback->waitForAudioDeviceCb());
+}
+
+void AudioSystemTest::createRecordSession(void) {
+ mCapture = new AudioCapture(AUDIO_SOURCE_DEFAULT, 44100, AUDIO_FORMAT_PCM_8_24_BIT,
+ AUDIO_CHANNEL_IN_MONO, AUDIO_INPUT_FLAG_FAST);
+ ASSERT_NE(nullptr, mCapture);
+ ASSERT_EQ(OK, mCapture->create()) << "record creation failed";
+ mCbRecord = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, mCapture->getAudioRecordHandle()->addAudioDeviceCallback(mCbRecord));
+ EXPECT_EQ(OK, mCapture->start()) << "record creation failed";
+ EXPECT_EQ(OK, mCbRecord->waitForAudioDeviceCb());
+}
+
+// UNIT TESTS
+TEST_F(AudioSystemTest, CheckServerSideValues) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ EXPECT_GT(mAF->sampleRate(mCbPlayback->mAudioIo), 0);
+ EXPECT_NE(mAF->format(mCbPlayback->mAudioIo), AUDIO_FORMAT_INVALID);
+ EXPECT_GT(mAF->frameCount(mCbPlayback->mAudioIo), 0);
+ size_t frameCountHal, frameCountHalCache;
+ frameCountHal = mAF->frameCountHAL(mCbPlayback->mAudioIo);
+ EXPECT_GT(frameCountHal, 0);
+ EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(mCbPlayback->mAudioIo, &frameCountHalCache));
+ EXPECT_EQ(frameCountHal, frameCountHalCache);
+ EXPECT_GT(mAF->latency(mCbPlayback->mAudioIo), 0);
+ // client side latency is at least server side latency
+ EXPECT_LE(mAF->latency(mCbPlayback->mAudioIo), mPlayback->getAudioTrackHandle()->latency());
+
+ ASSERT_NO_FATAL_FAILURE(createRecordSession());
+ EXPECT_GT(mAF->sampleRate(mCbRecord->mAudioIo), 0);
+ // EXPECT_NE(mAF->format(mCbRecord->mAudioIo), AUDIO_FORMAT_INVALID);
+ EXPECT_GT(mAF->frameCount(mCbRecord->mAudioIo), 0);
+ EXPECT_GT(mAF->frameCountHAL(mCbRecord->mAudioIo), 0);
+ frameCountHal = mAF->frameCountHAL(mCbRecord->mAudioIo);
+ EXPECT_GT(frameCountHal, 0);
+ EXPECT_EQ(OK, AudioSystem::getFrameCountHAL(mCbRecord->mAudioIo, &frameCountHalCache));
+ EXPECT_EQ(frameCountHal, frameCountHalCache);
+ // EXPECT_GT(mAF->latency(mCbRecord->mAudioIo), 0);
+ // client side latency is at least server side latency
+ // EXPECT_LE(mAF->latency(mCbRecord->mAudioIo), mCapture->getAudioRecordHandle()->latency());
+
+ EXPECT_GT(AudioSystem::getPrimaryOutputSamplingRate(), 0); // first fast mixer sample rate
+ EXPECT_GT(AudioSystem::getPrimaryOutputFrameCount(), 0); // fast mixer frame count
+}
+
+TEST_F(AudioSystemTest, GetSetMasterVolume) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ float origVol, tstVol;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterVolume(&origVol));
+ float newVol;
+ if (origVol + 0.2f > 1.0f) {
+ newVol = origVol - 0.2f;
+ } else {
+ newVol = origVol + 0.2f;
+ }
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterVolume(newVol));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterVolume(&tstVol));
+ EXPECT_EQ(newVol, tstVol);
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterVolume(origVol));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterVolume(&tstVol));
+ EXPECT_EQ(origVol, tstVol);
+}
+
+TEST_F(AudioSystemTest, GetSetMasterMute) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ bool origMuteState, tstMuteState;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterMute(&origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterMute(!origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterMute(&tstMuteState));
+ EXPECT_EQ(!origMuteState, tstMuteState);
+ EXPECT_EQ(NO_ERROR, AudioSystem::setMasterMute(origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getMasterMute(&tstMuteState));
+ EXPECT_EQ(origMuteState, tstMuteState);
+}
+
+TEST_F(AudioSystemTest, GetSetMicMute) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ bool origMuteState, tstMuteState;
+ EXPECT_EQ(NO_ERROR, AudioSystem::isMicrophoneMuted(&origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::muteMicrophone(!origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::isMicrophoneMuted(&tstMuteState));
+ EXPECT_EQ(!origMuteState, tstMuteState);
+ EXPECT_EQ(NO_ERROR, AudioSystem::muteMicrophone(origMuteState));
+ EXPECT_EQ(NO_ERROR, AudioSystem::isMicrophoneMuted(&tstMuteState));
+ EXPECT_EQ(origMuteState, tstMuteState);
+}
+
+TEST_F(AudioSystemTest, GetSetMasterBalance) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ float origBalance, tstBalance;
+ EXPECT_EQ(OK, AudioSystem::getMasterBalance(&origBalance));
+ float newBalance;
+ if (origBalance + 0.2f > 1.0f) {
+ newBalance = origBalance - 0.2f;
+ } else {
+ newBalance = origBalance + 0.2f;
+ }
+ EXPECT_EQ(OK, AudioSystem::setMasterBalance(newBalance));
+ EXPECT_EQ(OK, AudioSystem::getMasterBalance(&tstBalance));
+ EXPECT_EQ(newBalance, tstBalance);
+ EXPECT_EQ(OK, AudioSystem::setMasterBalance(origBalance));
+ EXPECT_EQ(OK, AudioSystem::getMasterBalance(&tstBalance));
+ EXPECT_EQ(origBalance, tstBalance);
+}
+
+TEST_F(AudioSystemTest, GetStreamVolume) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ float origStreamVol;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getStreamVolume(AUDIO_STREAM_MUSIC, &origStreamVol,
+ mCbPlayback->mAudioIo));
+}
+
+TEST_F(AudioSystemTest, GetStreamMute) {
+ ASSERT_NO_FATAL_FAILURE(createPlaybackSession());
+ bool origMuteState;
+ EXPECT_EQ(NO_ERROR, AudioSystem::getStreamMute(AUDIO_STREAM_MUSIC, &origMuteState));
+}
+
+TEST_F(AudioSystemTest, StartAndStopAudioSource) {
+ std::vector<struct audio_port_v7> ports;
+ audio_port_config sourcePortConfig;
+ audio_attributes_t attributes = AudioSystem::streamTypeToAttributes(AUDIO_STREAM_MUSIC);
+ audio_port_handle_t sourcePortHandle = AUDIO_PORT_HANDLE_NONE;
+
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+ if (ports.empty()) {
+ GTEST_SKIP() << "No ports returned by the audio system";
+ }
+
+ for (const auto& port : ports) {
+ if (port.role != AUDIO_PORT_ROLE_SOURCE || port.type != AUDIO_PORT_TYPE_DEVICE) continue;
+ sourcePortConfig = port.active_config;
+
+ bool patchFound;
+
+ // start audio source.
+ status_t ret =
+ AudioSystem::startAudioSource(&sourcePortConfig, &attributes, &sourcePortHandle);
+ EXPECT_EQ(OK, ret) << "AudioSystem::startAudioSource for source " << port.ext.device.address
+ << " failed";
+
+ // verify that patch is established by the source port.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(port.id, patchFound));
+ EXPECT_EQ(true, patchFound);
+ EXPECT_NE(sourcePortHandle, AUDIO_PORT_HANDLE_NONE);
+
+ if (sourcePortHandle != AUDIO_PORT_HANDLE_NONE) {
+ ret = AudioSystem::stopAudioSource(sourcePortHandle);
+ EXPECT_EQ(OK, ret) << "AudioSystem::stopAudioSource for handle failed";
+ }
+
+ // verify that no source port patch exists.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(port.id, patchFound));
+ EXPECT_EQ(false, patchFound);
+ }
+}
+
+TEST_F(AudioSystemTest, CreateAndReleaseAudioPatch) {
+ status_t status;
+ struct audio_patch audioPatch;
+ std::vector<struct audio_port_v7> ports;
+ audio_patch_handle_t audioPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+
+ bool patchFound = false;
+ audio_port_v7 sourcePort{};
+ audio_port_v7 sinkPort{};
+
+ audioPatch.id = 0;
+ audioPatch.num_sources = 1;
+ audioPatch.num_sinks = 1;
+
+ status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+ if (ports.empty()) {
+ GTEST_SKIP() << "No output devices returned by the audio system";
+ }
+
+ for (const auto& port : ports) {
+ if (port.role == AUDIO_PORT_ROLE_SOURCE && port.type == AUDIO_PORT_TYPE_DEVICE) {
+ sourcePort = port;
+ }
+ if (port.role == AUDIO_PORT_ROLE_SINK && port.type == AUDIO_PORT_TYPE_DEVICE &&
+ port.ext.device.type == AUDIO_DEVICE_OUT_SPEAKER) {
+ sinkPort = port;
+ }
+ }
+
+ audioPatch.sources[0] = sourcePort.active_config;
+ audioPatch.sinks[0] = sinkPort.active_config;
+
+ status = AudioSystem::createAudioPatch(&audioPatch, &audioPatchHandle);
+ EXPECT_EQ(OK, status) << "AudioSystem::createAudiopatch failed between source "
+ << sourcePort.ext.device.address << " and sink "
+ << sinkPort.ext.device.address;
+
+ // verify that patch is established between source and the sink.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(sourcePort.id, patchFound));
+ EXPECT_EQ(true, patchFound);
+
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, audioPatchHandle);
+ status = AudioSystem::releaseAudioPatch(audioPatchHandle);
+ EXPECT_EQ(OK, status) << "AudioSystem::releaseAudioPatch failed between source "
+ << sourcePort.ext.device.address << " and sink "
+ << sinkPort.ext.device.address;
+
+ // verify that no patch is established between source and the sink after releaseAudioPatch.
+ ASSERT_NO_FATAL_FAILURE(anyPatchContainsInputDevice(sourcePort.id, patchFound));
+ EXPECT_EQ(false, patchFound);
+}
+
+TEST_F(AudioSystemTest, GetAudioPort) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+ for (const auto& port : ports) {
+ audio_port_v7 portTest{.id = port.id};
+ EXPECT_EQ(OK, AudioSystem::getAudioPort(&portTest));
+ EXPECT_TRUE(audio_ports_v7_are_equal(&portTest, &port));
+ }
+}
+
+TEST_F(AudioSystemTest, TestPhoneState) {
+ uid_t uid = getuid();
+ EXPECT_EQ(OK, AudioSystem::setPhoneState(AUDIO_MODE_RINGTONE, uid));
+ audio_mode_t state = AudioSystem::getPhoneState();
+ EXPECT_EQ(AUDIO_MODE_RINGTONE, state);
+ EXPECT_EQ(OK, AudioSystem::setPhoneState(AUDIO_MODE_IN_COMMUNICATION, uid));
+ state = AudioSystem::getPhoneState();
+ EXPECT_EQ(AUDIO_MODE_IN_COMMUNICATION, state);
+ EXPECT_EQ(OK, AudioSystem::setPhoneState(AUDIO_MODE_NORMAL, uid));
+ state = AudioSystem::getPhoneState();
+ EXPECT_EQ(AUDIO_MODE_NORMAL, state);
+}
+
+TEST_F(AudioSystemTest, GetDirectProfilesForAttributes) {
+ std::vector<audio_profile> audioProfiles;
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDirectProfilesForAttributes(nullptr, nullptr));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDirectProfilesForAttributes(nullptr, &audioProfiles));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDirectProfilesForAttributes(&attributes, nullptr));
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDirectProfilesForAttributes(&attributes, &audioProfiles));
+}
+
+bool isPublicStrategy(const AudioProductStrategy& strategy) {
+ bool result = true;
+ for (auto& attribute : strategy.getAudioAttributes()) {
+ if (attribute.getAttributes() == AUDIO_ATTRIBUTES_INITIALIZER &&
+ (uint32_t(attribute.getStreamType()) >= AUDIO_STREAM_PUBLIC_CNT)) {
+ result = false;
+ break;
+ }
+ }
+ return result;
+}
+
+TEST_F(AudioSystemTest, DevicesForRoleAndStrategy) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+
+ std::vector<struct audio_port_v7> devicePorts;
+ for (const auto& port : ports) {
+ if (port.type == AUDIO_PORT_TYPE_DEVICE && audio_is_output_device(port.ext.device.type)) {
+ devicePorts.push_back(port);
+ }
+ }
+ if (devicePorts.empty()) {
+ GTEST_SKIP() << "No output devices returned by the audio system";
+ }
+
+ AudioProductStrategyVector strategies;
+ EXPECT_EQ(OK, AudioSystem::listAudioProductStrategies(strategies));
+ if (strategies.empty()) {
+ GTEST_SKIP() << "No strategies returned by the audio system";
+ }
+
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+
+ bool hasStrategyForMedia = false;
+ AudioProductStrategy mediaStrategy;
+ for (const auto& strategy : strategies) {
+ if (!isPublicStrategy(strategy)) continue;
+
+ for (const auto& att : strategy.getAudioAttributes()) {
+ if (strategy.attributesMatches(att.getAttributes(), attributes)) {
+ hasStrategyForMedia = true;
+ mediaStrategy = strategy;
+ break;
+ }
+ }
+ }
+
+ if (!hasStrategyForMedia) {
+ GTEST_SKIP() << "No strategies returned for music media";
+ }
+
+ AudioDeviceTypeAddrVector devices;
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDevicesForRoleAndStrategy(PRODUCT_STRATEGY_NONE,
+ DEVICE_ROLE_PREFERRED, devices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_NONE, devices));
+ status = AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(), DEVICE_ROLE_PREFERRED,
+ devices);
+ if (status == NAME_NOT_FOUND) {
+ AudioDeviceTypeAddrVector outputDevices;
+ for (const auto& port : devicePorts) {
+ if (port.ext.device.type == AUDIO_DEVICE_OUT_SPEAKER) {
+ const AudioDeviceTypeAddr outputDevice(port.ext.device.type,
+ port.ext.device.address);
+ outputDevices.push_back(outputDevice);
+ }
+ }
+ EXPECT_EQ(OK, AudioSystem::setDevicesRoleForStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED, outputDevices));
+ EXPECT_EQ(OK, AudioSystem::getDevicesForRoleAndStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED, devices));
+ EXPECT_EQ(devices, outputDevices);
+ EXPECT_EQ(OK, AudioSystem::removeDevicesRoleForStrategy(mediaStrategy.getId(),
+ DEVICE_ROLE_PREFERRED));
+ EXPECT_EQ(NAME_NOT_FOUND, AudioSystem::getDevicesForRoleAndStrategy(
+ mediaStrategy.getId(), DEVICE_ROLE_PREFERRED, devices));
+ }
+}
+
+TEST_F(AudioSystemTest, VolumeIndexForAttributes) {
+ AudioVolumeGroupVector groups;
+ EXPECT_EQ(OK, AudioSystem::listAudioVolumeGroups(groups));
+ for (const auto& group : groups) {
+ if (group.getAudioAttributes().empty()) continue;
+ const audio_attributes_t attr = group.getAudioAttributes()[0];
+ if (attr == AUDIO_ATTRIBUTES_INITIALIZER) continue;
+ audio_stream_type_t streamType = AudioSystem::attributesToStreamType(attr);
+ if (streamType >= AUDIO_STREAM_PUBLIC_CNT) continue;
+
+ volume_group_t vg;
+ EXPECT_EQ(OK, AudioSystem::getVolumeGroupFromAudioAttributes(attr, vg));
+ EXPECT_EQ(group.getId(), vg);
+
+ int index;
+ EXPECT_EQ(OK,
+ AudioSystem::getVolumeIndexForAttributes(attr, index, AUDIO_DEVICE_OUT_SPEAKER));
+
+ int indexTest;
+ EXPECT_EQ(OK, AudioSystem::getStreamVolumeIndex(streamType, &indexTest,
+ AUDIO_DEVICE_OUT_SPEAKER));
+ EXPECT_EQ(index, indexTest);
+ }
+}
+
+TEST_F(AudioSystemTest, DevicesRoleForCapturePreset) {
+ std::vector<struct audio_port_v7> ports;
+ status_t status = listAudioPorts(ports);
+ ASSERT_EQ(OK, status);
+
+ if (ports.empty()) {
+ GTEST_SKIP() << "No ports returned by the audio system";
+ }
+
+ audio_devices_t inDeviceA = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ audio_devices_t inDeviceB = AUDIO_DEVICE_IN_BUILTIN_MIC;
+ for (const auto& port : ports) {
+ if (port.role != AUDIO_PORT_ROLE_SOURCE || port.type != AUDIO_PORT_TYPE_DEVICE) continue;
+ if (port.ext.device.type == inDeviceA) continue;
+ inDeviceB = port.ext.device.type;
+ break;
+ }
+ const audio_source_t audioSource = AUDIO_SOURCE_MIC;
+ const device_role_t role = DEVICE_ROLE_PREFERRED;
+ const AudioDeviceTypeAddr inputDevice(inDeviceA, "");
+ const AudioDeviceTypeAddrVector inputDevices = {inputDevice};
+ const AudioDeviceTypeAddr outputDevice(AUDIO_DEVICE_OUT_SPEAKER, "");
+ const AudioDeviceTypeAddrVector outputDevices = {outputDevice};
+
+ // Test invalid device when setting
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, outputDevices));
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::addDevicesRoleForCapturePreset(audioSource, role, outputDevices));
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, outputDevices));
+
+ // Test invalid role
+ AudioDeviceTypeAddrVector devices;
+ EXPECT_EQ(BAD_VALUE, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource,
+ DEVICE_ROLE_NONE, devices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::setDevicesRoleForCapturePreset(audioSource, DEVICE_ROLE_NONE,
+ inputDevices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::addDevicesRoleForCapturePreset(audioSource, DEVICE_ROLE_NONE,
+ inputDevices));
+ EXPECT_EQ(BAD_VALUE, AudioSystem::removeDevicesRoleForCapturePreset(
+ audioSource, DEVICE_ROLE_NONE, inputDevices));
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::clearDevicesRoleForCapturePreset(audioSource, DEVICE_ROLE_NONE));
+
+ // Without setting, call get/remove/clear must fail
+ EXPECT_EQ(NAME_NOT_FOUND,
+ AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_TRUE(devices.empty());
+ EXPECT_EQ(NAME_NOT_FOUND,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(NAME_NOT_FOUND, AudioSystem::clearDevicesRoleForCapturePreset(audioSource, role));
+
+ // Test set/get devices role
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+ ASSERT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(devices, inputDevices);
+
+ // Test setting will change the previously set devices
+ const AudioDeviceTypeAddr inputDevice2 = AudioDeviceTypeAddr(inDeviceB, "");
+ AudioDeviceTypeAddrVector inputDevices2 = {inputDevice2};
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, inputDevices2));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(devices, inputDevices2);
+
+ // Test add devices
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::addDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(2, devices.size());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice) != devices.end());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice2) != devices.end());
+
+ // Test remove devices
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(devices, inputDevices2);
+
+ // Test remove devices that are not set as the device role
+ EXPECT_EQ(BAD_VALUE,
+ AudioSystem::removeDevicesRoleForCapturePreset(audioSource, role, inputDevices));
+
+ // Test clear devices
+ EXPECT_EQ(NO_ERROR, AudioSystem::clearDevicesRoleForCapturePreset(audioSource, role));
+ devices.clear();
+ EXPECT_EQ(NAME_NOT_FOUND,
+ AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+
+ AudioDeviceTypeAddrVector inputDevices3 = {inputDevice, inputDevice2};
+ EXPECT_EQ(NO_ERROR,
+ AudioSystem::setDevicesRoleForCapturePreset(audioSource, role, inputDevices3));
+ devices.clear();
+ EXPECT_EQ(NO_ERROR, AudioSystem::getDevicesForRoleAndCapturePreset(audioSource, role, devices));
+ EXPECT_EQ(2, devices.size());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice) != devices.end());
+ EXPECT_TRUE(std::find(devices.begin(), devices.end(), inputDevice2) != devices.end());
+ EXPECT_EQ(NO_ERROR, AudioSystem::clearDevicesRoleForCapturePreset(audioSource, role));
+}
+
+TEST_F(AudioSystemTest, UidDeviceAffinities) {
+ uid_t uid = getuid();
+
+ // Test invalid device for example audio_is_input_device
+ AudioDeviceTypeAddr inputDevice(AUDIO_DEVICE_IN_BUILTIN_MIC, "");
+ AudioDeviceTypeAddrVector inputDevices = {inputDevice};
+ EXPECT_EQ(BAD_VALUE, AudioSystem::setUidDeviceAffinities(uid, inputDevices));
+
+ // Test valid device for example audio_is_output_device
+ AudioDeviceTypeAddr outputDevice(AUDIO_DEVICE_OUT_SPEAKER, "");
+ AudioDeviceTypeAddrVector outputDevices = {outputDevice};
+ EXPECT_EQ(NO_ERROR, AudioSystem::setUidDeviceAffinities(uid, outputDevices));
+ EXPECT_EQ(NO_ERROR, AudioSystem::removeUidDeviceAffinities(uid));
+}
+
+TEST_F(AudioSystemTest, UserIdDeviceAffinities) {
+ int userId = 200;
+
+ // Test invalid device for example audio_is_input_device
+ AudioDeviceTypeAddr inputDevice(AUDIO_DEVICE_IN_BUILTIN_MIC, "");
+ AudioDeviceTypeAddrVector inputDevices = {inputDevice};
+ EXPECT_EQ(BAD_VALUE, AudioSystem::setUserIdDeviceAffinities(userId, inputDevices));
+
+ // Test valid device for ezample audio_is_output_device
+ AudioDeviceTypeAddr outputDevice(AUDIO_DEVICE_OUT_SPEAKER, "");
+ AudioDeviceTypeAddrVector outputDevices = {outputDevice};
+ EXPECT_EQ(NO_ERROR, AudioSystem::setUserIdDeviceAffinities(userId, outputDevices));
+ EXPECT_EQ(NO_ERROR, AudioSystem::removeUserIdDeviceAffinities(userId));
+}
diff --git a/media/libaudioclient/tests/audiotrack_tests.cpp b/media/libaudioclient/tests/audiotrack_tests.cpp
new file mode 100644
index 0000000..8daba0a
--- /dev/null
+++ b/media/libaudioclient/tests/audiotrack_tests.cpp
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+
+#include <gtest/gtest.h>
+
+#include "audio_test_utils.h"
+
+using namespace android;
+
+TEST(AudioTrackTest, TestPlayTrack) {
+ const auto ap = sp<AudioPlayback>::make(44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_STEREO, AUDIO_OUTPUT_FLAG_NONE,
+ AUDIO_SESSION_NONE, AudioTrack::TRANSFER_OBTAIN);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess());
+ ap->stop();
+}
+
+TEST(AudioTrackTest, TestSeek) {
+ const auto ap = sp<AudioPlayback>::make(
+ 44100 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess(true));
+ ap->stop();
+}
+
+TEST(AudioTrackTest, OffloadOrDirectPlayback) {
+ audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
+ info.sample_rate = 44100;
+ info.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+ info.format = AUDIO_FORMAT_MP3;
+ info.stream_type = AUDIO_STREAM_MUSIC;
+ info.bit_rate = 192;
+ info.duration_us = 120 * 1000000; // 120 sec
+
+ audio_config_base_t config = {/* .sample_rate = */ info.sample_rate,
+ /* .channel_mask = */ info.channel_mask,
+ /* .format = */ AUDIO_FORMAT_PCM_16_BIT};
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
+ attributes.usage = AUDIO_USAGE_MEDIA;
+ attributes.flags = AUDIO_FLAG_NONE;
+
+ if (!AudioTrack::isDirectOutputSupported(config, attributes) &&
+ AUDIO_OFFLOAD_NOT_SUPPORTED == AudioSystem::getOffloadSupport(info)) {
+ GTEST_SKIP() << "offload or direct playback is not supported";
+ }
+ sp<AudioPlayback> ap = nullptr;
+ if (AUDIO_OFFLOAD_NOT_SUPPORTED != AudioSystem::getOffloadSupport(info)) {
+ ap = sp<AudioPlayback>::make(info.sample_rate, info.format, info.channel_mask,
+ AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_OBTAIN, nullptr, &info);
+ } else {
+ ap = sp<AudioPlayback>::make(config.sample_rate, config.format, config.channel_mask,
+ AUDIO_OUTPUT_FLAG_DIRECT, AUDIO_SESSION_NONE,
+ AudioTrack::TRANSFER_OBTAIN);
+ }
+ ASSERT_NE(nullptr, ap);
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ audio_dual_mono_mode_t mode;
+ if (OK != ap->getAudioTrackHandle()->getDualMonoMode(&mode)) {
+ std::cerr << "no dual mono presentation is available" << std::endl;
+ }
+ if (OK != ap->getAudioTrackHandle()->setDualMonoMode(AUDIO_DUAL_MONO_MODE_LR)) {
+ std::cerr << "no dual mono presentation is available" << std::endl;
+ } else {
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->getDualMonoMode(&mode));
+ EXPECT_EQ(AUDIO_DUAL_MONO_MODE_LR, mode);
+ }
+ float leveldB;
+ if (OK != ap->getAudioTrackHandle()->getAudioDescriptionMixLevel(&leveldB)) {
+ std::cerr << "Audio Description mixing is unavailable" << std::endl;
+ }
+ if (OK != ap->getAudioTrackHandle()->setAudioDescriptionMixLevel(3.14f)) {
+ std::cerr << "Audio Description mixing is unavailable" << std::endl;
+ } else {
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->getAudioDescriptionMixLevel(&leveldB));
+ EXPECT_EQ(3.14f, leveldB);
+ }
+ AudioPlaybackRate audioRate;
+ audioRate = ap->getAudioTrackHandle()->getPlaybackRate();
+ std::cerr << "playback speed :: " << audioRate.mSpeed << std::endl
+ << "playback pitch :: " << audioRate.mPitch << std::endl;
+ audioRate.mSpeed = 2.0f;
+ audioRate.mPitch = 2.0f;
+ audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_VOICE;
+ audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_MUTE;
+ EXPECT_TRUE(isAudioPlaybackRateValid(audioRate));
+ if (OK != ap->getAudioTrackHandle()->setPlaybackRate(audioRate)) {
+ std::cerr << "unable to set playback rate parameters" << std::endl;
+ } else {
+ AudioPlaybackRate audioRateLocal;
+ audioRateLocal = ap->getAudioTrackHandle()->getPlaybackRate();
+ EXPECT_TRUE(isAudioPlaybackRateEqual(audioRate, audioRateLocal));
+ }
+ ap->stop();
+}
+
+TEST(AudioTrackTest, TestAudioCbNotifier) {
+ const auto ap = sp<AudioPlayback>::make(0 /* sampleRate */, AUDIO_FORMAT_PCM_16_BIT,
+ AUDIO_CHANNEL_OUT_STEREO, AUDIO_OUTPUT_FLAG_FAST,
+ AUDIO_SESSION_NONE, AudioTrack::TRANSFER_SHARED);
+ ASSERT_NE(nullptr, ap);
+ ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ EXPECT_EQ(OK, ap->create()) << "track creation failed";
+ EXPECT_EQ(BAD_VALUE, ap->getAudioTrackHandle()->addAudioDeviceCallback(nullptr));
+ sp<OnAudioDeviceUpdateNotifier> cb = sp<OnAudioDeviceUpdateNotifier>::make();
+ sp<OnAudioDeviceUpdateNotifier> cbOld = sp<OnAudioDeviceUpdateNotifier>::make();
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(INVALID_OPERATION, ap->getAudioTrackHandle()->addAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->addAudioDeviceCallback(cb));
+ EXPECT_EQ(OK, ap->start()) << "audio track start failed";
+ EXPECT_EQ(OK, ap->onProcess());
+ EXPECT_EQ(OK, cb->waitForAudioDeviceCb());
+ EXPECT_EQ(AUDIO_IO_HANDLE_NONE, cbOld->mAudioIo);
+ EXPECT_EQ(AUDIO_PORT_HANDLE_NONE, cbOld->mDeviceId);
+ EXPECT_NE(AUDIO_IO_HANDLE_NONE, cb->mAudioIo);
+ EXPECT_NE(AUDIO_PORT_HANDLE_NONE, cb->mDeviceId);
+ EXPECT_EQ(cb->mAudioIo, ap->getAudioTrackHandle()->getOutput());
+ EXPECT_EQ(cb->mDeviceId, ap->getAudioTrackHandle()->getRoutedDeviceId());
+ String8 keys;
+ keys = ap->getAudioTrackHandle()->getParameters(keys);
+ if (!keys.isEmpty()) {
+ std::cerr << "track parameters :: " << keys << std::endl;
+ }
+ EXPECT_TRUE(checkPatchPlayback(cb->mAudioIo, cb->mDeviceId));
+ EXPECT_EQ(BAD_VALUE, ap->getAudioTrackHandle()->removeAudioDeviceCallback(nullptr));
+ EXPECT_EQ(INVALID_OPERATION, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cbOld));
+ EXPECT_EQ(OK, ap->getAudioTrackHandle()->removeAudioDeviceCallback(cb));
+ ap->stop();
+}
+
+class AudioTrackCreateTest
+ : public ::testing::TestWithParam<std::tuple<uint32_t, audio_format_t, audio_channel_mask_t,
+ audio_output_flags_t, audio_session_t>> {
+ public:
+ AudioTrackCreateTest()
+ : mSampleRate(std::get<0>(GetParam())),
+ mFormat(std::get<1>(GetParam())),
+ mChannelMask(std::get<2>(GetParam())),
+ mFlags(std::get<3>(GetParam())),
+ mSessionId(std::get<4>(GetParam())){};
+
+ const uint32_t mSampleRate;
+ const audio_format_t mFormat;
+ const audio_channel_mask_t mChannelMask;
+ const audio_output_flags_t mFlags;
+ const audio_session_t mSessionId;
+
+ sp<AudioPlayback> mAP;
+
+ virtual void SetUp() override {
+ mAP = sp<AudioPlayback>::make(mSampleRate, mFormat, mChannelMask, mFlags,
+ mSessionId);
+ ASSERT_NE(nullptr, mAP);
+ ASSERT_EQ(OK, mAP->loadResource("/data/local/tmp/bbb_2ch_24kHz_s16le.raw"))
+ << "Unable to open Resource";
+ ASSERT_EQ(OK, mAP->create()) << "track creation failed";
+ }
+
+ virtual void TearDown() override {
+ if (mAP) mAP->stop();
+ }
+};
+
+TEST_P(AudioTrackCreateTest, TestCreateTrack) {
+ EXPECT_EQ(mFormat, mAP->getAudioTrackHandle()->format());
+ EXPECT_EQ(audio_channel_count_from_out_mask(mChannelMask),
+ mAP->getAudioTrackHandle()->channelCount());
+ if (mSampleRate != 0) EXPECT_EQ(mSampleRate, mAP->getAudioTrackHandle()->getSampleRate());
+ if (mSessionId != AUDIO_SESSION_NONE)
+ EXPECT_EQ(mSessionId, mAP->getAudioTrackHandle()->getSessionId());
+ EXPECT_EQ(mSampleRate, mAP->getAudioTrackHandle()->getOriginalSampleRate());
+ EXPECT_EQ(OK, mAP->start()) << "audio track start failed";
+ EXPECT_EQ(OK, mAP->onProcess());
+}
+
+// sampleRate, format, channelMask, flags, sessionId
+INSTANTIATE_TEST_SUITE_P(
+ AudioTrackParameterizedTest, AudioTrackCreateTest,
+ ::testing::Combine(::testing::Values(48000), ::testing::Values(AUDIO_FORMAT_PCM_16_BIT),
+ ::testing::Values(AUDIO_CHANNEL_OUT_STEREO),
+ ::testing::Values(AUDIO_OUTPUT_FLAG_NONE,
+ AUDIO_OUTPUT_FLAG_PRIMARY | AUDIO_OUTPUT_FLAG_FAST,
+ AUDIO_OUTPUT_FLAG_RAW | AUDIO_OUTPUT_FLAG_FAST,
+ AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
+ ::testing::Values(AUDIO_SESSION_NONE)));
diff --git a/media/libaudioclient/tests/bbb_1ch_8kHz_s16le.raw b/media/libaudioclient/tests/bbb_1ch_8kHz_s16le.raw
new file mode 100644
index 0000000..2d1e4bf
--- /dev/null
+++ b/media/libaudioclient/tests/bbb_1ch_8kHz_s16le.raw
Binary files differ
diff --git a/media/libaudioclient/tests/bbb_2ch_24kHz_s16le.raw b/media/libaudioclient/tests/bbb_2ch_24kHz_s16le.raw
new file mode 100644
index 0000000..c8ac5f7
--- /dev/null
+++ b/media/libaudioclient/tests/bbb_2ch_24kHz_s16le.raw
Binary files differ
diff --git a/media/libaudioclient/tests/test_create_audiorecord.cpp b/media/libaudioclient/tests/test_create_audiorecord.cpp
index 2e0883b..277110b 100644
--- a/media/libaudioclient/tests/test_create_audiorecord.cpp
+++ b/media/libaudioclient/tests/test_create_audiorecord.cpp
@@ -29,14 +29,13 @@
#define NUM_ARGUMENTS 8
#define VERSION_VALUE "1.0"
-#define PACKAGE_NAME "AudioRecord test"
+#define PACKAGE_NAME "AudioRecord test"
namespace android {
using android::content::AttributionSourceState;
-int testRecord(FILE *inputFile, int outputFileFd)
-{
+int testRecord(FILE* inputFile, int outputFileFd) {
char line[MAX_INPUT_FILE_LINE_LENGTH];
uint32_t testCount = 0;
Vector<String16> args;
@@ -47,11 +46,9 @@
attributionSource.token = sp<BBinder>::make();
if (inputFile == nullptr) {
- sp<AudioRecord> record = new AudioRecord(AUDIO_SOURCE_DEFAULT,
- 0 /* sampleRate */,
- AUDIO_FORMAT_DEFAULT,
- AUDIO_CHANNEL_IN_MONO,
- attributionSource);
+ sp<AudioRecord> record =
+ new AudioRecord(AUDIO_SOURCE_DEFAULT, 0 /* sampleRate */, AUDIO_FORMAT_DEFAULT,
+ AUDIO_CHANNEL_IN_MONO, attributionSource);
if (record == 0 || record->initCheck() != NO_ERROR) {
write(outputFileFd, "Error creating AudioRecord\n",
sizeof("Error creating AudioRecord\n"));
@@ -80,11 +77,10 @@
char statusStr[MAX_OUTPUT_FILE_LINE_LENGTH];
bool fast = false;
- if (sscanf(line, " %u %x %x %zu %d %x %u %u",
- &sampleRate, &format, &channelMask,
- &frameCount, ¬ificationFrames,
- &flags, &sessionId, &inputSource) != NUM_ARGUMENTS) {
- fprintf(stderr, "Malformed line for test #%u in input file\n", testCount+1);
+ if (sscanf(line, " %u %x %x %zu %d %x %u %u", &sampleRate, &format, &channelMask,
+ &frameCount, ¬ificationFrames, &flags, &sessionId,
+ &inputSource) != NUM_ARGUMENTS) {
+ fprintf(stderr, "Malformed line for test #%u in input file\n", testCount + 1);
ret = 1;
continue;
}
@@ -100,21 +96,10 @@
sp<AudioRecord> record = new AudioRecord(attributionSource);
const auto emptyCallback = sp<AudioRecord::IAudioRecordCallback>::make();
- record->set(AUDIO_SOURCE_DEFAULT,
- sampleRate,
- format,
- channelMask,
- frameCount,
- fast ? emptyCallback : nullptr,
- notificationFrames,
- false,
- sessionId,
- fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT,
- flags,
- getuid(),
- getpid(),
- &attributes,
- AUDIO_PORT_HANDLE_NONE);
+ record->set(AUDIO_SOURCE_DEFAULT, sampleRate, format, channelMask, frameCount,
+ fast ? emptyCallback : nullptr, notificationFrames, false, sessionId,
+ fast ? AudioRecord::TRANSFER_CALLBACK : AudioRecord::TRANSFER_DEFAULT, flags,
+ getuid(), getpid(), &attributes, AUDIO_PORT_HANDLE_NONE);
status = record->initCheck();
sprintf(statusStr, "\n#### Test %u status %d\n", testCount, status);
write(outputFileFd, statusStr, strlen(statusStr));
@@ -126,11 +111,8 @@
return ret;
}
-}; // namespace android
+}; // namespace android
-
-int main(int argc, char **argv)
-{
+int main(int argc, char** argv) {
return android::main(argc, argv, android::testRecord);
}
-
diff --git a/media/libaudioclient/tests/test_create_audiotrack.cpp b/media/libaudioclient/tests/test_create_audiotrack.cpp
index e7231d3..4e09e21 100644
--- a/media/libaudioclient/tests/test_create_audiotrack.cpp
+++ b/media/libaudioclient/tests/test_create_audiotrack.cpp
@@ -32,18 +32,15 @@
namespace android {
-int testTrack(FILE *inputFile, int outputFileFd)
-{
+int testTrack(FILE* inputFile, int outputFileFd) {
char line[MAX_INPUT_FILE_LINE_LENGTH];
uint32_t testCount = 0;
Vector<String16> args;
int ret = 0;
if (inputFile == nullptr) {
- sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_DEFAULT,
- 0 /* sampleRate */,
- AUDIO_FORMAT_DEFAULT,
- AUDIO_CHANNEL_OUT_STEREO);
+ sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_DEFAULT, 0 /* sampleRate */,
+ AUDIO_FORMAT_DEFAULT, AUDIO_CHANNEL_OUT_STEREO);
if (track == 0 || track->initCheck() != NO_ERROR) {
write(outputFileFd, "Error creating AudioTrack\n",
sizeof("Error creating AudioTrack\n"));
@@ -78,11 +75,10 @@
bool offload = false;
bool fast = false;
- if (sscanf(line, " %u %x %x %zu %d %u %x %u %u %u",
- &sampleRate, &format, &channelMask,
- &frameCount, ¬ificationFrames, &useSharedBuffer,
- &flags, &sessionId, &usage, &contentType) != NUM_ARGUMENTS) {
- fprintf(stderr, "Malformed line for test #%u in input file\n", testCount+1);
+ if (sscanf(line, " %u %x %x %zu %d %u %x %u %u %u", &sampleRate, &format, &channelMask,
+ &frameCount, ¬ificationFrames, &useSharedBuffer, &flags, &sessionId, &usage,
+ &contentType) != NUM_ARGUMENTS) {
+ fprintf(stderr, "Malformed line for test #%u in input file\n", testCount + 1);
ret = 1;
continue;
}
@@ -90,7 +86,7 @@
if (useSharedBuffer != 0) {
size_t heapSize = audio_channel_count_from_out_mask(channelMask) *
- audio_bytes_per_sample(format) * frameCount;
+ audio_bytes_per_sample(format) * frameCount;
heap = new MemoryDealer(heapSize, "AudioTrack Heap Base");
sharedBuffer = heap->allocate(heapSize);
frameCount = 0;
@@ -111,25 +107,13 @@
attributes.usage = usage;
sp<AudioTrack> track = new AudioTrack();
const auto emptyCallback = sp<AudioTrack::IAudioTrackCallback>::make();
- track->set(AUDIO_STREAM_DEFAULT,
- sampleRate,
- format,
- channelMask,
- frameCount,
- flags,
- (fast || offload) ? emptyCallback : nullptr,
- notificationFrames,
- sharedBuffer,
- false,
- sessionId,
- ((fast && sharedBuffer == 0) || offload) ?
- AudioTrack::TRANSFER_CALLBACK : AudioTrack::TRANSFER_DEFAULT,
- offload ? &offloadInfo : nullptr,
- AttributionSourceState(),
- &attributes,
- false,
- 1.0f,
- AUDIO_PORT_HANDLE_NONE);
+ track->set(AUDIO_STREAM_DEFAULT, sampleRate, format, channelMask, frameCount, flags,
+ (fast || offload) ? emptyCallback : nullptr, notificationFrames, sharedBuffer,
+ false, sessionId,
+ ((fast && sharedBuffer == 0) || offload) ? AudioTrack::TRANSFER_CALLBACK
+ : AudioTrack::TRANSFER_DEFAULT,
+ offload ? &offloadInfo : nullptr, AttributionSourceState(), &attributes, false,
+ 1.0f, AUDIO_PORT_HANDLE_NONE);
status = track->initCheck();
sprintf(statusStr, "\n#### Test %u status %d\n", testCount, status);
write(outputFileFd, statusStr, strlen(statusStr));
@@ -141,11 +125,8 @@
return ret;
}
-}; // namespace android
+}; // namespace android
-
-int main(int argc, char **argv)
-{
+int main(int argc, char** argv) {
return android::main(argc, argv, android::testTrack);
}
-
diff --git a/media/libaudioclient/tests/test_create_utils.cpp b/media/libaudioclient/tests/test_create_utils.cpp
index caf5227..c2c2e8b 100644
--- a/media/libaudioclient/tests/test_create_utils.cpp
+++ b/media/libaudioclient/tests/test_create_utils.cpp
@@ -23,10 +23,10 @@
namespace android {
-int readLine(FILE *inputFile, char *line, int size) {
+int readLine(FILE* inputFile, char* line, int size) {
int ret = 0;
while (true) {
- char *str = fgets(line, size, inputFile);
+ char* str = fgets(line, size, inputFile);
if (str == nullptr) {
ret = -1;
break;
@@ -42,8 +42,7 @@
return ret;
}
-bool checkVersion(FILE *inputFile, const char *version)
-{
+bool checkVersion(FILE* inputFile, const char* version) {
char line[MAX_INPUT_FILE_LINE_LENGTH];
char versionKey[MAX_INPUT_FILE_LINE_LENGTH];
char versionValue[MAX_INPUT_FILE_LINE_LENGTH];
@@ -68,9 +67,8 @@
return true;
}
-int main(int argc, char **argv, test_func_t testFunc)
-{
- FILE *inputFile = nullptr;
+int main(int argc, char** argv, test_func_t testFunc) {
+ FILE* inputFile = nullptr;
int outputFileFd = STDOUT_FILENO;
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
int ret = 0;
@@ -96,7 +94,7 @@
if (strcmp(*argv, "-o") == 0) {
argv++;
if (*argv) {
- outputFileFd = open(*argv, O_WRONLY|O_CREAT, mode);
+ outputFileFd = open(*argv, O_WRONLY | O_CREAT, mode);
if (outputFileFd < 0) {
ret = 1;
}
@@ -126,5 +124,4 @@
return ret;
}
-}; // namespace android
-
+}; // namespace android
diff --git a/media/libaudioclient/tests/test_create_utils.h b/media/libaudioclient/tests/test_create_utils.h
index 9a6f9fa..110baf7 100644
--- a/media/libaudioclient/tests/test_create_utils.h
+++ b/media/libaudioclient/tests/test_create_utils.h
@@ -27,13 +27,12 @@
namespace android {
-int readLine(FILE *inputFile, char *line, int size);
+int readLine(FILE* inputFile, char* line, int size);
-bool checkVersion(FILE *inputFile, const char *version);
+bool checkVersion(FILE* inputFile, const char* version);
+typedef int (*test_func_t)(FILE* inputFile, int outputFileFd);
-typedef int (*test_func_t)(FILE *inputFile, int outputFileFd);
+int main(int argc, char** argv, test_func_t testFunc);
-int main(int argc, char **argv, test_func_t testFunc);
-
-}; // namespace android
+}; // namespace android
diff --git a/media/libaudioclient/tests/trackplayerbase_tests.cpp b/media/libaudioclient/tests/trackplayerbase_tests.cpp
new file mode 100644
index 0000000..c9b704d
--- /dev/null
+++ b/media/libaudioclient/tests/trackplayerbase_tests.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TrackPlayerBaseTest"
+
+#include <gtest/gtest.h>
+
+#include <media/TrackPlayerBase.h>
+
+using namespace android;
+using namespace android::media;
+
+class TrackPlayer : public TrackPlayerBase, public AudioTrack::IAudioTrackCallback {
+ public:
+ // methods protected in base class
+ using TrackPlayerBase::playerPause;
+ using TrackPlayerBase::playerSetVolume;
+ using TrackPlayerBase::playerStart;
+ using TrackPlayerBase::playerStop;
+};
+
+class TrackPlayerBaseTest
+ : public ::testing::TestWithParam<std::tuple<double, double, uint32_t, uint32_t>> {
+ public:
+ TrackPlayerBaseTest()
+ : mDuration(std::get<0>(GetParam())),
+ mPulseFreq(std::get<1>(GetParam())),
+ mChannelCount(std::get<2>(GetParam())),
+ mSampleRate(std::get<3>(GetParam())){};
+
+ virtual void SetUp() override {
+ mFrameCount = mDuration * mSampleRate;
+ audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(mChannelCount);
+ sp<AudioTrack> track = new AudioTrack(mStreamType, mSampleRate, mFormat, channelMask,
+ mFrameCount, mFlags, nullptr /* callback */,
+ 0 /* notificationFrames */, AUDIO_SESSION_NONE);
+ ASSERT_EQ(track->initCheck(), NO_ERROR);
+
+ mPlayer = new TrackPlayer();
+ mPlayer->init(track.get(), mPlayer, PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA,
+ AUDIO_SESSION_NONE);
+ sp<AudioTrack> playerTrack = mPlayer->mAudioTrack;
+ ASSERT_EQ(playerTrack->initCheck(), NO_ERROR);
+
+ mBufferSize = mFrameCount * playerTrack->frameSize();
+ mBuffer.resize(mBufferSize, 0);
+
+ // populate buffer
+ ASSERT_NE(mPulseFreq, 0);
+ int32_t nPulseSamples = mSampleRate / mPulseFreq;
+ int32_t pulseSize = nPulseSamples * playerTrack->frameSize();
+
+ int32_t marker = 0;
+ while (marker + pulseSize <= mBufferSize) {
+ memset(mBuffer.data() + marker, 127, pulseSize / 2);
+ marker += pulseSize;
+ }
+ }
+
+ void playBuffer() {
+ bool blocking = true;
+ ssize_t nbytes = mPlayer->mAudioTrack->write(mBuffer.data(), mBufferSize, blocking);
+ EXPECT_EQ(nbytes, mBufferSize) << "Did not write all data in blocking mode";
+ }
+
+ const double mDuration; // seconds
+ sp<TrackPlayer> mPlayer;
+
+ private:
+ const double mPulseFreq;
+ const uint32_t mChannelCount;
+ const uint32_t mSampleRate;
+
+ const audio_format_t mFormat = AUDIO_FORMAT_PCM_16_BIT;
+ const audio_output_flags_t mFlags = AUDIO_OUTPUT_FLAG_NONE;
+ const audio_stream_type_t mStreamType = AUDIO_STREAM_MUSIC;
+
+ int32_t mBufferSize;
+ int32_t mFrameCount;
+ std::vector<uint8_t> mBuffer;
+};
+
+class PlaybackTestParam : public TrackPlayerBaseTest {};
+
+TEST_P(PlaybackTestParam, PlaybackTest) {
+ // no-op implementation
+ EXPECT_TRUE(mPlayer->setStartDelayMs(0).isOk());
+
+ ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+ EXPECT_EQ(mPlayer->playerStop(), NO_ERROR);
+}
+
+INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PlaybackTestParam,
+ ::testing::Values(std::make_tuple(2.5, 25.0, 2, 48000)));
+
+class ChangeVolumeTestParam : public TrackPlayerBaseTest {};
+
+TEST_P(ChangeVolumeTestParam, ChangeVolumeTest) {
+ float volume = 1.0f;
+ (void)mPlayer->setPlayerVolume(volume / 2, volume);
+
+ ASSERT_TRUE(mPlayer->start().isOk());
+ ASSERT_EQ(mPlayer->playerSetVolume(), NO_ERROR);
+
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+
+ EXPECT_TRUE(mPlayer->stop().isOk());
+
+ std::vector<float> setVol = {0.95f, 0.05f, 0.5f, 0.25f, -1.0f, 1.0f, 1.0f};
+ std::vector<float> setPan = {0.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.5f, -0.5f};
+
+ ASSERT_TRUE(mPlayer->start().isOk());
+
+ for (int32_t i = 0; i < setVol.size(); i++) {
+ EXPECT_TRUE(mPlayer->setVolume(setVol[i]).isOk());
+ EXPECT_TRUE(mPlayer->setPan(setPan[i]).isOk());
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+ }
+ EXPECT_TRUE(mPlayer->stop().isOk());
+}
+
+INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, ChangeVolumeTestParam,
+ ::testing::Values(std::make_tuple(1.0, 100.0, 1, 24000)));
+
+class PauseTestParam : public TrackPlayerBaseTest {};
+
+TEST_P(PauseTestParam, PauseTest) {
+ ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+
+ ASSERT_EQ(mPlayer->playerPause(), NO_ERROR);
+ ASSERT_EQ(mPlayer->playerStart(), NO_ERROR);
+
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+
+ EXPECT_EQ(mPlayer->playerStop(), NO_ERROR);
+
+ for (int32_t i = 0; i < 5; i++) {
+ ASSERT_TRUE(mPlayer->start().isOk());
+ ASSERT_NO_FATAL_FAILURE(playBuffer());
+ ASSERT_TRUE(mPlayer->pause().isOk());
+ }
+ EXPECT_TRUE(mPlayer->stop().isOk());
+}
+
+INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PauseTestParam,
+ ::testing::Values(std::make_tuple(1.0, 75.0, 2, 24000)));
diff --git a/media/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp
index 5ffbffc..91efb96 100644
--- a/media/libaudiofoundation/DeviceDescriptorBase.cpp
+++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp
@@ -126,7 +126,14 @@
"%*sEncapsulation modes: %u, metadata types: %u\n", spaces, "",
mEncapsulationModes, mEncapsulationMetadataTypes));
- AudioPort::dump(dst, spaces, nullptr, verbose);
+ std::string portStr;
+ AudioPort::dump(&portStr, spaces, nullptr, verbose);
+ if (!portStr.empty()) {
+ if (!mName.empty()) {
+ dst->append(base::StringPrintf("%*s", spaces, ""));
+ }
+ dst->append(portStr);
+ }
}
std::string DeviceDescriptorBase::toString(bool includeSensitiveInfo) const
diff --git a/media/libaudiofoundation/include/media/AudioPort.h b/media/libaudiofoundation/include/media/AudioPort.h
index d6a098f..b1235f5 100644
--- a/media/libaudiofoundation/include/media/AudioPort.h
+++ b/media/libaudiofoundation/include/media/AudioPort.h
@@ -72,7 +72,7 @@
AudioProfileVector &getAudioProfiles() { return mProfiles; }
void setExtraAudioDescriptors(
- const std::vector<media::audio::common::ExtraAudioDescriptor> extraAudioDescriptors) {
+ const std::vector<media::audio::common::ExtraAudioDescriptor>& extraAudioDescriptors) {
mExtraAudioDescriptors = extraAudioDescriptors;
}
std::vector<media::audio::common::ExtraAudioDescriptor> &getExtraAudioDescriptors() {
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index 3137e13..d8cf20e 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -22,6 +22,7 @@
#include <algorithm>
#include <memory>
+#include <sstream>
#include <string>
#include <utility>
@@ -94,6 +95,33 @@
return defaultValue;
}
+std::string hapticParamToString(const struct HapticGeneratorParam& param) {
+ std::stringstream ss;
+ ss << "\t\tHapticGenerator Parameters:\n";
+ ss << "\t\t- resonant frequency: " << param.resonantFrequency << '\n';
+ ss << "\t\t- bpf Q: " << param.bpfQ << '\n';
+ ss << "\t\t- slow env normalization power: " << param.slowEnvNormalizationPower << '\n';
+ ss << "\t\t- bsf zero Q: " << param.bsfZeroQ << '\n';
+ ss << "\t\t- bsf pole Q: " << param.bsfPoleQ << '\n';
+ ss << "\t\t- distortion corner frequency: " << param.distortionCornerFrequency << '\n';
+ ss << "\t\t- distortion input gain: " << param.distortionInputGain << '\n';
+ ss << "\t\t- distortion cube threshold: " << param.distortionCubeThreshold << '\n';
+ ss << "\t\t- distortion output gain: " << param.distortionOutputGain << '\n';
+ return ss.str();
+}
+
+std::string hapticSettingToString(const struct HapticGeneratorParam& param) {
+ std::stringstream ss;
+ ss << "\t\tHaptic setting:\n";
+ ss << "\t\t- tracks intensity map:\n";
+ for (const auto&[id, intensity] : param.id2Intensity) {
+ ss << "\t\t\t- id=" << id << ", intensity=" << (int) intensity;
+ }
+ ss << "\t\t- max intensity: " << (int) param.maxHapticIntensity << '\n';
+ ss << "\t\t- max haptic amplitude: " << param.maxHapticAmplitude << '\n';
+ return ss.str();
+}
+
int HapticGenerator_Init(struct HapticGeneratorContext *context) {
context->itfe = &gHapticGeneratorInterface;
@@ -129,7 +157,7 @@
context->param.distortionCubeThreshold = 0.1f;
context->param.distortionOutputGain = getFloatProperty(
"vendor.audio.hapticgenerator.distortion.output.gain", DEFAULT_DISTORTION_OUTPUT_GAIN);
- ALOGD("Using distortion output gain as %f", context->param.distortionOutputGain);
+ ALOGD("%s\n%s", __func__, hapticParamToString(context->param).c_str());
context->state = HAPTICGENERATOR_STATE_INITIALIZED;
return 0;
@@ -289,6 +317,7 @@
}
int id = *(int *) value;
os::HapticScale hapticIntensity = static_cast<os::HapticScale>(*((int *) value + 1));
+ ALOGD("Setting haptic intensity as %d", hapticIntensity);
if (hapticIntensity == os::HapticScale::MUTE) {
context->param.id2Intensity.erase(id);
} else {
@@ -313,6 +342,10 @@
context->param.bsfZeroQ = isnan(qFactor) ? DEFAULT_BSF_POLE_Q : qFactor;
context->param.bsfPoleQ = context->param.bsfZeroQ / 2.0f;
context->param.maxHapticAmplitude = maxAmplitude;
+ ALOGD("Updating vibrator info, resonantFrequency=%f, bsfZeroQ=%f, bsfPoleQ=%f, "
+ "maxHapticAmplitude=%f",
+ context->param.resonantFrequency, context->param.bsfZeroQ, context->param.bsfPoleQ,
+ context->param.maxHapticAmplitude);
if (context->processorsRecord.bpf != nullptr) {
context->processorsRecord.bpf->setCoefficients(
@@ -358,6 +391,11 @@
return in;
}
+void HapticGenerator_Dump(int32_t fd, const struct HapticGeneratorParam& param) {
+ dprintf(fd, "%s", hapticParamToString(param).c_str());
+ dprintf(fd, "%s", hapticSettingToString(param).c_str());
+}
+
} // namespace (anonymous)
//-----------------------------------------------------------------------------
@@ -562,6 +600,10 @@
case EFFECT_CMD_SET_AUDIO_MODE:
break;
+ case EFFECT_CMD_DUMP:
+ HapticGenerator_Dump(*(reinterpret_cast<int32_t*>(cmdData)), context->param);
+ break;
+
default:
ALOGW("HapticGenerator_Command invalid command %u", cmdCode);
return -EINVAL;
diff --git a/media/libheadtracking/SensorPoseProvider.cpp b/media/libheadtracking/SensorPoseProvider.cpp
index 8ebaf6e..e5e1521 100644
--- a/media/libheadtracking/SensorPoseProvider.cpp
+++ b/media/libheadtracking/SensorPoseProvider.cpp
@@ -174,7 +174,6 @@
sp<Looper> mLooper;
Listener* const mListener;
SensorManager* const mSensorManager;
- std::thread mThread;
std::mutex mMutex;
std::map<int32_t, SensorEnableGuard> mEnabledSensors;
std::map<int32_t, SensorExtra> mEnabledSensorsExtra GUARDED_BY(mMutex);
@@ -187,11 +186,13 @@
// the worker thread and that thread would notify, via the promise below whenever initialization
// is finished, and whether it was successful.
std::promise<bool> mInitPromise;
+ std::thread mThread;
SensorPoseProviderImpl(const char* packageName, Listener* listener)
: mListener(listener),
- mSensorManager(&SensorManager::getInstanceForPackage(String16(packageName))),
- mThread([this] { threadFunc(); }) {}
+ mSensorManager(&SensorManager::getInstanceForPackage(String16(packageName))) {
+ mThread = std::thread([this] { threadFunc(); });
+ }
void initFinished(bool success) { mInitPromise.set_value(success); }
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 1b8656d..691eede 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -344,19 +344,22 @@
mFrameDecoded = false;
mFrameMemory.clear();
- mRetriever = new MediaMetadataRetriever();
- status_t err = mRetriever->setDataSource(mDataSource, "image/heif");
+ sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
+ status_t err = retriever->setDataSource(mDataSource, "image/heif");
if (err != OK) {
ALOGE("failed to set data source!");
-
mRetriever.clear();
mDataSource.clear();
return false;
}
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ mRetriever = retriever;
+ }
ALOGV("successfully set data source.");
- const char* hasImage = mRetriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
- const char* hasVideo = mRetriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
+ const char* hasImage = retriever->extractMetadata(METADATA_KEY_HAS_IMAGE);
+ const char* hasVideo = retriever->extractMetadata(METADATA_KEY_HAS_VIDEO);
mHasImage = hasImage && !strcasecmp(hasImage, "yes");
mHasVideo = hasVideo && !strcasecmp(hasVideo, "yes");
@@ -364,7 +367,7 @@
HeifFrameInfo* defaultInfo = nullptr;
if (mHasImage) {
// image index < 0 to retrieve primary image
- sp<IMemory> sharedMem = mRetriever->getImageAtIndex(
+ sp<IMemory> sharedMem = retriever->getImageAtIndex(
-1, mOutputColor, true /*metaOnly*/);
if (sharedMem == nullptr || sharedMem->unsecurePointer() == nullptr) {
@@ -399,7 +402,7 @@
}
if (mHasVideo) {
- sp<IMemory> sharedMem = mRetriever->getFrameAtTime(0,
+ sp<IMemory> sharedMem = retriever->getFrameAtTime(0,
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC,
mOutputColor, true /*metaOnly*/);
@@ -425,7 +428,7 @@
initFrameInfo(&mSequenceInfo, videoFrame);
- const char* frameCount = mRetriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT);
+ const char* frameCount = retriever->extractMetadata(METADATA_KEY_VIDEO_FRAME_COUNT);
if (frameCount == nullptr) {
android_errorWriteWithInfoLog(0x534e4554, "215002587", -1, NULL, 0);
ALOGD("No valid sequence information in metadata");
@@ -511,14 +514,27 @@
}
bool HeifDecoderImpl::decodeAsync() {
+ wp<MediaMetadataRetriever> weakRetriever;
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ weakRetriever = mRetriever;
+ mRetriever.clear();
+ }
+
for (size_t i = 1; i < mNumSlices; i++) {
+ sp<MediaMetadataRetriever> retriever = weakRetriever.promote();
+ if (retriever == nullptr) {
+ return false;
+ }
+
ALOGV("decodeAsync(): decoding slice %zu", i);
size_t top = i * mSliceHeight;
size_t bottom = (i + 1) * mSliceHeight;
if (bottom > mImageInfo.mHeight) {
bottom = mImageInfo.mHeight;
}
- sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+
+ sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
-1, mOutputColor, 0, top, mImageInfo.mWidth, bottom);
{
Mutex::Autolock autolock(mLock);
@@ -534,9 +550,6 @@
mScanlineReady.signal();
}
}
- // Aggressive clear to avoid holding on to resources
- mRetriever.clear();
-
// Hold on to mDataSource in case the client wants to redecode.
return false;
}
@@ -549,6 +562,17 @@
return true;
}
+ sp<MediaMetadataRetriever> retriever;
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ if (mRetriever == nullptr) {
+ ALOGE("Failed to get MediaMetadataRetriever!");
+ return false;
+ }
+
+ retriever = mRetriever;
+ }
+
// See if we want to decode in slices to allow client to start
// scanline processing in parallel with decode. If this fails
// we fallback to decoding the full frame.
@@ -563,7 +587,7 @@
if (mNumSlices > 1) {
// get first slice and metadata
- sp<IMemory> frameMemory = mRetriever->getImageRectAtIndex(
+ sp<IMemory> frameMemory = retriever->getImageRectAtIndex(
-1, mOutputColor, 0, 0, mImageInfo.mWidth, mSliceHeight);
if (frameMemory == nullptr || frameMemory->unsecurePointer() == nullptr) {
@@ -598,9 +622,9 @@
if (mHasImage) {
// image index < 0 to retrieve primary image
- mFrameMemory = mRetriever->getImageAtIndex(-1, mOutputColor);
+ mFrameMemory = retriever->getImageAtIndex(-1, mOutputColor);
} else if (mHasVideo) {
- mFrameMemory = mRetriever->getFrameAtTime(0,
+ mFrameMemory = retriever->getFrameAtTime(0,
MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
}
@@ -658,7 +682,17 @@
// set total scanline to sequence height now
mTotalScanline = mSequenceInfo.mHeight;
- mFrameMemory = mRetriever->getFrameAtIndex(frameIndex, mOutputColor);
+ sp<MediaMetadataRetriever> retriever;
+ {
+ Mutex::Autolock _l(mRetrieverLock);
+ retriever = mRetriever;
+ if (retriever == nullptr) {
+ ALOGE("failed to get MediaMetadataRetriever!");
+ return false;
+ }
+ }
+
+ mFrameMemory = retriever->getFrameAtIndex(frameIndex, mOutputColor);
if (mFrameMemory == nullptr || mFrameMemory->unsecurePointer() == nullptr) {
ALOGE("decode: videoFrame is a nullptr");
return false;
@@ -735,9 +769,9 @@
HeifFrameInfo* info = &mImageInfo;
if (info != nullptr) {
return mImageInfo.mBitDepth;
+ } else {
+ return 0;
}
-
- return 0;
}
} // namespace android
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
index 86a8628..c1504cd 100644
--- a/media/libheif/HeifDecoderImpl.h
+++ b/media/libheif/HeifDecoderImpl.h
@@ -72,6 +72,8 @@
bool mHasVideo;
size_t mSequenceLength;
+ Mutex mRetrieverLock;
+
// Slice decoding only
Mutex mLock;
Condition mScanlineReady;
diff --git a/media/libheif/include/HeifDecoderAPI.h b/media/libheif/include/HeifDecoderAPI.h
index dc12486..56f4765 100644
--- a/media/libheif/include/HeifDecoderAPI.h
+++ b/media/libheif/include/HeifDecoderAPI.h
@@ -47,7 +47,7 @@
int32_t mRotationAngle; // Rotation angle, clockwise, should be multiple of 90
uint32_t mBytesPerPixel; // Number of bytes for one pixel
int64_t mDurationUs; // Duration of the frame in us
- uint32_t mBitDepth; // Number of bits for each of the R/G/B channels
+ uint32_t mBitDepth; // Number of bits of R/G/B channel
std::vector<uint8_t> mIccData; // ICC data array
};
@@ -164,7 +164,7 @@
virtual size_t skipScanlines(size_t count) = 0;
/*
- * Returns color depth in bits for each of the R/G/B channels.
+ * Returns color depth of R/G/B channel.
*/
virtual uint32_t getColorDepth() = 0;
diff --git a/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp b/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
index 7799f44..a189d04 100644
--- a/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
+++ b/media/libmediaplayerservice/fuzzer/mediaplayer_fuzzer.cpp
@@ -26,6 +26,8 @@
#include <media/IMediaRecorder.h>
#include <media/IRemoteDisplay.h>
#include <media/IRemoteDisplayClient.h>
+#include <media/MediaHTTPConnection.h>
+#include <media/MediaHTTPService.h>
#include <media/stagefright/RemoteDataSource.h>
#include <media/stagefright/foundation/base64.h>
#include <thread>
@@ -102,6 +104,42 @@
IBinder *onAsBinder() { return nullptr; };
};
+struct TestMediaHTTPConnection : public MediaHTTPConnection {
+ public:
+ TestMediaHTTPConnection() {}
+ virtual ~TestMediaHTTPConnection() {}
+
+ virtual bool connect(const char* /*uri*/, const KeyedVector<String8, String8>* /*headers*/) {
+ return true;
+ }
+
+ virtual void disconnect() { return; }
+
+ virtual ssize_t readAt(off64_t /*offset*/, void* /*data*/, size_t size) { return size; }
+
+ virtual off64_t getSize() { return 0; }
+ virtual status_t getMIMEType(String8* /*mimeType*/) { return NO_ERROR; }
+ virtual status_t getUri(String8* /*uri*/) { return NO_ERROR; }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(TestMediaHTTPConnection);
+};
+
+struct TestMediaHTTPService : public BnInterface<IMediaHTTPService> {
+ public:
+ TestMediaHTTPService() {}
+ ~TestMediaHTTPService(){};
+
+ virtual sp<MediaHTTPConnection> makeHTTPConnection() {
+ mMediaHTTPConnection = sp<TestMediaHTTPConnection>::make();
+ return mMediaHTTPConnection;
+ }
+
+ private:
+ sp<TestMediaHTTPConnection> mMediaHTTPConnection = nullptr;
+ DISALLOW_EVIL_CONSTRUCTORS(TestMediaHTTPService);
+};
+
class BinderDeathNotifier : public IBinder::DeathRecipient {
public:
void binderDied(const wp<IBinder> &) { abort(); }
@@ -140,7 +178,9 @@
AString out;
encodeBase64(uriSuffix.data(), uriSuffix.size(), &out);
uri += out.c_str();
- status = mMediaPlayer->setDataSource(nullptr /*httpService*/, uri.c_str(), &headers);
+ sp<TestMediaHTTPService> testService = sp<TestMediaHTTPService>::make();
+ status =
+ mMediaPlayer->setDataSource(testService /*httpService*/, uri.c_str(), &headers);
break;
}
case fd: {
diff --git a/media/libnblog/Reader.cpp b/media/libnblog/Reader.cpp
index 67d028d..d6232d4 100644
--- a/media/libnblog/Reader.cpp
+++ b/media/libnblog/Reader.cpp
@@ -208,11 +208,14 @@
}
while (back + Entry::kPreviousLengthOffset >= front) {
const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
- const Event type = (const Event)prev[offsetof(entry, type)];
if (prev < front
- || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back
- || type <= EVENT_RESERVED || type >= EVENT_UPPER_BOUND) {
- // prev points to an out of limits or inconsistent entry
+ || prev + prev[offsetof(entry, length)] + Entry::kOverhead != back) {
+ // prev points to an out of limits entry
+ return nullptr;
+ }
+ const Event type = (const Event)prev[offsetof(entry, type)];
+ if (type <= EVENT_RESERVED || type >= EVENT_UPPER_BOUND) {
+ // prev points to an inconsistent entry
return nullptr;
}
// if invalidTypes does not contain the type, then the type is valid.
diff --git a/media/libstagefright/FrameDecoder.cpp b/media/libstagefright/FrameDecoder.cpp
index ce4b4e6..6de112a 100644
--- a/media/libstagefright/FrameDecoder.cpp
+++ b/media/libstagefright/FrameDecoder.cpp
@@ -917,7 +917,7 @@
return ERROR_MALFORMED;
}
- int32_t width, height, stride, srcFormat;
+ int32_t width, height, stride;
if (outputFormat->findInt32("width", &width) == false) {
ALOGE("MediaImageDecoder::onOutputReceived:width is missing in outputFormat");
return ERROR_MALFORMED;
@@ -930,10 +930,9 @@
ALOGE("MediaImageDecoder::onOutputReceived:stride is missing in outputFormat");
return ERROR_MALFORMED;
}
- if (outputFormat->findInt32("color-format", &srcFormat) == false) {
- ALOGE("MediaImageDecoder::onOutputReceived: color format is missing in outputFormat");
- return ERROR_MALFORMED;
- }
+
+ int32_t srcFormat;
+ CHECK(outputFormat->findInt32("color-format", &srcFormat));
uint32_t bitDepth = 8;
if (COLOR_FormatYUVP010 == srcFormat) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 63d3180..9ff2177 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -155,6 +155,7 @@
void bufferChunk(int64_t timestampUs);
bool isAvc() const { return mIsAvc; }
bool isHevc() const { return mIsHevc; }
+ bool isAv1() const { return mIsAv1; }
bool isHeic() const { return mIsHeic; }
bool isAudio() const { return mIsAudio; }
bool isMPEG4() const { return mIsMPEG4; }
@@ -319,6 +320,7 @@
volatile bool mStarted;
bool mIsAvc;
bool mIsHevc;
+ bool mIsAv1;
bool mIsDovi;
bool mIsAudio;
bool mIsVideo;
@@ -467,6 +469,7 @@
void writePaspBox();
void writeAvccBox();
void writeHvccBox();
+ void writeAv1cBox();
void writeDoviConfigBox();
void writeUrlBox();
void writeDrefBox();
@@ -660,6 +663,8 @@
return "avc1";
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
return "hvc1";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
+ return "av01";
}
} else if (!strncasecmp(mime, "application/", 12)) {
return "mett";
@@ -1541,6 +1546,15 @@
writeFourcc("isom");
writeFourcc("mp42");
}
+ // If an AV1 video track is present, write "av01" as one of the
+ // compatible brands.
+ for (List<Track *>::iterator it = mTracks.begin(); it != mTracks.end();
+ ++it) {
+ if ((*it)->isAv1()) {
+ writeFourcc("av01");
+ break;
+ }
+ }
}
endBox();
@@ -2205,6 +2219,7 @@
mMeta->findCString(kKeyMIMEType, &mime);
mIsAvc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
mIsHevc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC);
+ mIsAv1 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1);
mIsDovi = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION);
mIsAudio = !strncasecmp(mime, "audio/", 6);
mIsVideo = !strncasecmp(mime, "video/", 6);
@@ -2639,6 +2654,8 @@
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC) ||
!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC)) {
mMeta->findData(kKeyHVCC, &type, &data, &size);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AV1)) {
+ mMeta->findData(kKeyAV1C, &type, &data, &size);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_DOLBY_VISION)) {
getDolbyVisionProfile();
if (!mMeta->findData(kKeyAVCC, &type, &data, &size) &&
@@ -4262,6 +4279,7 @@
!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime) ||
!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime) ||
!strcasecmp(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC, mime)) {
if (!mCodecSpecificData ||
@@ -4433,6 +4451,8 @@
writeAvccBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)) {
writeHvccBox();
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AV1, mime)) {
+ writeAv1cBox();
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DOLBY_VISION, mime)) {
if (mDoviProfile <= DolbyVisionProfileDvheSt) {
writeHvccBox();
@@ -5000,6 +5020,15 @@
mOwner->endBox(); // hvcC
}
+void MPEG4Writer::Track::writeAv1cBox() {
+ CHECK(mCodecSpecificData);
+ CHECK_GE(mCodecSpecificDataSize, 4u);
+
+ mOwner->beginBox("av1C");
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+ mOwner->endBox(); // av1C
+}
+
void MPEG4Writer::Track::writeDoviConfigBox() {
CHECK_NE(mDoviProfile, 0u);
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 24608a7..ed0819d 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -110,8 +110,12 @@
if (mAnchorTimeRealUs != -1) {
int64_t oldNowMediaUs =
mAnchorTimeMediaUs + (nowUs - mAnchorTimeRealUs) * (double)mPlaybackRate;
- if (nowMediaUs < oldNowMediaUs + kAnchorFluctuationAllowedUs
- && nowMediaUs > oldNowMediaUs - kAnchorFluctuationAllowedUs) {
+ // earlier, we ensured that the anchor times are non-negative and the
+ // math to calculate the now/oldNow times stays non-negative.
+ // by casting into uint64_t, we gain headroom to avoid any overflows at the upper end
+ // when adding the fluctuation allowance.
+ if ((uint64_t)nowMediaUs < (uint64_t)oldNowMediaUs + kAnchorFluctuationAllowedUs
+ && (uint64_t)nowMediaUs + kAnchorFluctuationAllowedUs > (uint64_t)oldNowMediaUs) {
return;
}
}
diff --git a/media/libstagefright/bqhelper/Android.bp b/media/libstagefright/bqhelper/Android.bp
index 0e2b472..a4f0fb3 100644
--- a/media/libstagefright/bqhelper/Android.bp
+++ b/media/libstagefright/bqhelper/Android.bp
@@ -12,6 +12,7 @@
double_loadable: true,
srcs: [
+ ":libgui_frame_event_aidl",
"FrameDropper.cpp",
"GraphicBufferSource.cpp",
],
diff --git a/media/libstagefright/data/media_codecs_google_c2_video.xml b/media/libstagefright/data/media_codecs_google_c2_video.xml
index 3509ef8..03d8b78 100644
--- a/media/libstagefright/data/media_codecs_google_c2_video.xml
+++ b/media/libstagefright/data/media_codecs_google_c2_video.xml
@@ -60,7 +60,7 @@
<MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8">
<Alias name="OMX.google.vp8.decoder" />
<Limit name="size" min="2x2" max="2048x2048" />
- <Limit name="alignment" value="2x2" />
+ <Limit name="alignment" value="1x1" />
<Limit name="block-size" value="16x16" />
<Limit name="block-count" range="1-16384" />
<Limit name="blocks-per-second" range="1-1000000" />
@@ -70,7 +70,7 @@
<MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9">
<Alias name="OMX.google.vp9.decoder" />
<Limit name="size" min="2x2" max="2048x2048" />
- <Limit name="alignment" value="2x2" />
+ <Limit name="alignment" value="1x1" />
<Limit name="block-size" value="16x16" />
<Limit name="block-count" range="1-16384" />
<Limit name="blocks-per-second" range="1-500000" />
diff --git a/media/libstagefright/data/media_codecs_sw.xml b/media/libstagefright/data/media_codecs_sw.xml
index d7e2d18..fb4f9a0 100644
--- a/media/libstagefright/data/media_codecs_sw.xml
+++ b/media/libstagefright/data/media_codecs_sw.xml
@@ -150,7 +150,7 @@
<MediaCodec name="c2.android.vp8.decoder" type="video/x-vnd.on2.vp8" variant="slow-cpu,!slow-cpu">
<Alias name="OMX.google.vp8.decoder" />
<Limit name="size" min="2x2" max="2048x2048" />
- <Limit name="alignment" value="2x2" />
+ <Limit name="alignment" value="1x1" />
<Limit name="block-size" value="16x16" />
<Variant name="!slow-cpu">
<Limit name="block-count" range="1-16384" />
@@ -166,7 +166,7 @@
</MediaCodec>
<MediaCodec name="c2.android.vp9.decoder" type="video/x-vnd.on2.vp9" variant="slow-cpu,!slow-cpu">
<Alias name="OMX.google.vp9.decoder" />
- <Limit name="alignment" value="2x2" />
+ <Limit name="alignment" value="1x1" />
<Limit name="block-size" value="16x16" />
<Variant name="!slow-cpu">
<Limit name="size" min="2x2" max="2048x2048" />
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 12a0d53..0bdb41b 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -24,6 +24,7 @@
#include <android_media_Utils.h>
#include <private/android/AHardwareBufferHelpers.h>
+#include <ui/PublicFormat.h>
#include <utils/Log.h>
using namespace android;
@@ -34,6 +35,8 @@
int64_t timestamp, int32_t width, int32_t height, int32_t numPlanes) :
mReader(reader), mFormat(format), mUsage(usage), mBuffer(buffer), mLockedBuffer(nullptr),
mTimestamp(timestamp), mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
+ PublicFormat publicFormat = static_cast<PublicFormat>(format);
+ mHalDataSpace = mapPublicFormatToHalDataspace(publicFormat);
LOG_FATAL_IF(reader == nullptr, "AImageReader shouldn't be null while creating AImage");
}
@@ -156,6 +159,20 @@
return AMEDIA_OK;
}
+media_status_t
+AImage::getDataSpace(android_dataspace* dataSpace) const {
+ if (dataSpace == nullptr) {
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ *dataSpace = static_cast<android_dataspace>(-1);
+ if (isClosed()) {
+ ALOGE("%s: image %p has been closed!", __FUNCTION__, this);
+ return AMEDIA_ERROR_INVALID_OBJECT;
+ }
+ *dataSpace = mHalDataSpace;
+ return AMEDIA_OK;
+}
+
media_status_t AImage::lockImage() {
if (mBuffer == nullptr || mBuffer->mGraphicBuffer == nullptr) {
LOG_ALWAYS_FATAL("%s: AImage %p has no buffer.", __FUNCTION__, this);
@@ -762,3 +779,15 @@
}
return image->getHardwareBuffer(buffer);
}
+
+EXPORT
+media_status_t AImage_getDataSpace(
+ const AImage* image, /*out*/int32_t* dataSpace) {
+ ALOGV("%s", __FUNCTION__);
+
+ if (image == nullptr || dataSpace == nullptr) {
+ ALOGE("%s: bad argument. image %p dataSpace %p", __FUNCTION__, image, dataSpace);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return image->getDataSpace((android_dataspace*)(dataSpace));
+}
\ No newline at end of file
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
index 05115b9..dc10a6a 100644
--- a/media/ndk/NdkImagePriv.h
+++ b/media/ndk/NdkImagePriv.h
@@ -82,6 +82,7 @@
media_status_t getPlaneRowStride(int planeIdx, /*out*/int32_t* rowStride) const;
media_status_t getPlaneData(int planeIdx,/*out*/uint8_t** data, /*out*/int* dataLength) const;
media_status_t getHardwareBuffer(/*out*/AHardwareBuffer** buffer) const;
+ media_status_t getDataSpace(/*out*/android_dataspace* dataSpace) const;
private:
// AImage should be deleted through free() API.
@@ -101,6 +102,7 @@
const int32_t mWidth;
const int32_t mHeight;
const int32_t mNumPlanes;
+ android_dataspace mHalDataSpace = HAL_DATASPACE_UNKNOWN;
bool mIsClosed = false;
mutable Mutex mLock;
};
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 1067e24..ac5cba8 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -46,6 +46,9 @@
static constexpr int kWindowHalTokenSizeMax = 256;
+static media_status_t validateParameters(int32_t width, int32_t height, int32_t format,
+ uint64_t usage, int32_t maxImages,
+ /*out*/ AImageReader**& reader);
static native_handle_t *convertHalTokenToNativeHandle(const HalToken &halToken);
bool
@@ -263,13 +266,17 @@
int32_t height,
int32_t format,
uint64_t usage,
- int32_t maxImages)
+ int32_t maxImages,
+ uint32_t hardwareBufferFormat,
+ android_dataspace dataSpace)
: mWidth(width),
mHeight(height),
mFormat(format),
mUsage(usage),
mMaxImages(maxImages),
mNumPlanes(getNumPlanesForFormat(format)),
+ mHalFormat(hardwareBufferFormat),
+ mHalDataSpace(dataSpace),
mFrameListener(new FrameListener(this)),
mBufferRemovedListener(new BufferRemovedListener(this)) {}
@@ -280,9 +287,6 @@
media_status_t
AImageReader::init() {
- PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
- mHalFormat = mapPublicFormatToHalFormat(publicFormat);
- mHalDataSpace = mapPublicFormatToHalDataspace(publicFormat);
mHalUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage);
sp<IGraphicBufferProducer> gbProducer;
@@ -646,6 +650,41 @@
}
}
+static
+media_status_t validateParameters(int32_t width, int32_t height, int32_t format,
+ uint64_t usage, int32_t maxImages,
+ /*out*/ AImageReader**& reader) {
+ if (reader == nullptr) {
+ ALOGE("%s: reader argument is null", __FUNCTION__);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (width < 1 || height < 1) {
+ ALOGE("%s: image dimension must be positive: w:%d h:%d",
+ __FUNCTION__, width, height);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (maxImages < 1) {
+ ALOGE("%s: max outstanding image count must be at least 1 (%d)",
+ __FUNCTION__, maxImages);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
+ ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
+ __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+
+ if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
+ ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
+ __FUNCTION__, format, usage);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return AMEDIA_OK;
+}
+
static native_handle_t *convertHalTokenToNativeHandle(
const HalToken &halToken) {
// We attempt to store halToken in the ints of the native_handle_t after its
@@ -696,42 +735,32 @@
} //extern "C"
EXPORT
+media_status_t AImageReader_newWithDataSpace(
+ int32_t width, int32_t height, uint64_t usage, int32_t maxImages,
+ uint32_t hardwareBufferFormat, int32_t dataSpace,
+ /*out*/ AImageReader** reader) {
+ ALOGV("%s", __FUNCTION__);
+
+ android_dataspace halDataSpace = static_cast<android_dataspace>(dataSpace);
+ int32_t format = static_cast<int32_t>(
+ mapHalFormatDataspaceToPublicFormat(hardwareBufferFormat, halDataSpace));
+ return AImageReader_newWithUsage(width, height, format, usage, maxImages, reader);
+}
+
+EXPORT
media_status_t AImageReader_newWithUsage(
int32_t width, int32_t height, int32_t format, uint64_t usage,
int32_t maxImages, /*out*/ AImageReader** reader) {
ALOGV("%s", __FUNCTION__);
- if (width < 1 || height < 1) {
- ALOGE("%s: image dimension must be positive: w:%d h:%d",
- __FUNCTION__, width, height);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
+ validateParameters(width, height, format, usage, maxImages, reader);
- if (maxImages < 1) {
- ALOGE("%s: max outstanding image count must be at least 1 (%d)",
- __FUNCTION__, maxImages);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
-
- if (maxImages > BufferQueueDefs::NUM_BUFFER_SLOTS) {
- ALOGE("%s: max outstanding image count (%d) cannot be larget than %d.",
- __FUNCTION__, maxImages, BufferQueueDefs::NUM_BUFFER_SLOTS);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
-
- if (!AImageReader::isSupportedFormatAndUsage(format, usage)) {
- ALOGE("%s: format %d is not supported with usage 0x%" PRIx64 " by AImageReader",
- __FUNCTION__, format, usage);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
-
- if (reader == nullptr) {
- ALOGE("%s: reader argument is null", __FUNCTION__);
- return AMEDIA_ERROR_INVALID_PARAMETER;
- }
+ PublicFormat publicFormat = static_cast<PublicFormat>(format);
+ uint32_t halFormat = mapPublicFormatToHalFormat(publicFormat);
+ android_dataspace halDataSpace = mapPublicFormatToHalDataspace(publicFormat);
AImageReader* tmpReader = new AImageReader(
- width, height, format, usage, maxImages);
+ width, height, format, usage, maxImages, halFormat, halDataSpace);
if (tmpReader == nullptr) {
ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
return AMEDIA_ERROR_UNKNOWN;
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 37c606e..0199616 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -56,10 +56,12 @@
int32_t height,
int32_t format,
uint64_t usage,
- int32_t maxImages);
+ int32_t maxImages,
+ uint32_t hardwareBufferFormat,
+ android_dataspace dataSpace);
~AImageReader();
- // Inintialize AImageReader, uninitialized or failed to initialize AImageReader
+ // Initialize AImageReader, uninitialized or failed to initialize AImageReader
// should never be passed to application
media_status_t init();
@@ -79,7 +81,6 @@
void close();
private:
-
friend struct AImage; // for grabing reader lock
BufferItem* getBufferItemLocked();
@@ -118,13 +119,16 @@
const int32_t mWidth;
const int32_t mHeight;
- const int32_t mFormat;
+ int32_t mFormat;
const uint64_t mUsage; // AHARDWAREBUFFER_USAGE_* flags.
const int32_t mMaxImages;
// TODO(jwcai) Seems completely unused in AImageReader class.
const int32_t mNumPlanes;
+ uint32_t mHalFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ android_dataspace mHalDataSpace = HAL_DATASPACE_UNKNOWN;
+
struct FrameListener : public ConsumerBase::FrameAvailableListener {
public:
explicit FrameListener(AImageReader* parent) : mReader(parent) {}
@@ -155,8 +159,6 @@
};
sp<BufferRemovedListener> mBufferRemovedListener;
- int mHalFormat;
- android_dataspace mHalDataSpace;
uint64_t mHalUsage;
sp<IGraphicBufferProducer> mProducer;
diff --git a/media/ndk/fuzzer/Android.bp b/media/ndk/fuzzer/Android.bp
new file mode 100644
index 0000000..b2ae220
--- /dev/null
+++ b/media/ndk/fuzzer/Android.bp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_av_media_ndk_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_av_media_ndk_license"],
+}
+
+cc_defaults {
+ name: "libmediandk_fuzzer_defaults",
+ shared_libs: [
+ "libandroid_runtime_lazy",
+ "libbase",
+ "libdatasource",
+ "libmedia",
+ "libmediadrm",
+ "libmedia_omx",
+ "libmedia_jni_utils",
+ "libstagefright",
+ "libstagefright_foundation",
+ "liblog",
+ "libutils",
+ "libcutils",
+ "libnativewindow",
+ "libhidlbase",
+ "libgui",
+ "libui",
+ "libmediandk",
+ ],
+ static_libs: [
+ "libmediandk_utils",
+ "libnativehelper_lazy",
+ ],
+ header_libs: [
+ "media_ndk_headers",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
+
+cc_fuzz {
+ name: "ndk_crypto_fuzzer",
+ srcs: ["ndk_crypto_fuzzer.cpp"],
+ defaults: ["libmediandk_fuzzer_defaults"],
+}
diff --git a/media/ndk/fuzzer/README.md b/media/ndk/fuzzer/README.md
new file mode 100644
index 0000000..4f78e4a
--- /dev/null
+++ b/media/ndk/fuzzer/README.md
@@ -0,0 +1,24 @@
+# Fuzzers for libmediandk
+
+## Table of contents
++ [ndk_crypto_fuzzer](#NdkCrypto)
+
+# <a name="NdkCrypto"></a> Fuzzer for NdkCrypto
+
+NdkCrypto supports the following parameters:
+ UniversalIdentifier (parameter name: "uuid")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+| `uuid`| `Array`| Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_crypto_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_crypto_fuzzer/ndk_crypto_fuzzer
+```
diff --git a/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
new file mode 100644
index 0000000..2b22f0f
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_crypto_fuzzer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 <fuzzer/FuzzedDataProvider.h>
+#include <media/NdkMediaCrypto.h>
+
+constexpr size_t kMaxString = 256;
+constexpr size_t kMinBytes = 0;
+constexpr size_t kMaxBytes = 1000;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp(data, size);
+ AMediaUUID uuid = {};
+ int32_t maxLen = fdp.ConsumeIntegralInRange<size_t>(kMinBytes, (size_t)sizeof(AMediaUUID));
+ for (size_t idx = 0; idx < maxLen; ++idx) {
+ uuid[idx] = fdp.ConsumeIntegral<uint8_t>();
+ }
+ std::vector<uint8_t> initData =
+ fdp.ConsumeBytes<uint8_t>(fdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaCrypto* crypto = AMediaCrypto_new(uuid, initData.data(), initData.size());
+ while (fdp.remaining_bytes()) {
+ auto invokeNdkCryptoFuzzer = fdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ AMediaCrypto_requiresSecureDecoderComponent(
+ fdp.ConsumeRandomLengthString(kMaxString).c_str());
+ },
+ [&]() { AMediaCrypto_isCryptoSchemeSupported(uuid); },
+ });
+ invokeNdkCryptoFuzzer();
+ }
+ AMediaCrypto_delete(crypto);
+ return 0;
+}
diff --git a/media/ndk/include/media/NdkImage.h b/media/ndk/include/media/NdkImage.h
index 71bc6d9..814a327 100644
--- a/media/ndk/include/media/NdkImage.h
+++ b/media/ndk/include/media/NdkImage.h
@@ -583,7 +583,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param width the width of the image will be filled here if the method call succeeeds.
+ * @param width the width of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -599,7 +599,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param height the height of the image will be filled here if the method call succeeeds.
+ * @param height the height of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -617,7 +617,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param format the format of the image will be filled here if the method call succeeeds.
+ * @param format the format of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -636,7 +636,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param rect the cropped rectangle of the image will be filled here if the method call succeeeds.
+ * @param rect the cropped rectangle of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -662,7 +662,7 @@
* Available since API level 24.
*
* @param image the {@link AImage} of interest.
- * @param timestampNs the timestamp of the image will be filled here if the method call succeeeds.
+ * @param timestampNs the timestamp of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -682,7 +682,7 @@
*
* @param image the {@link AImage} of interest.
* @param numPlanes the number of planes of the image will be filled here if the method call
- * succeeeds.
+ * succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -706,7 +706,7 @@
*
* @param image the {@link AImage} of interest.
* @param planeIdx the index of the plane. Must be less than the number of planes of input image.
- * @param pixelStride the pixel stride of the image will be filled here if the method call succeeeds.
+ * @param pixelStride the pixel stride of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -735,7 +735,7 @@
*
* @param image the {@link AImage} of interest.
* @param planeIdx the index of the plane. Must be less than the number of planes of input image.
- * @param rowStride the row stride of the image will be filled here if the method call succeeeds.
+ * @param rowStride the row stride of the image will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -762,8 +762,8 @@
*
* @param image the {@link AImage} of interest.
* @param planeIdx the index of the plane. Must be less than the number of planes of input image.
- * @param data the data pointer of the image will be filled here if the method call succeeeds.
- * @param dataLength the valid length of data will be filled here if the method call succeeeds.
+ * @param data the data pointer of the image will be filled here if the method call succeeds.
+ * @param dataLength the valid length of data will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -826,6 +826,25 @@
*/
media_status_t AImage_getHardwareBuffer(const AImage* image, /*out*/AHardwareBuffer** buffer) __INTRODUCED_IN(26);
+/**
+ * Query the dataspace of the input {@link AImage}.
+ *
+ * Available since API level 33.
+ *
+ * @param image the {@link AImage} of interest.
+ * @param dataSpace the dataspace of the image will be filled here if the method call succeeds.
+ * This must be one of the ADATASPACE_* enum value defined in
+ * {@link ADataSpace}.
+ *
+ * @return <ul>
+ * <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if image or dataSpace is NULL.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_OBJECT} if the {@link AImageReader} generated this
+ * image has been deleted.</li></ul>
+ */
+media_status_t AImage_getDataSpace(const AImage* image,
+ /*out*/int32_t* dataSpace) __INTRODUCED_IN(33);
+
__END_DECLS
#endif //_NDK_IMAGE_H
diff --git a/media/ndk/include/media/NdkImageReader.h b/media/ndk/include/media/NdkImageReader.h
index 4bd7f2a..992955b 100644
--- a/media/ndk/include/media/NdkImageReader.h
+++ b/media/ndk/include/media/NdkImageReader.h
@@ -79,7 +79,7 @@
* by the user, one of them has to be released before a new {@link AImage} will become
* available for access through {@link AImageReader_acquireLatestImage} or
* {@link AImageReader_acquireNextImage}. Must be greater than 0.
- * @param reader The created image reader will be filled here if the method call succeeeds.
+ * @param reader The created image reader will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -133,7 +133,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param width the default width of the reader will be filled here if the method call succeeeds.
+ * @param width the default width of the reader will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -151,7 +151,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param height the default height of the reader will be filled here if the method call succeeeds.
+ * @param height the default height of the reader will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -165,7 +165,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param format the fromat of the reader will be filled here if the method call succeeeds. The
+ * @param format the format of the reader will be filled here if the method call succeeds. The
* value will be one of the AIMAGE_FORMAT_* enum value defiend in {@link NdkImage.h}.
*
* @return <ul>
@@ -181,7 +181,7 @@
*
* @param reader The image reader of interest.
* @param maxImages the maximum number of concurrently acquired images of the reader will be filled
- * here if the method call succeeeds.
+ * here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -212,7 +212,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param image the acquired {@link AImage} will be filled here if the method call succeeeds.
+ * @param image the acquired {@link AImage} will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -257,7 +257,7 @@
* Available since API level 24.
*
* @param reader The image reader of interest.
- * @param image the acquired {@link AImage} will be filled here if the method call succeeeds.
+ * @param image the acquired {@link AImage} will be filled here if the method call succeeds.
*
* @return <ul>
* <li>{@link AMEDIA_OK} if the method call succeeds.</li>
@@ -387,6 +387,44 @@
/*out*/ AImageReader** reader) __INTRODUCED_IN(26);
/**
+ * AImageReader constructor similar to {@link AImageReader_newWithUsage} that takes
+ * two additional parameters to build the format of the Image. All other parameters
+ * and the return values are identical to those passed to {@link AImageReader_newWithUsage}.
+ *
+ * <p>Instead of passing {@code format} parameter, this constructor accepts
+ * the combination of {@code hardwareBufferFormat} and {@code dataSpace} for the
+ * format of the Image that the reader will produce.</p>
+ *
+ * Available since API level 33.
+ *
+ * @param width The default width in pixels of the Images that this reader will produce.
+ * @param height The default height in pixels of the Images that this reader will produce.
+ * @param usage specifies how the consumer will access the AImage.
+ * See {@link AImageReader_newWithUsage} parameter description for more details.
+ * @param maxImages The maximum number of images the user will want to access simultaneously.
+ * See {@link AImageReader_newWithUsage} parameter description for more details.
+ * @param hardwareBufferFormat The hardware buffer format passed by the producer.
+ * This must be one of the AHARDWAREBUFFER_FORMAT_* enum values defined
+ * in {@link hardware_buffer.h}.
+ * @param dataSpace The dataspace of the Image passed by the producer.
+ * This must be one of the ADATASPACE_* enum values defined in
+ * {@link ADataSpace}.
+ * @param reader The created image reader will be filled here if the method call succeeds.
+ *
+ * @return <ul>
+ * <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL, or one or more of width,
+ * height, maxImages, hardwareBufferFormat or dataSpace arguments
+ * is not supported.</li>
+ * <li>{@link AMEDIA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
+ *
+ * @see AImageReader_newWithUsage
+ */
+media_status_t AImageReader_newWithDataSpace(int32_t width, int32_t height, uint64_t usage,
+ int32_t maxImages, uint32_t hardwareBufferFormat, int32_t dataSpace,
+ /*out*/ AImageReader** reader) __INTRODUCED_IN(33);
+
+/**
* Acquire the next {@link AImage} from the image reader's queue asynchronously.
*
* <p>AImageReader acquire method similar to {@link AImageReader_acquireNextImage} that takes an
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index c8faced..bac4b22 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -13,11 +13,13 @@
AImageReader_getWindow; # introduced=24
AImageReader_new; # introduced=24
AImageReader_newWithUsage; # introduced=26
+ AImageReader_newWithDataSpace; # introduced=Tiramisu
AImageReader_setBufferRemovedListener; # introduced=26
AImageReader_setImageListener; # introduced=24
AImage_delete; # introduced=24
AImage_deleteAsync; # introduced=26
AImage_getCropRect; # introduced=24
+ AImage_getDataSpace; # introduced=Tiramisu
AImage_getFormat; # introduced=24
AImage_getHardwareBuffer; # introduced=26
AImage_getHeight; # introduced=24
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index a38ef57..04d9ed9 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -21,23 +21,76 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-cc_library {
- name: "libmediautils",
+cc_defaults {
+ name: "libmediautils_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
+filegroup {
+ name: "libmediautils_core_srcs",
srcs: [
"AImageReaderUtils.cpp",
- "BatteryNotifier.cpp",
"ISchedulingPolicyService.cpp",
"Library.cpp",
- "LimitProcessMemory.cpp",
"MediaUtilsDelayed.cpp",
- "MemoryLeakTrackUtil.cpp",
"MethodStatistics.cpp",
"Process.cpp",
- "ProcessInfo.cpp",
"SchedulingPolicyService.cpp",
- "ServiceUtilities.cpp",
"ThreadSnapshot.cpp",
+ ],
+}
+
+cc_library_headers {
+ name: "libmediautils_headers",
+ host_supported: true,
+ vendor_available: true, // required for platform/hardware/interfaces
+ shared_libs: [
+ "liblog",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libmediautils_core",
+ defaults: ["libmediautils_defaults"],
+ host_supported: true,
+ srcs: [":libmediautils_core_srcs"],
+ shared_libs: [
+ "libaudioutils", // for clock.h, Statistics.h
+ "libbase",
+ "libbinder",
+ "libhidlbase",
+ "liblog",
+ "libpermission",
+ "libutils",
+ "android.hardware.graphics.bufferqueue@1.0",
+ "android.hidl.token@1.0-utils",
+ "packagemanager_aidl-cpp",
+ ],
+
+ export_shared_lib_headers: [
+ "libpermission",
+ ],
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libmediautils",
+ defaults: ["libmediautils_defaults"],
+ srcs: [
+ ":libmediautils_core_srcs",
+ "BatteryNotifier.cpp",
+ "MemoryLeakTrackUtil.cpp",
+ "LimitProcessMemory.cpp",
+ "ProcessInfo.cpp",
+ "ServiceUtilities.cpp",
"TimeCheck.cpp",
"TimerThread.cpp",
],
@@ -49,12 +102,13 @@
shared_libs: [
"libaudioclient_aidl_conversion",
"libaudioutils", // for clock.h, Statistics.h
+ "libbase",
"libbinder",
"libcutils",
- "liblog",
- "libutils",
"libhidlbase",
+ "liblog",
"libpermission",
+ "libutils",
"android.hardware.graphics.bufferqueue@1.0",
"android.hidl.token@1.0-utils",
"packagemanager_aidl-cpp",
@@ -65,12 +119,6 @@
logtags: ["EventLogTags.logtags"],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
-
header_libs: [
"bionic_libc_platform_headers",
"libmedia_headers",
@@ -81,7 +129,7 @@
],
required: [
- "libmediautils_delayed", // lazy loaded
+ "libmediautils_delayed", // lazy loaded
],
include_dirs: [
@@ -94,14 +142,10 @@
cc_library {
name: "libmediautils_delayed", // match with MEDIAUTILS_DELAYED_LIBRARY_NAME
+ defaults: ["libmediautils_defaults"],
srcs: [
"MediaUtilsDelayedLibrary.cpp",
],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
shared_libs: [
"liblog",
"libutils",
@@ -111,16 +155,12 @@
cc_library {
name: "libmediautils_vendor",
- vendor_available: true, // required for platform/hardware/interfaces
+ defaults: ["libmediautils_defaults"],
+ vendor_available: true, // required for platform/hardware/interfaces
srcs: [
"MemoryLeakTrackUtil.cpp",
],
- cflags: [
- "-Wall",
- "-Wextra",
- "-Werror",
- ],
shared_libs: [
"liblog",
"libutils",
@@ -137,23 +177,3 @@
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
-
-
-cc_library_headers {
- name: "libmediautils_headers",
- vendor_available: true, // required for platform/hardware/interfaces
-
- export_include_dirs: ["include"],
-}
-
-cc_test {
- name: "libmediautils_test",
- srcs: [
- "memory-test.cpp",
- "TimerThread-test.cpp",
- ],
- shared_libs: [
- "libmediautils",
- "libutils",
- ]
-}
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
index d26e6c2..fc4c2f9 100644
--- a/media/utils/fuzzers/Android.bp
+++ b/media/utils/fuzzers/Android.bp
@@ -9,14 +9,13 @@
cc_defaults {
name: "libmediautils_fuzzer_defaults",
+ host_supported: true,
shared_libs: [
- "libbatterystats_aidl",
"libbinder",
- "libcutils",
"liblog",
- "libmediautils",
+ "libcutils",
+ "libmediautils_core",
"libutils",
- "libbinder",
"framework-permission-aidl-cpp",
"packagemanager_aidl-cpp",
],
@@ -27,33 +26,36 @@
"-Werror",
"-Wno-c++2a-extensions",
],
-
- header_libs: [
- "bionic_libc_platform_headers",
- "libmedia_headers",
- ],
}
cc_fuzz {
name: "libmediautils_fuzzer_battery_notifier",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["BatteryNotifierFuzz.cpp"],
}
cc_fuzz {
name: "libmediautils_fuzzer_scheduling_policy_service",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["SchedulingPolicyServiceFuzz.cpp"],
}
cc_fuzz {
name: "libmediautils_fuzzer_service_utilities",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["ServiceUtilitiesFuzz.cpp"],
}
cc_fuzz {
name: "libmediautils_fuzzer_time_check",
+ host_supported: false,
+ shared_libs: ["libmediautils"],
defaults: ["libmediautils_fuzzer_defaults"],
srcs: ["TimeCheckFuzz.cpp"],
}
diff --git a/media/utils/include/mediautils/ExtendedAccumulator.h b/media/utils/include/mediautils/ExtendedAccumulator.h
new file mode 100644
index 0000000..7e3e170
--- /dev/null
+++ b/media/utils/include/mediautils/ExtendedAccumulator.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2022 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 <atomic>
+#include <cstdint>
+#include <tuple>
+#include <type_traits>
+
+#include <log/log.h>
+
+namespace android::mediautils {
+
+// The goal of this class is to detect and accumulate wraparound occurrences on a
+// lower sized integer.
+
+// This class assumes that the underlying unsigned type is either incremented or
+// decremented by at most the underlying signed type between any two subsequent
+// polls (or construction). This is well-defined as the modular nature of
+// unsigned arithmetic ensures that every new value maps 1-1 to an
+// increment/decrement over the same sized signed type. It also ensures that our
+// counter will be equivalent mod the size of the integer even if the underlying
+// type is modified outside of this range.
+//
+// For convenience, this class is thread compatible. Additionally, it is safe
+// as long as there is only one writer.
+template <typename Integral = uint32_t, typename AccumulatingType = uint64_t>
+class ExtendedAccumulator {
+ static_assert(sizeof(Integral) < sizeof(AccumulatingType),
+ "Accumulating type should be larger than underlying type");
+ static_assert(std::is_integral_v<Integral> && std::is_unsigned_v<Integral>,
+ "Wraparound behavior is only well-defiend for unsigned ints");
+ static_assert(std::is_integral_v<AccumulatingType>);
+
+ public:
+ enum class Wrap {
+ NORMAL = 0,
+ UNDERFLOW = 1,
+ OVERFLOW = 2,
+ };
+
+ using UnsignedInt = Integral;
+ using SignedInt = std::make_signed_t<UnsignedInt>;
+
+ explicit ExtendedAccumulator(AccumulatingType initial = 0) : mAccumulated(initial) {}
+
+ // Returns a pair of the calculated change on the accumulating value, and a
+ // Wrap type representing the type of wraparound (if any) which occurred.
+ std::pair<SignedInt, Wrap> poll(UnsignedInt value) {
+ auto acc = mAccumulated.load(std::memory_order_relaxed);
+ const auto bottom_bits = static_cast<UnsignedInt>(acc);
+ std::pair<SignedInt, Wrap> res = {0, Wrap::NORMAL};
+ const bool overflow = __builtin_sub_overflow(value, bottom_bits, &res.first);
+
+ if (overflow) {
+ res.second = (res.first > 0) ? Wrap::OVERFLOW : Wrap::UNDERFLOW;
+ }
+
+ const bool acc_overflow = __builtin_add_overflow(acc, res.first, &acc);
+ // If our *accumulating* type overflows or underflows (depending on its
+ // signedness), we should abort.
+ if (acc_overflow) LOG_ALWAYS_FATAL("Unexpected overflow/underflow in %s", __func__);
+
+ mAccumulated.store(acc, std::memory_order_relaxed);
+ return res;
+ }
+
+ AccumulatingType getValue() const { return mAccumulated.load(std::memory_order_relaxed); }
+
+ private:
+ // Invariant - the bottom underlying bits of accumulated are the same as the
+ // last value provided to poll.
+ std::atomic<AccumulatingType> mAccumulated;
+};
+
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/ServiceUtilities.h b/media/utils/include/mediautils/ServiceUtilities.h
index de20d55..3d7981a 100644
--- a/media/utils/include/mediautils/ServiceUtilities.h
+++ b/media/utils/include/mediautils/ServiceUtilities.h
@@ -130,7 +130,7 @@
std::optional<bool> doIsAllowed(uid_t uid);
sp<content::pm::IPackageManagerNative> retrievePackageManager();
sp<content::pm::IPackageManagerNative> mPackageManager; // To check apps manifest
- uint_t mPackageManagerErrors = 0;
+ unsigned int mPackageManagerErrors = 0;
struct Package {
std::string name;
bool playbackCaptureAllowed = false;
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 1024018..f0e3c1e 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -7,78 +7,106 @@
default_applicable_licenses: ["frameworks_av_license"],
}
-cc_test_library {
- name: "libsharedtest",
+// general test config
+cc_defaults {
+ name: "libmediautils_tests_config",
+
+ host_supported: true,
+
cflags: [
"-Wall",
"-Werror",
"-Wextra",
],
- sanitize:{
- address: true,
- cfi: true,
- integer_overflow: true,
- memtag_heap: true,
+ sanitize: {
+ address: true,
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
},
+}
+
+cc_defaults {
+ name: "libmediautils_tests_defaults",
+
+ defaults: ["libmediautils_tests_config"],
+
+ host_supported: true,
shared_libs: [
"liblog",
+ "libutils",
],
+ static_libs: [
+ "libmediautils_core",
+ ],
+}
+
+cc_defaults {
+ name: "libmediautils_tests_host_unavail",
+
+ defaults: ["libmediautils_tests_config"],
+
+ host_supported: false,
+
+ shared_libs: [
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+}
+
+cc_test_library {
+ name: "libsharedtest",
+
+ defaults: ["libmediautils_tests_defaults"],
+
srcs: [
"sharedtest.cpp",
- ]
+ ],
}
cc_test {
name: "library_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- sanitize:{
- address: true,
- cfi: true,
- integer_overflow: true,
- memtag_heap: true,
- },
-
- shared_libs: [
- "libbase",
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_defaults"],
data_libs: [
"libsharedtest",
],
+ shared_libs: [
+ "libbase",
+ ],
+
srcs: [
"library_tests.cpp",
],
}
cc_test {
- name: "media_process_tests",
+ name: "libmediautils_test",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
shared_libs: [
- "liblog",
"libmediautils",
- "libutils",
],
srcs: [
+ "memory-test.cpp",
+ "TimerThread-test.cpp",
+ ],
+}
+
+cc_test {
+ name: "media_process_tests",
+
+ defaults: ["libmediautils_tests_host_unavail"],
+
+ srcs: [
"media_process_tests.cpp",
],
}
@@ -86,17 +114,7 @@
cc_test {
name: "media_synchronization_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- shared_libs: [
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
srcs: [
"media_synchronization_tests.cpp",
@@ -106,17 +124,7 @@
cc_test {
name: "media_threadsnapshot_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- shared_libs: [
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
srcs: [
"media_threadsnapshot_tests.cpp",
@@ -126,17 +134,10 @@
cc_test {
name: "mediautils_scopedstatistics_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libmediautils_tests_defaults"],
shared_libs: [
"libaudioutils",
- "liblog",
- "libmediautils",
- "libutils",
],
srcs: [
@@ -147,17 +148,10 @@
cc_test {
name: "methodstatistics_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libmediautils_tests_defaults"],
shared_libs: [
"libaudioutils",
- "liblog",
- "libmediautils",
- "libutils",
],
srcs: [
@@ -168,26 +162,19 @@
cc_test {
name: "timecheck_tests",
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
-
- sanitize:{
- address: true,
- cfi: true,
- integer_overflow: true,
- memtag_heap: true,
- },
-
- shared_libs: [
- "liblog",
- "libmediautils",
- "libutils",
- ],
+ defaults: ["libmediautils_tests_host_unavail"],
srcs: [
"timecheck_tests.cpp",
],
}
+
+cc_test {
+ name: "extended_accumulator_tests",
+
+ defaults: ["libmediautils_tests_defaults"],
+
+ srcs: [
+ "extended_accumulator_tests.cpp",
+ ],
+}
diff --git a/media/utils/TimerThread-test.cpp b/media/utils/tests/TimerThread-test.cpp
similarity index 70%
rename from media/utils/TimerThread-test.cpp
rename to media/utils/tests/TimerThread-test.cpp
index 93cd64c..1fbe894 100644
--- a/media/utils/TimerThread-test.cpp
+++ b/media/utils/tests/TimerThread-test.cpp
@@ -40,8 +40,10 @@
std::this_thread::sleep_for(100ms - kJitter);
ASSERT_FALSE(taskRan);
std::this_thread::sleep_for(2 * kJitter);
- ASSERT_TRUE(taskRan);
- ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_TRUE(taskRan); // timed-out called.
+ ASSERT_EQ(1ul, countChars(thread.timeoutToString(), REQUEST_START));
+ // nothing cancelled
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, Cancel) {
@@ -53,8 +55,10 @@
ASSERT_FALSE(taskRan);
ASSERT_TRUE(thread.cancelTask(handle));
std::this_thread::sleep_for(2 * kJitter);
- ASSERT_FALSE(taskRan);
- ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_FALSE(taskRan); // timed-out did not call.
+ ASSERT_EQ(0ul, countChars(thread.timeoutToString(), REQUEST_START));
+ // task cancelled.
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, CancelAfterRun) {
@@ -63,9 +67,11 @@
TimerThread::Handle handle =
thread.scheduleTask("CancelAfterRun", [&taskRan] { taskRan = true; }, 100ms);
std::this_thread::sleep_for(100ms + kJitter);
- ASSERT_TRUE(taskRan);
+ ASSERT_TRUE(taskRan); // timed-out called.
ASSERT_FALSE(thread.cancelTask(handle));
- ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.timeoutToString(), REQUEST_START));
+ // nothing actually cancelled
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, MultipleTasks) {
@@ -82,9 +88,9 @@
thread.scheduleTask("5", [&taskRan] { taskRan[5] = true; }, 200ms);
// 6 tasks pending
- ASSERT_EQ(6, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(6ul, countChars(thread.pendingToString(), REQUEST_START));
// 0 tasks completed
- ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
// Task 1 should trigger around 100ms.
std::this_thread::sleep_until(startTime + 100ms - kJitter);
@@ -141,9 +147,10 @@
ASSERT_TRUE(taskRan[5]);
// 1 task pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
- // 4 tasks ran and 1 cancelled
- ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
+ // 4 tasks called on timeout, and 1 cancelled
+ ASSERT_EQ(4ul, countChars(thread.timeoutToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
// Task 3 should trigger around 400ms.
std::this_thread::sleep_until(startTime + 400ms - kJitter);
@@ -154,8 +161,9 @@
ASSERT_FALSE(taskRan[4]);
ASSERT_TRUE(taskRan[5]);
- // 4 tasks ran and 1 cancelled
- ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
+ // 4 tasks called on timeout and 1 cancelled
+ ASSERT_EQ(4ul, countChars(thread.timeoutToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
std::this_thread::sleep_until(startTime + 400ms + kJitter);
ASSERT_TRUE(taskRan[0]);
@@ -166,9 +174,10 @@
ASSERT_TRUE(taskRan[5]);
// 0 tasks pending
- ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
- // 5 tasks ran and 1 cancelled
- ASSERT_EQ(5 + 1, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.pendingToString(), REQUEST_START));
+ // 5 tasks called on timeout and 1 cancelled
+ ASSERT_EQ(5ul, countChars(thread.timeoutToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, TrackedTasks) {
@@ -179,47 +188,47 @@
auto handle2 = thread.trackTask("2");
// 3 tasks pending
- ASSERT_EQ(3, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(3ul, countChars(thread.pendingToString(), REQUEST_START));
// 0 tasks retired
- ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.retiredToString(), REQUEST_START));
ASSERT_TRUE(thread.cancelTask(handle0));
ASSERT_TRUE(thread.cancelTask(handle1));
// 1 task pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
// 2 tasks retired
- ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
// handle1 is stale, cancel returns false.
ASSERT_FALSE(thread.cancelTask(handle1));
// 1 task pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
// 2 tasks retired
- ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
// Add another tracked task.
auto handle3 = thread.trackTask("3");
// 2 tasks pending
- ASSERT_EQ(2, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.pendingToString(), REQUEST_START));
// 2 tasks retired
- ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(2ul, countChars(thread.retiredToString(), REQUEST_START));
ASSERT_TRUE(thread.cancelTask(handle2));
// 1 tasks pending
- ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(1ul, countChars(thread.pendingToString(), REQUEST_START));
// 3 tasks retired
- ASSERT_EQ(3, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(3ul, countChars(thread.retiredToString(), REQUEST_START));
ASSERT_TRUE(thread.cancelTask(handle3));
// 0 tasks pending
- ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
+ ASSERT_EQ(0ul, countChars(thread.pendingToString(), REQUEST_START));
// 4 tasks retired
- ASSERT_EQ(4, countChars(thread.retiredToString(), REQUEST_START));
+ ASSERT_EQ(4ul, countChars(thread.retiredToString(), REQUEST_START));
}
} // namespace
diff --git a/media/utils/tests/extended_accumulator_tests.cpp b/media/utils/tests/extended_accumulator_tests.cpp
new file mode 100644
index 0000000..e243e7e
--- /dev/null
+++ b/media/utils/tests/extended_accumulator_tests.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 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 "extended_accumulator_tests"
+
+#include <mediautils/ExtendedAccumulator.h>
+
+#include <type_traits>
+#include <cstdint>
+#include <limits.h>
+
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android;
+using namespace android::mediautils;
+
+// Conditionally choose a base accumulating counter value in order to prevent
+// unsigned underflow on the accumulator from aborting the tests.
+template <typename TType, typename CType>
+static constexpr CType getBase() {
+ static_assert(sizeof(TType) < sizeof(CType));
+ if constexpr (std::is_unsigned_v<CType>) {
+ return std::numeric_limits<TType>::max() + 1;
+ } else {
+ return 0;
+ }
+}
+
+// Since the entire state of this utility is the previous value, and the
+// behavior is isomorphic mod the underlying type on the previous value, we can
+// test combinations of the previous value of the underlying type and a
+// hypothetical signed update to that type and ensure the accumulator moves
+// correctly and reports overflow correctly.
+template <typename TestUInt, typename CType>
+void testPair(TestUInt prevVal, std::make_signed_t<TestUInt> delta) {
+ using TestDetect = ExtendedAccumulator<TestUInt, CType>;
+ using TestInt = typename TestDetect::SignedInt;
+ static_assert(std::is_same_v<typename TestDetect::UnsignedInt, TestUInt>);
+ static_assert(std::is_same_v<TestInt, std::make_signed_t<TestUInt>>);
+ static_assert(sizeof(TestUInt) < sizeof(CType));
+
+ // To safely detect underflow/overflow for testing
+ // Should be 0 mod TestUInt, max + 1 is convenient
+ static constexpr CType base = getBase<TestUInt, CType>();
+ const CType prev = base + prevVal;
+ TestDetect test{prev};
+ EXPECT_EQ(test.getValue(), prev);
+ // Prevent unsigned wraparound abort
+ CType next;
+ const auto err = __builtin_add_overflow(prev, delta, &next);
+ LOG_ALWAYS_FATAL_IF(err, "Unexpected wrap in tests");
+ const auto [result, status] = test.poll(static_cast<TestUInt>(next));
+ EXPECT_EQ(test.getValue(), next);
+ EXPECT_EQ(result, delta);
+
+ // Test overflow/underflow event reporting.
+ if (next < base) EXPECT_EQ(TestDetect::Wrap::UNDERFLOW, status);
+ else if (next > base + std::numeric_limits<TestUInt>::max())
+ EXPECT_EQ(TestDetect::Wrap::OVERFLOW, status);
+ else EXPECT_EQ(TestDetect::Wrap::NORMAL, status);
+}
+
+// Test this utility on every combination of prior and update value for the
+// type uint8_t, with an unsigned containing type.
+TEST(wraparound_tests, cover_u8_u64) {
+ using TType = uint8_t;
+ using CType = uint64_t;
+ static constexpr CType max = std::numeric_limits<TType>::max();
+ for (CType i = 0; i <= max; i++) {
+ for (CType j = 0; j <= max; j++) {
+ testPair<TType, CType>(i, static_cast<int64_t>(j));
+ }
+ }
+}
+
+// Test this utility on every combination of prior and update value for the
+// type uint8_t, with a signed containing type.
+TEST(wraparound_tests, cover_u8_s64) {
+ using TType = uint8_t;
+ using CType = int64_t;
+ static constexpr CType max = std::numeric_limits<TType>::max();
+ for (CType i = 0; i <= max; i++) {
+ for (CType j = 0; j <= max; j++) {
+ testPair<TType, CType>(i, static_cast<int64_t>(j));
+ }
+ }
+}
diff --git a/media/utils/tests/media_process_tests.cpp b/media/utils/tests/media_process_tests.cpp
index 2ae3f70..6e738b1 100644
--- a/media/utils/tests/media_process_tests.cpp
+++ b/media/utils/tests/media_process_tests.cpp
@@ -24,6 +24,14 @@
using namespace android;
using namespace android::mediautils;
+// Disables false-positives from base::Split()
+//
+// See mismatched sanitized libraries here:
+// https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
+extern "C" const char* __asan_default_options() {
+ return "detect_container_overflow=0";
+}
+
TEST(media_process_tests, basic) {
const std::string schedString = getThreadSchedAsString(gettid());
diff --git a/media/utils/tests/media_threadsnapshot_tests.cpp b/media/utils/tests/media_threadsnapshot_tests.cpp
index c7a45e2..fc9aeab 100644
--- a/media/utils/tests/media_threadsnapshot_tests.cpp
+++ b/media/utils/tests/media_threadsnapshot_tests.cpp
@@ -27,6 +27,14 @@
using namespace android;
using namespace android::mediautils;
+// Disables false-positives from base::Split()
+//
+// See mismatched sanitized libraries here:
+// https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow
+extern "C" const char* __asan_default_options() {
+ return "detect_container_overflow=0";
+}
+
TEST(media_threadsnapshot_tests, basic) {
using namespace std::chrono_literals;
diff --git a/media/utils/memory-test.cpp b/media/utils/tests/memory-test.cpp
similarity index 100%
rename from media/utils/memory-test.cpp
rename to media/utils/tests/memory-test.cpp
diff --git a/services/audioflinger/Android.bp b/services/audioflinger/Android.bp
index 763c070..a08879e 100644
--- a/services/audioflinger/Android.bp
+++ b/services/audioflinger/Android.bp
@@ -78,7 +78,6 @@
"libnblog",
"libpermission",
"libpowermanager",
- "libmediautils",
"libmemunreachable",
"libmedia_helper",
"libshmemcompat",
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8ae06d0..7a9027c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -657,6 +657,7 @@
void AudioFlinger::onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration) {
sp<os::IExternalVibratorService> evs = getExternalVibratorService();
if (evs != 0) {
+ ALOGD("%s, stopping external vibration", __func__);
evs->onExternalVibrationStop(*externalVibration);
}
}
@@ -768,6 +769,11 @@
(uint32_t)(mStandbyTimeInNsecs / 1000000));
result.append(buffer);
write(fd, result.string(), result.size());
+
+ dprintf(fd, "Vibrator infos(size=%zu):\n", mAudioVibratorInfos.size());
+ for (const auto& vibratorInfo : mAudioVibratorInfos) {
+ dprintf(fd, " - %s\n", vibratorInfo.toString().c_str());
+ }
}
void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args __unused)
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index e6d7cf7..72c378d 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -569,7 +569,8 @@
mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
mDisableWaitCnt(0), // set by process() and updateState()
mOffloaded(false),
- mAddedToHal(false)
+ mAddedToHal(false),
+ mIsOutput(false)
#ifdef FLOAT_EFFECT_CHAIN
, mSupportsFloat(false)
#endif
@@ -962,6 +963,7 @@
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.inputCfg.buffer.frameCount = callback->frameCount();
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
+ mIsOutput = callback->isOutput();
ALOGV("configure() %p chain %p buffer %p framecount %zu",
this, callback->chain().promote().get(),
@@ -980,7 +982,7 @@
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
- callback->isOutput() &&
+ mIsOutput &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
|| mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
// Older effects may require exact STEREO position mask.
@@ -1643,6 +1645,22 @@
return status;
}
+status_t AudioFlinger::EffectModule::getConfigs(
+ audio_config_base_t* inputCfg, audio_config_base_t* outputCfg, bool* isOutput) const {
+ Mutex::Autolock _l(mLock);
+ if (mConfig.inputCfg.mask == 0 || mConfig.outputCfg.mask == 0) {
+ return NO_INIT;
+ }
+ inputCfg->sample_rate = mConfig.inputCfg.samplingRate;
+ inputCfg->channel_mask = static_cast<audio_channel_mask_t>(mConfig.inputCfg.channels);
+ inputCfg->format = static_cast<audio_format_t>(mConfig.inputCfg.format);
+ outputCfg->sample_rate = mConfig.outputCfg.samplingRate;
+ outputCfg->channel_mask = static_cast<audio_channel_mask_t>(mConfig.outputCfg.channels);
+ outputCfg->format = static_cast<audio_format_t>(mConfig.outputCfg.format);
+ *isOutput = mIsOutput;
+ return NO_ERROR;
+}
+
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
@@ -1760,6 +1778,7 @@
BINDER_METHOD_ENTRY(command) \
BINDER_METHOD_ENTRY(disconnect) \
BINDER_METHOD_ENTRY(getCblk) \
+BINDER_METHOD_ENTRY(getConfig) \
// singleton for Binder Method Statistics for IEffect
mediautils::MethodStatistics<int>& getIEffectStatistics() {
@@ -1803,6 +1822,13 @@
*_aidl_return = (code); \
return Status::ok();
+#define VALUE_OR_RETURN_STATUS_AS_OUT(exp) \
+ ({ \
+ auto _tmp = (exp); \
+ if (!_tmp.ok()) { RETURN(_tmp.error()); } \
+ std::move(_tmp.value()); \
+ })
+
Status AudioFlinger::EffectHandle::enable(int32_t* _aidl_return)
{
AutoMutex _l(mLock);
@@ -1914,6 +1940,32 @@
return Status::ok();
}
+Status AudioFlinger::EffectHandle::getConfig(
+ media::EffectConfig* _config, int32_t* _aidl_return) {
+ AutoMutex _l(mLock);
+ sp<EffectBase> effect = mEffect.promote();
+ if (effect == nullptr || mDisconnected) {
+ RETURN(DEAD_OBJECT);
+ }
+ sp<EffectModule> effectModule = effect->asEffectModule();
+ if (effectModule == nullptr) {
+ RETURN(INVALID_OPERATION);
+ }
+ audio_config_base_t inputCfg = AUDIO_CONFIG_BASE_INITIALIZER;
+ audio_config_base_t outputCfg = AUDIO_CONFIG_BASE_INITIALIZER;
+ bool isOutput;
+ status_t status = effectModule->getConfigs(&inputCfg, &outputCfg, &isOutput);
+ if (status == NO_ERROR) {
+ constexpr bool isInput = false; // effects always use 'OUT' channel masks.
+ _config->inputCfg = VALUE_OR_RETURN_STATUS_AS_OUT(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(inputCfg, isInput));
+ _config->outputCfg = VALUE_OR_RETURN_STATUS_AS_OUT(
+ legacy2aidl_audio_config_base_t_AudioConfigBase(outputCfg, isInput));
+ _config->isOnInputStream = !isOutput;
+ }
+ RETURN(status);
+}
+
Status AudioFlinger::EffectHandle::command(int32_t cmdCode,
const std::vector<uint8_t>& cmdData,
int32_t maxResponseSize,
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 42614cc..a89a814 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -283,6 +283,10 @@
status_t setHapticIntensity(int id, int intensity);
status_t setVibratorInfo(const media::AudioVibratorInfo& vibratorInfo);
+ status_t getConfigs(audio_config_base_t* inputCfg,
+ audio_config_base_t* outputCfg,
+ bool* isOutput) const;
+
void dump(int fd, const Vector<String16>& args);
private:
@@ -314,6 +318,7 @@
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mOffloaded; // effect is currently offloaded to the audio DSP
bool mAddedToHal; // effect has been added to the audio HAL
+ bool mIsOutput; // direction of the AF thread
#ifdef FLOAT_EFFECT_CHAIN
bool mSupportsFloat; // effect supports float processing
@@ -370,6 +375,8 @@
int32_t* _aidl_return) override;
android::binder::Status disconnect() override;
android::binder::Status getCblk(media::SharedFileRegion* _aidl_return) override;
+ android::binder::Status getConfig(media::EffectConfig* _config,
+ int32_t* _aidl_return) override;
sp<Client> client() const { return mClient; }
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c77d662..3ac8670 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -9671,6 +9671,12 @@
if (isOutput()) {
ret = AudioSystem::startOutput(portId);
} else {
+ {
+ // Add the track record before starting input so that the silent status for the
+ // client can be cached.
+ Mutex::Autolock _l(mLock);
+ setClientSilencedState_l(portId, false /*silenced*/);
+ }
ret = AudioSystem::startInput(portId);
}
@@ -9689,6 +9695,7 @@
} else {
mHalStream->stop();
}
+ eraseClientSilencedState_l(portId);
return PERMISSION_DENIED;
}
@@ -9697,6 +9704,9 @@
mChannelMask, mSessionId, isOutput(),
client.attributionSource,
IPCThreadState::self()->getCallingPid(), portId);
+ if (!isOutput()) {
+ track->setSilenced_l(isClientSilenced_l(portId));
+ }
if (isOutput()) {
// force volume update when a new track is added
@@ -9754,6 +9764,7 @@
}
mActiveTracks.remove(track);
+ eraseClientSilencedState_l(track->portId());
mLock.unlock();
if (isOutput()) {
@@ -10359,20 +10370,10 @@
} else {
sp<MmapStreamCallback> callback = mCallback.promote();
if (callback != 0) {
- int channelCount;
- if (isOutput()) {
- channelCount = audio_channel_count_from_out_mask(mChannelMask);
- } else {
- channelCount = audio_channel_count_from_in_mask(mChannelMask);
- }
- Vector<float> values;
- for (int i = 0; i < channelCount; i++) {
- values.add(volume);
- }
mHalVolFloat = volume; // SW volume control worked, so update value.
mNoCallbackWarningCount = 0;
mLock.unlock();
- callback->onVolumeChanged(mChannelMask, values);
+ callback->onVolumeChanged(volume);
mLock.lock();
} else {
if (mNoCallbackWarningCount < kMaxNoCallbackWarnings) {
@@ -10544,6 +10545,7 @@
broadcast_l();
}
}
+ setClientSilencedIfExists_l(portId, silenced);
}
void AudioFlinger::MmapCaptureThread::toAudioPortConfig(struct audio_port_config *config)
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a401048..2f85378 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -2105,6 +2105,26 @@
virtual bool isStreamInitialized() { return false; }
+ void setClientSilencedState_l(audio_port_handle_t portId, bool silenced) {
+ mClientSilencedStates[portId] = silenced;
+ }
+
+ size_t eraseClientSilencedState_l(audio_port_handle_t portId) {
+ return mClientSilencedStates.erase(portId);
+ }
+
+ bool isClientSilenced_l(audio_port_handle_t portId) const {
+ const auto it = mClientSilencedStates.find(portId);
+ return it != mClientSilencedStates.end() ? it->second : false;
+ }
+
+ void setClientSilencedIfExists_l(audio_port_handle_t portId, bool silenced) {
+ const auto it = mClientSilencedStates.find(portId);
+ if (it != mClientSilencedStates.end()) {
+ it->second = silenced;
+ }
+ }
+
protected:
void dumpInternals_l(int fd, const Vector<String16>& args) override;
void dumpTracks_l(int fd, const Vector<String16>& args) override;
@@ -2124,6 +2144,7 @@
AudioHwDevice* const mAudioHwDev;
ActiveTracks<MmapTrack> mActiveTracks;
float mHalVolFloat;
+ std::map<audio_port_handle_t, bool> mClientSilencedStates;
int32_t mNoCallbackWarningCount;
static constexpr int32_t kMaxNoCallbackWarnings = 5;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b422bb2..2dfa1b2 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1918,6 +1918,7 @@
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& playbackThread->mHapticChannelCount > 0) {
+ ALOGD("%s, haptic playback was muted for track %d", __func__, mTrack->id());
mTrack->setHapticPlaybackEnabled(false);
*ret = true;
}
@@ -1935,6 +1936,7 @@
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& playbackThread->mHapticChannelCount > 0) {
+ ALOGD("%s, haptic playback was unmuted for track %d", __func__, mTrack->id());
mTrack->setHapticPlaybackEnabled(true);
*ret = true;
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 6eccc9f..0135183 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -140,8 +140,6 @@
status_t AudioPolicyManager::setDeviceConnectionStateInt(
audio_policy_dev_state_t state, const android::media::audio::common::AudioPort& port,
audio_format_t encodedFormat) {
- // TODO: b/211601178 Forward 'port' to Audio HAL via mHwModules. For now, only device_type,
- // device_address and device_name are forwarded.
if (port.ext.getTag() != AudioPortExt::device) {
return BAD_VALUE;
}
@@ -160,7 +158,13 @@
sp<DeviceDescriptor> device = mHwModules.getDeviceDescriptor(
device_type, device_address.c_str(), device_name, encodedFormat,
state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
- return device ? setDeviceConnectionStateInt(device, state) : INVALID_OPERATION;
+ if (device == nullptr) {
+ return INVALID_OPERATION;
+ }
+ if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+ device->setExtraAudioDescriptors(port.extraAudioDescriptors);
+ }
+ return setDeviceConnectionStateInt(device, state);
}
status_t AudioPolicyManager::setDeviceConnectionStateInt(audio_devices_t deviceType,
@@ -2595,31 +2599,19 @@
flags = (audio_input_flags_t)(flags | AUDIO_INPUT_FLAG_ULTRASOUND);
}
- // find a compatible input profile (not necessarily identical in parameters)
- sp<IOProfile> profile;
// sampling rate and flags may be updated by getInputProfile
uint32_t profileSamplingRate = (config->sample_rate == 0) ?
SAMPLE_RATE_HZ_DEFAULT : config->sample_rate;
- audio_format_t profileFormat;
+ audio_format_t profileFormat = config->format;
audio_channel_mask_t profileChannelMask = config->channel_mask;
audio_input_flags_t profileFlags = flags;
- for (;;) {
- profileFormat = config->format; // reset each time through loop, in case it is updated
- profile = getInputProfile(device, profileSamplingRate, profileFormat, profileChannelMask,
- profileFlags);
- if (profile != 0) {
- break; // success
- } else if (profileFlags & AUDIO_INPUT_FLAG_RAW) {
- profileFlags = (audio_input_flags_t) (profileFlags & ~AUDIO_INPUT_FLAG_RAW); // retry
- } else if (profileFlags != AUDIO_INPUT_FLAG_NONE && audio_is_linear_pcm(config->format)) {
- profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
- } else { // fail
- ALOGW("%s could not find profile for device %s, sampling rate %u, format %#x, "
- "channel mask 0x%X, flags %#x", __func__, device->toString().c_str(),
- config->sample_rate, config->format, config->channel_mask, flags);
- return input;
- }
+ // find a compatible input profile (not necessarily identical in parameters)
+ sp<IOProfile> profile = getInputProfile(
+ device, profileSamplingRate, profileFormat, profileChannelMask, profileFlags);
+ if (profile == nullptr) {
+ return input;
}
+
// Pick input sampling rate if not specified by client
uint32_t samplingRate = config->sample_rate;
if (samplingRate == 0) {
@@ -4527,7 +4519,11 @@
audio_attributes_t resultAttr;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = sourceDesc->config().sample_rate;
- config.channel_mask = sourceDesc->config().channel_mask;
+ audio_channel_mask_t sourceMask = sourceDesc->config().channel_mask;
+ config.channel_mask =
+ (audio_channel_mask_get_representation(sourceMask)
+ == AUDIO_CHANNEL_REPRESENTATION_INDEX) ? sourceMask
+ : audio_channel_mask_in_to_out(sourceMask);
config.format = sourceDesc->config().format;
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
@@ -7090,51 +7086,68 @@
{
// Choose an input profile based on the requested capture parameters: select the first available
// profile supporting all requested parameters.
+ // The flags can be ignored if it doesn't contain a much match flag.
//
// TODO: perhaps isCompatibleProfile should return a "matching" score so we can return
// the best matching profile, not the first one.
- sp<IOProfile> firstInexact;
- uint32_t updatedSamplingRate = 0;
- audio_format_t updatedFormat = AUDIO_FORMAT_INVALID;
- audio_channel_mask_t updatedChannelMask = AUDIO_CHANNEL_INVALID;
- for (const auto& hwModule : mHwModules) {
- for (const auto& profile : hwModule->getInputProfiles()) {
- // profile->log();
- //updatedFormat = format;
- if (profile->isCompatibleProfile(DeviceVector(device), samplingRate,
- &samplingRate /*updatedSamplingRate*/,
- format,
- &format, /*updatedFormat*/
- channelMask,
- &channelMask /*updatedChannelMask*/,
- // FIXME ugly cast
- (audio_output_flags_t) flags,
- true /*exactMatchRequiredForInputFlags*/)) {
- return profile;
- }
- if (firstInexact == nullptr && profile->isCompatibleProfile(DeviceVector(device),
- samplingRate,
- &updatedSamplingRate,
- format,
- &updatedFormat,
- channelMask,
- &updatedChannelMask,
- // FIXME ugly cast
- (audio_output_flags_t) flags,
- false /*exactMatchRequiredForInputFlags*/)) {
- firstInexact = profile;
- }
+ const audio_input_flags_t mustMatchFlag = AUDIO_INPUT_FLAG_MMAP_NOIRQ;
+ const audio_input_flags_t oriFlags = flags;
+ for (;;) {
+ sp<IOProfile> firstInexact = nullptr;
+ uint32_t updatedSamplingRate = 0;
+ audio_format_t updatedFormat = AUDIO_FORMAT_INVALID;
+ audio_channel_mask_t updatedChannelMask = AUDIO_CHANNEL_INVALID;
+ for (const auto& hwModule : mHwModules) {
+ for (const auto& profile : hwModule->getInputProfiles()) {
+ // profile->log();
+ //updatedFormat = format;
+ if (profile->isCompatibleProfile(DeviceVector(device), samplingRate,
+ &samplingRate /*updatedSamplingRate*/,
+ format,
+ &format, /*updatedFormat*/
+ channelMask,
+ &channelMask /*updatedChannelMask*/,
+ // FIXME ugly cast
+ (audio_output_flags_t) flags,
+ true /*exactMatchRequiredForInputFlags*/)) {
+ return profile;
+ }
+ if (firstInexact == nullptr && profile->isCompatibleProfile(DeviceVector(device),
+ samplingRate,
+ &updatedSamplingRate,
+ format,
+ &updatedFormat,
+ channelMask,
+ &updatedChannelMask,
+ // FIXME ugly cast
+ (audio_output_flags_t) flags,
+ false /*exactMatchRequiredForInputFlags*/)) {
+ firstInexact = profile;
+ }
+ }
+ }
+
+ if (firstInexact != nullptr) {
+ samplingRate = updatedSamplingRate;
+ format = updatedFormat;
+ channelMask = updatedChannelMask;
+ return firstInexact;
+ } else if (flags & AUDIO_INPUT_FLAG_RAW) {
+ flags = (audio_input_flags_t) (flags & ~AUDIO_INPUT_FLAG_RAW); // retry
+ } else if ((flags & mustMatchFlag) == AUDIO_INPUT_FLAG_NONE &&
+ flags != AUDIO_INPUT_FLAG_NONE && audio_is_linear_pcm(format)) {
+ flags = AUDIO_INPUT_FLAG_NONE;
+ } else { // fail
+ ALOGW("%s could not find profile for device %s, sampling rate %u, format %#x, "
+ "channel mask 0x%X, flags %#x", __func__, device->toString().c_str(),
+ samplingRate, format, channelMask, oriFlags);
+ break;
}
}
- if (firstInexact != nullptr) {
- samplingRate = updatedSamplingRate;
- format = updatedFormat;
- channelMask = updatedChannelMask;
- return firstInexact;
- }
- return NULL;
+
+ return nullptr;
}
float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 70fdfcb..c7a60c2 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -127,7 +127,8 @@
attributionSource.packageName = "android";
attributionSource.token = sp<BBinder>::make();
sp<AudioEffect> fx = new AudioEffect(attributionSource);
- fx->set(NULL, &effect->mUuid, -1, 0, 0, audioSession, input);
+ fx->set(nullptr /*type */, &effect->mUuid, -1 /* priority */, nullptr /* callback */,
+ audioSession, input);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGW("addInputEffects(): failed to create Fx %s on source %d",
@@ -279,7 +280,8 @@
attributionSource.packageName = "android";
attributionSource.token = sp<BBinder>::make();
sp<AudioEffect> fx = new AudioEffect(attributionSource);
- fx->set(NULL, &effect->mUuid, 0, 0, 0, audioSession, output);
+ fx->set(nullptr /* type */, &effect->mUuid, 0 /* priority */, nullptr /* callback */,
+ audioSession, output);
status_t status = fx->initCheck();
if (status != NO_ERROR && status != ALREADY_EXISTS) {
ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d",
@@ -984,8 +986,8 @@
attributionSource.packageName = "android";
attributionSource.token = sp<BBinder>::make();
sp<AudioEffect> fx = new AudioEffect(attributionSource);
- fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0, nullptr,
- nullptr, AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
+ fx->set(EFFECT_UUID_NULL, &effectDesc->mUuid, 0 /* priority */, nullptr /* callback */,
+ AUDIO_SESSION_DEVICE, AUDIO_IO_HANDLE_NONE,
AudioDeviceTypeAddr{deviceEffects->getDeviceType(),
deviceEffects->getDeviceAddress()});
status_t status = fx->initCheck();
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index e8b599a..5335efa 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -720,9 +720,10 @@
// create FX instance on output
AttributionSourceState attributionSource = AttributionSourceState();
mEngine = new AudioEffect(attributionSource);
- mEngine->set(nullptr, &mEngineDescriptor.uuid, 0, Spatializer::engineCallback /* cbf */,
- this /* user */, AUDIO_SESSION_OUTPUT_STAGE, output, {} /* device */,
- false /* probe */, true /* notifyFramesProcessed */);
+ mEngine->set(nullptr /* type */, &mEngineDescriptor.uuid, 0 /* priority */,
+ wp<AudioEffect::IAudioEffectCallback>::fromExisting(this),
+ AUDIO_SESSION_OUTPUT_STAGE, output, {} /* device */, false /* probe */,
+ true /* notifyFramesProcessed */);
status_t status = mEngine->initCheck();
ALOGV("%s mEngine create status %d", __func__, (int)status);
if (status != NO_ERROR) {
@@ -864,27 +865,10 @@
}
}
-void Spatializer::engineCallback(int32_t event, void *user, void *info) {
- if (user == nullptr) {
- return;
- }
- Spatializer* const me = reinterpret_cast<Spatializer *>(user);
- switch (event) {
- case AudioEffect::EVENT_FRAMES_PROCESSED: {
- int frames = info == nullptr ? 0 : *(int*)info;
- ALOGV("%s frames processed %d for me %p", __func__, frames, me);
- me->postFramesProcessedMsg(frames);
- } break;
- default:
- ALOGV("%s event %d", __func__, event);
- break;
- }
-}
-
-void Spatializer::postFramesProcessedMsg(int frames) {
+void Spatializer::onFramesProcessed(int32_t framesProcessed) {
sp<AMessage> msg =
new AMessage(EngineCallbackHandler::kWhatOnFramesProcessed, mHandler);
- msg->setInt32(EngineCallbackHandler::kNumFramesKey, frames);
+ msg->setInt32(EngineCallbackHandler::kNumFramesKey, framesProcessed);
msg->post();
}
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 0921a90..4aebcea 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -84,6 +84,7 @@
* spatializer mixer thread is destroyed.
*/
class Spatializer : public media::BnSpatializer,
+ public AudioEffect::IAudioEffectCallback,
public IBinder::DeathRecipient,
private SpatializerPoseController::Listener,
public virtual AudioSystem::SupportedLatencyModesCallback {
@@ -279,7 +280,7 @@
return NO_ERROR;
}
- void postFramesProcessedMsg(int frames);
+ virtual void onFramesProcessed(int32_t framesProcessed) override;
/**
* Checks if head and screen sensors must be actively monitored based on
diff --git a/services/audiopolicy/service/SpatializerPoseController.cpp b/services/audiopolicy/service/SpatializerPoseController.cpp
index 304d44a..23bcd77 100644
--- a/services/audiopolicy/service/SpatializerPoseController.cpp
+++ b/services/audiopolicy/service/SpatializerPoseController.cpp
@@ -100,7 +100,10 @@
.screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
})),
mPoseProvider(SensorPoseProvider::create("headtracker", this)),
- mThread([this, maxUpdatePeriod] {
+ mThread([this, maxUpdatePeriod] { // It's important that mThread is initialized after
+ // everything else because it runs a member
+ // function that may use any member
+ // of this class.
while (true) {
Pose3f headToStage;
std::optional<HeadTrackingMode> modeIfChanged;
diff --git a/services/audiopolicy/service/SpatializerPoseController.h b/services/audiopolicy/service/SpatializerPoseController.h
index 2c6d79a..f199ecb 100644
--- a/services/audiopolicy/service/SpatializerPoseController.h
+++ b/services/audiopolicy/service/SpatializerPoseController.h
@@ -123,12 +123,15 @@
int32_t mHeadSensor = media::SensorPoseProvider::INVALID_HANDLE;
int32_t mScreenSensor = media::SensorPoseProvider::INVALID_HANDLE;
std::optional<media::HeadTrackingMode> mActualMode;
- std::thread mThread;
std::condition_variable mCondVar;
bool mShouldCalculate = true;
bool mShouldExit = false;
bool mCalculated = false;
+ // It's important that mThread is the last variable in this class
+ // since we starts mThread in initializer list
+ std::thread mThread;
+
void onPose(int64_t timestamp, int32_t sensor, const media::Pose3f& pose,
const std::optional<media::Twist3f>& twist, bool isNewReference) override;
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index 2e220bc..e887798 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -30,6 +30,8 @@
],
static_libs: [
+ "android.media.audio.common.types-V1-cpp",
+ "audioclient-types-aidl-cpp",
"libaudiopolicycomponents",
"libgmock",
],
diff --git a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
index 057fa58..96f58d2 100644
--- a/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
+++ b/services/audiopolicy/tests/AudioPolicyManagerTestClient.h
@@ -103,8 +103,12 @@
++mAudioPortListUpdateCount;
}
- status_t setDeviceConnectedState(
- const struct audio_port_v7 *port __unused, bool connected __unused) override {
+ status_t setDeviceConnectedState(const struct audio_port_v7 *port, bool connected) override {
+ if (connected) {
+ mConnectedDevicePorts.push_back(*port);
+ } else {
+ mDisconnectedDevicePorts.push_back(*port);
+ }
return NO_ERROR;
}
@@ -150,6 +154,30 @@
return NO_ERROR;
}
+ size_t getConnectedDevicePortCount() const {
+ return mConnectedDevicePorts.size();
+ }
+
+ const struct audio_port_v7 *getLastConnectedDevicePort() const {
+ if (mConnectedDevicePorts.empty()) {
+ return nullptr;
+ }
+ auto it = --mConnectedDevicePorts.end();
+ return &(*it);
+ }
+
+ size_t getDisconnectedDevicePortCount() const {
+ return mDisconnectedDevicePorts.size();
+ }
+
+ const struct audio_port_v7 *getLastDisconnectedDevicePort() const {
+ if (mDisconnectedDevicePorts.empty()) {
+ return nullptr;
+ }
+ auto it = --mDisconnectedDevicePorts.end();
+ return &(*it);
+ }
+
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
@@ -158,6 +186,8 @@
std::set<std::string> mAllowedModuleNames;
size_t mAudioPortListUpdateCount = 0;
size_t mRoutingUpdatedUpdateCount = 0;
+ std::vector<struct audio_port_v7> mConnectedDevicePorts;
+ std::vector<struct audio_port_v7> mDisconnectedDevicePorts;
};
} // namespace android
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index 7441f20..2a7a060 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -37,6 +37,7 @@
using AudioPolicyManager::getDirectProfilesForAttributes;
using AudioPolicyManager::setDeviceConnectionState;
using AudioPolicyManager::deviceToAudioPort;
+ using AudioPolicyManager::handleDeviceConfigChange;
uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
};
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 5429176..e5bd9bc 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -919,6 +919,30 @@
EXPECT_TRUE(foundVoipTx);
}
+TEST_F(AudioPolicyManagerTestWithConfigurationFile, HandleDeviceConfigChange) {
+ {
+ const auto prevCounter = mClient->getRoutingUpdatedCounter();
+
+ EXPECT_EQ(NO_ERROR, mManager->setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ "", "", AUDIO_FORMAT_LDAC));
+ const auto currCounter = mClient->getRoutingUpdatedCounter();
+ EXPECT_GT(currCounter, prevCounter);
+ }
+ {
+ const auto prevCounter = mClient->getRoutingUpdatedCounter();
+ // Update device configuration
+ EXPECT_EQ(NO_ERROR, mManager->handleDeviceConfigChange(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+ "" /*address*/, "" /*name*/,
+ AUDIO_FORMAT_AAC));
+
+ // As mClient marks isReconfigA2dpSupported to false, device state needs to be toggled for
+ // config changes to take effect
+ const auto currCounter = mClient->getRoutingUpdatedCounter();
+ EXPECT_GT(currCounter, prevCounter);
+ }
+}
+
using PolicyMixTuple = std::tuple<audio_usage_t, audio_source_t, uint32_t>;
class AudioPolicyManagerTestDynamicPolicy : public AudioPolicyManagerTestWithConfigurationFile {
@@ -1700,6 +1724,45 @@
address.c_str(), name.c_str(), AUDIO_FORMAT_DEFAULT));
}
+android::media::audio::common::ExtraAudioDescriptor make_ExtraAudioDescriptor(
+ android::media::audio::common::AudioStandard audioStandard,
+ android::media::audio::common::AudioEncapsulationType audioEncapsulationType) {
+ android::media::audio::common::ExtraAudioDescriptor result;
+ result.standard = audioStandard;
+ result.audioDescriptor = {0xb4, 0xaf, 0x98, 0x1a};
+ result.encapsulationType = audioEncapsulationType;
+ return result;
+}
+
+TEST_P(AudioPolicyManagerTestDeviceConnection, PassingExtraAudioDescriptors) {
+ const audio_devices_t type = std::get<0>(GetParam());
+ if (!audio_device_is_digital(type)) {
+ // EADs are used only for HDMI devices.
+ GTEST_SKIP() << "Not a digital device type: " << audio_device_to_string(type);
+ }
+ const std::string name = std::get<1>(GetParam());
+ const std::string address = std::get<2>(GetParam());
+ android::media::AudioPort audioPort;
+ ASSERT_EQ(NO_ERROR,
+ mManager->deviceToAudioPort(type, address.c_str(), name.c_str(), &audioPort));
+ android::media::audio::common::AudioPort& port = audioPort.hal;
+ port.extraAudioDescriptors.push_back(make_ExtraAudioDescriptor(
+ android::media::audio::common::AudioStandard::EDID,
+ android::media::audio::common::AudioEncapsulationType::IEC61937));
+ const size_t lastConnectedDevicePortCount = mClient->getConnectedDevicePortCount();
+ const size_t lastDisconnectedDevicePortCount = mClient->getDisconnectedDevicePortCount();
+ EXPECT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_POLICY_DEVICE_STATE_AVAILABLE, port, AUDIO_FORMAT_DEFAULT));
+ EXPECT_EQ(lastConnectedDevicePortCount + 1, mClient->getConnectedDevicePortCount());
+ EXPECT_EQ(lastDisconnectedDevicePortCount, mClient->getDisconnectedDevicePortCount());
+ const audio_port_v7* devicePort = mClient->getLastConnectedDevicePort();
+ EXPECT_EQ(port.extraAudioDescriptors.size(), devicePort->num_extra_audio_descriptors);
+ EXPECT_EQ(AUDIO_STANDARD_EDID, devicePort->extra_audio_descriptors[0].standard);
+ EXPECT_EQ(AUDIO_ENCAPSULATION_TYPE_IEC61937,
+ devicePort->extra_audio_descriptors[0].encapsulation_type);
+ EXPECT_NE(0, devicePort->extra_audio_descriptors[0].descriptor[0]);
+}
+
INSTANTIATE_TEST_CASE_P(
DeviceConnectionState,
AudioPolicyManagerTestDeviceConnection,
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 5e1822a..d342aea 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -71,6 +71,9 @@
<devicePort tagName="BT SCO Headset Mic" type="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"
role="source" address="hfp_client_in">
</devicePort>
+ <devicePort tagName="BT A2DP Out" type="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP" role="sink"
+ encodedFormats="AUDIO_FORMAT_LDAC AUDIO_FORMAT_APTX AUDIO_FORMAT_APTX_HD AUDIO_FORMAT_AAC AUDIO_FORMAT_SBC">
+ </devicePort>
</devicePorts>
<routes>
<route type="mix" sink="Speaker"
@@ -85,6 +88,8 @@
sources="mixport_bt_hfp_output,voip_rx"/>
<route type="mix" sink="mixport_bt_hfp_input"
sources="BT SCO Headset Mic"/>
+ <route type="mix" sink="BT A2DP Out"
+ sources="primary output"/>
</routes>
</module>
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 80410ab..a2e7f7e 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -142,7 +142,10 @@
// Set to keep track of logged service error events.
static std::set<String8> sServiceErrorEventSet;
-CameraService::CameraService() :
+CameraService::CameraService(
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper) :
+ mCameraServiceProxyWrapper(cameraServiceProxyWrapper == nullptr ?
+ std::make_shared<CameraServiceProxyWrapper>() : cameraServiceProxyWrapper),
mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mNumberOfCameras(0),
mNumberOfCamerasWithoutSystemCamera(0),
@@ -195,7 +198,7 @@
// This needs to be last call in this function, so that it's as close to
// ServiceManager::addService() as possible.
- CameraServiceProxyWrapper::pingCameraServiceProxy();
+ mCameraServiceProxyWrapper->pingCameraServiceProxy();
ALOGI("CameraService pinged cameraservice proxy");
}
@@ -269,7 +272,10 @@
cameraId.c_str());
continue;
}
- i->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId});
+ auto ret = i->getListener()->onTorchStatusChanged(mapToInterface(status),
+ String16{cameraId});
+ i->handleBinderStatus(ret, "%s: Failed to trigger onTorchStatusChanged for %d:%d: %d",
+ __FUNCTION__, i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
}
}
@@ -538,8 +544,12 @@
id.c_str());
continue;
}
- listener->getListener()->onPhysicalCameraStatusChanged(mapToInterface(newStatus),
- id16, physicalId16);
+ auto ret = listener->getListener()->onPhysicalCameraStatusChanged(
+ mapToInterface(newStatus), id16, physicalId16);
+ listener->handleBinderStatus(ret,
+ "%s: Failed to trigger onPhysicalCameraStatusChanged for %d:%d: %d",
+ __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+ ret.exceptionCode());
}
}
}
@@ -580,8 +590,11 @@
int32_t newStrengthLevel) {
Mutex::Autolock lock(mStatusListenerLock);
for (auto& i : mListenerList) {
- i->getListener()->onTorchStrengthLevelChanged(String16{cameraId},
+ auto ret = i->getListener()->onTorchStrengthLevelChanged(String16{cameraId},
newStrengthLevel);
+ i->handleBinderStatus(ret,
+ "%s: Failed to trigger onTorchStrengthLevelChanged for %d:%d: %d", __FUNCTION__,
+ i->getListenerUid(), i->getListenerPid(), ret.exceptionCode());
}
}
@@ -973,15 +986,16 @@
}
if (effectiveApiLevel == API_1) { // Camera1 API route
sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
- *client = new Camera2Client(cameraService, tmp, packageName, featureId,
- cameraId, api1CameraId, facing, sensorOrientation, clientPid, clientUid,
- servicePid, overrideForPerfClass);
+ *client = new Camera2Client(cameraService, tmp, cameraService->mCameraServiceProxyWrapper,
+ packageName, featureId, cameraId, api1CameraId, facing, sensorOrientation,
+ clientPid, clientUid, servicePid, overrideForPerfClass);
} else { // Camera2 API route
sp<hardware::camera2::ICameraDeviceCallbacks> tmp =
static_cast<hardware::camera2::ICameraDeviceCallbacks*>(cameraCb.get());
- *client = new CameraDeviceClient(cameraService, tmp, packageName,
- systemNativeClient, featureId, cameraId, facing, sensorOrientation,
- clientPid, clientUid, servicePid, overrideForPerfClass);
+ *client = new CameraDeviceClient(cameraService, tmp,
+ cameraService->mCameraServiceProxyWrapper, packageName, systemNativeClient,
+ featureId, cameraId, facing, sensorOrientation, clientPid, clientUid, servicePid,
+ overrideForPerfClass);
}
return Status::ok();
}
@@ -1698,7 +1712,7 @@
return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
}
- if (CameraServiceProxyWrapper::isCameraDisabled()) {
+ if (mCameraServiceProxyWrapper->isCameraDisabled()) {
String8 msg =
String8::format("Camera disabled by device policy");
ALOGE("%s: %s", __FUNCTION__, msg.string());
@@ -1707,7 +1721,8 @@
// enforce system camera permissions
if (oomScoreOffset > 0 &&
- !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid())) {
+ !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid()) &&
+ !isTrustedCallingUid(CameraThreadState::getCallingUid())) {
String8 msg =
String8::format("Cannot change the priority of a client %s pid %d for "
"camera id %s without SYSTEM_CAMERA permissions",
@@ -1901,12 +1916,15 @@
}
}
+ // Enable/disable camera service watchdog
+ client->setCameraServiceWatchdog(mCameraServiceWatchdogEnabled);
+
// Set rotate-and-crop override behavior
if (mOverrideRotateAndCropMode != ANDROID_SCALER_ROTATE_AND_CROP_AUTO) {
client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
} else {
client->setRotateAndCropOverride(
- CameraServiceProxyWrapper::getRotateAndCropOverride(
+ mCameraServiceProxyWrapper->getRotateAndCropOverride(
clientPackageName, facing, multiuser_get_user_id(clientUid)));
}
@@ -1956,7 +1974,7 @@
device = client;
int32_t openLatencyMs = ns2ms(systemTime() - openTimeNs);
- CameraServiceProxyWrapper::logOpen(cameraId, facing, clientPackageName,
+ mCameraServiceProxyWrapper->logOpen(cameraId, facing, clientPackageName,
effectiveApiLevel, isNonSystemNdk, openLatencyMs);
{
@@ -2382,10 +2400,8 @@
for (const auto& it : mListenerList) {
auto ret = it->getListener()->onCameraAccessPrioritiesChanged();
- if (!ret.isOk()) {
- ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__,
- ret.exceptionCode());
- }
+ it->handleBinderStatus(ret, "%s: Failed to trigger permission callback for %d:%d: %d",
+ __FUNCTION__, it->getListenerUid(), it->getListenerPid(), ret.exceptionCode());
}
}
@@ -2444,7 +2460,7 @@
const auto basicClient = current->getValue();
if (basicClient.get() != nullptr) {
basicClient->setRotateAndCropOverride(
- CameraServiceProxyWrapper::getRotateAndCropOverride(
+ mCameraServiceProxyWrapper->getRotateAndCropOverride(
basicClient->getPackageName(),
basicClient->getCameraFacing(),
multiuser_get_user_id(basicClient->getClientUid())));
@@ -4647,8 +4663,12 @@
cameraId.c_str());
continue;
}
- listener->getListener()->onStatusChanged(mapToInterface(status),
+ auto ret = listener->getListener()->onStatusChanged(mapToInterface(status),
String16(cameraId));
+ listener->handleBinderStatus(ret,
+ "%s: Failed to trigger onStatusChanged callback for %d:%d: %d",
+ __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+ ret.exceptionCode());
}
});
}
@@ -4681,10 +4701,10 @@
} else {
ret = it->getListener()->onCameraClosed(cameraId64);
}
- if (!ret.isOk()) {
- ALOGE("%s: Failed to trigger onCameraOpened/onCameraClosed callback: %d", __FUNCTION__,
- ret.exceptionCode());
- }
+
+ it->handleBinderStatus(ret,
+ "%s: Failed to trigger onCameraOpened/onCameraClosed callback for %d:%d: %d",
+ __FUNCTION__, it->getListenerUid(), it->getListenerPid(), ret.exceptionCode());
}
}
@@ -4785,8 +4805,12 @@
String8(physicalCameraId).c_str());
continue;
}
- listener->getListener()->onPhysicalCameraStatusChanged(status,
+ auto ret = listener->getListener()->onPhysicalCameraStatusChanged(status,
logicalCameraId, physicalCameraId);
+ listener->handleBinderStatus(ret,
+ "%s: Failed to trigger onPhysicalCameraStatusChanged for %d:%d: %d",
+ __FUNCTION__, listener->getListenerUid(), listener->getListenerPid(),
+ ret.exceptionCode());
}
}
}
@@ -4842,6 +4866,8 @@
return handleSetCameraMute(args);
} else if (args.size() >= 2 && args[0] == String16("watch")) {
return handleWatchCommand(args, in, out);
+ } else if (args.size() >= 2 && args[0] == String16("set-watchdog")) {
+ return handleSetCameraServiceWatchdog(args);
} else if (args.size() == 1 && args[0] == String16("help")) {
printHelp(out);
return OK;
@@ -4935,6 +4961,28 @@
return OK;
}
+status_t CameraService::handleSetCameraServiceWatchdog(const Vector<String16>& args) {
+ int enableWatchdog = atoi(String8(args[1]));
+
+ if (enableWatchdog < 0 || enableWatchdog > 1) return BAD_VALUE;
+
+ Mutex::Autolock lock(mServiceLock);
+
+ mCameraServiceWatchdogEnabled = enableWatchdog;
+
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ basicClient->setCameraServiceWatchdog(enableWatchdog);
+ }
+ }
+ }
+
+ return OK;
+}
+
status_t CameraService::handleGetRotateAndCrop(int out) {
Mutex::Autolock lock(mServiceLock);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index d96ea00..0395475 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -48,6 +48,7 @@
#include "utils/AutoConditionLock.h"
#include "utils/ClientManager.h"
#include "utils/IPCTransport.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include <set>
#include <string>
@@ -100,7 +101,10 @@
// Implementation of BinderService<T>
static char const* getServiceName() { return "media.camera"; }
- CameraService();
+ // Non-null arguments for cameraServiceProxyWrapper should be provided for
+ // testing purposes only.
+ CameraService(std::shared_ptr<CameraServiceProxyWrapper>
+ cameraServiceProxyWrapper = nullptr);
virtual ~CameraService();
/////////////////////////////////////////////////////////////////////
@@ -339,6 +343,9 @@
// Set/reset camera mute
virtual status_t setCameraMute(bool enabled) = 0;
+ // Set Camera service watchdog
+ virtual status_t setCameraServiceWatchdog(bool enabled) = 0;
+
// The injection camera session to replace the internal camera
// session.
virtual status_t injectCamera(const String8& injectedCamId,
@@ -769,6 +776,8 @@
sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
+ std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
+
// Delay-load the Camera HAL module
virtual void onFirstRef();
@@ -1055,6 +1064,29 @@
return IInterface::asBinder(mListener)->linkToDeath(this);
}
+ template<typename... args_t>
+ void handleBinderStatus(const binder::Status &ret, const char *logOnError,
+ args_t... args) {
+ if (!ret.isOk() &&
+ (ret.exceptionCode() != binder::Status::Exception::EX_TRANSACTION_FAILED
+ || !mLastTransactFailed)) {
+ ALOGE(logOnError, args...);
+ }
+
+ // If the transaction failed, the process may have died (or other things, see
+ // b/28321379). Mute consecutive errors from this listener to avoid log spam.
+ if (ret.exceptionCode() == binder::Status::Exception::EX_TRANSACTION_FAILED) {
+ if (!mLastTransactFailed) {
+ ALOGE("%s: Muting similar errors from listener %d:%d", __FUNCTION__,
+ mListenerUid, mListenerPid);
+ }
+ mLastTransactFailed = true;
+ } else {
+ // Reset mLastTransactFailed when binder becomes healthy again.
+ mLastTransactFailed = false;
+ }
+ }
+
virtual void binderDied(const wp<IBinder> &/*who*/) {
auto parent = mParent.promote();
if (parent.get() != nullptr) {
@@ -1075,6 +1107,9 @@
int mListenerPid = -1;
bool mIsVendorListener = false;
bool mOpenCloseCallbackAllowed = false;
+
+ // Flag for preventing log spam when binder becomes unhealthy
+ bool mLastTransactFailed = false;
};
// Guarded by mStatusListenerMutex
@@ -1198,6 +1233,9 @@
// Handle 'watch' command as passed through 'cmd'
status_t handleWatchCommand(const Vector<String16> &args, int inFd, int outFd);
+ // Set the camera service watchdog
+ status_t handleSetCameraServiceWatchdog(const Vector<String16>& args);
+
// Enable tag monitoring of the given tags in provided clients
status_t startWatchingTags(const Vector<String16> &args, int outFd);
@@ -1284,6 +1322,9 @@
// Current camera mute mode
bool mOverrideCameraMuteMode = false;
+ // Camera Service watchdog flag
+ bool mCameraServiceWatchdogEnabled = true;
+
/**
* A listener class that implements the IBinder::DeathRecipient interface
* for use to call back the error state injected by the external camera, and
diff --git a/services/camera/libcameraservice/CameraServiceWatchdog.cpp b/services/camera/libcameraservice/CameraServiceWatchdog.cpp
index a169667..e101dd3 100644
--- a/services/camera/libcameraservice/CameraServiceWatchdog.cpp
+++ b/services/camera/libcameraservice/CameraServiceWatchdog.cpp
@@ -66,6 +66,17 @@
}
}
+void CameraServiceWatchdog::setEnabled(bool enable)
+{
+ AutoMutex _l(mEnabledLock);
+
+ if (enable) {
+ mEnabled = true;
+ } else {
+ mEnabled = false;
+ }
+}
+
void CameraServiceWatchdog::stop(uint32_t tid)
{
AutoMutex _l(mWatchdogLock);
diff --git a/services/camera/libcameraservice/CameraServiceWatchdog.h b/services/camera/libcameraservice/CameraServiceWatchdog.h
index 29ddab1..e35d69e 100644
--- a/services/camera/libcameraservice/CameraServiceWatchdog.h
+++ b/services/camera/libcameraservice/CameraServiceWatchdog.h
@@ -21,10 +21,12 @@
* expected duration has exceeded.
* Notes on multi-threaded behaviors:
* - The threadloop is blocked/paused when there are no calls being
- * monitored.
+ * monitored (when the TID cycle to counter map is empty).
* - The start and stop functions handle simultaneous call monitoring
* and single call monitoring differently. See function documentation for
* more details.
+ * To disable/enable:
+ * - adb shell cmd media.camera set-cameraservice-watchdog [0/1]
*/
#pragma once
#include <chrono>
@@ -49,15 +51,19 @@
public:
explicit CameraServiceWatchdog() : mPause(true), mMaxCycles(kMaxCycles),
- mCycleLengthMs(kCycleLengthMs) {};
+ mCycleLengthMs(kCycleLengthMs), mEnabled(true) {};
- explicit CameraServiceWatchdog (size_t maxCycles, uint32_t cycleLengthMs) :
- mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs) {};
+ explicit CameraServiceWatchdog (size_t maxCycles, uint32_t cycleLengthMs, bool enabled) :
+ mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs), mEnabled(enabled)
+ {};
virtual ~CameraServiceWatchdog() {};
virtual void requestExit();
+ /** Enables/disables the watchdog */
+ void setEnabled(bool enable);
+
/** Used to wrap monitored calls in start and stop functions using custom timer values */
template<typename T>
auto watchThread(T func, uint32_t tid, uint32_t cycles, uint32_t cycleLength) {
@@ -66,9 +72,20 @@
if (cycles != mMaxCycles || cycleLength != mCycleLengthMs) {
// Create another instance of the watchdog to prevent disruption
// of timer for current monitored calls
+
+ // Lock for mEnabled
+ mEnabledLock.lock();
sp<CameraServiceWatchdog> tempWatchdog =
- new CameraServiceWatchdog(cycles, cycleLength);
- tempWatchdog->run("CameraServiceWatchdog");
+ new CameraServiceWatchdog(cycles, cycleLength, mEnabled);
+ mEnabledLock.unlock();
+
+ status_t status = tempWatchdog->run("CameraServiceWatchdog");
+ if (status != OK) {
+ ALOGE("Unable to watch thread: %s (%d)", strerror(-status), status);
+ res = watchThread(func, tid);
+ return res;
+ }
+
res = tempWatchdog->watchThread(func, tid);
tempWatchdog->requestExit();
tempWatchdog.clear();
@@ -84,10 +101,16 @@
/** Used to wrap monitored calls in start and stop functions using class timer values */
template<typename T>
auto watchThread(T func, uint32_t tid) {
+ decltype(func()) res;
+ AutoMutex _l(mEnabledLock);
- start(tid);
- auto res = func();
- stop(tid);
+ if (mEnabled) {
+ start(tid);
+ res = func();
+ stop(tid);
+ } else {
+ res = func();
+ }
return res;
}
@@ -108,11 +131,13 @@
virtual bool threadLoop();
- Mutex mWatchdogLock; // Lock for condition variable
- Condition mWatchdogCondition; // Condition variable for stop/start
- bool mPause; // True if thread is currently paused
- uint32_t mMaxCycles; // Max cycles
- uint32_t mCycleLengthMs; // Length of time elapsed per cycle
+ Mutex mWatchdogLock; // Lock for condition variable
+ Mutex mEnabledLock; // Lock for enabled status
+ Condition mWatchdogCondition; // Condition variable for stop/start
+ bool mPause; // True if tid map is empty
+ uint32_t mMaxCycles; // Max cycles
+ uint32_t mCycleLengthMs; // Length of time elapsed per cycle
+ bool mEnabled; // True if watchdog is enabled
std::unordered_map<uint32_t, uint32_t> tidToCycleCounterMap; // Thread Id to cycle counter map
};
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 68dc7f8..88f27de 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -52,6 +52,7 @@
Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
const std::optional<String16>& clientFeatureId,
const String8& cameraDeviceId,
@@ -62,7 +63,7 @@
uid_t clientUid,
int servicePid,
bool overrideForPerfClass):
- Camera2ClientBase(cameraService, cameraClient, clientPackageName,
+ Camera2ClientBase(cameraService, cameraClient, cameraServiceProxyWrapper, clientPackageName,
false/*systemNativeClient - since no ndk for api1*/, clientFeatureId,
cameraDeviceId, api1CameraId, cameraFacing, sensorOrientation, clientPid,
clientUid, servicePid, overrideForPerfClass, /*legacyClient*/ true),
@@ -139,28 +140,53 @@
mFrameProcessor = new FrameProcessor(mDevice, this);
threadName = String8::format("C2-%d-FrameProc",
mCameraId);
- mFrameProcessor->run(threadName.string());
+ res = mFrameProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start frame processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mCaptureSequencer = new CaptureSequencer(this);
threadName = String8::format("C2-%d-CaptureSeq",
mCameraId);
- mCaptureSequencer->run(threadName.string());
+ res = mCaptureSequencer->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start capture sequencer thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mJpegProcessor = new JpegProcessor(this, mCaptureSequencer);
threadName = String8::format("C2-%d-JpegProc",
mCameraId);
- mJpegProcessor->run(threadName.string());
+ res = mJpegProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start jpeg processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mZslProcessor = new ZslProcessor(this, mCaptureSequencer);
threadName = String8::format("C2-%d-ZslProc",
mCameraId);
- mZslProcessor->run(threadName.string());
+ res = mZslProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start zsl processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mCallbackProcessor = new CallbackProcessor(this);
threadName = String8::format("C2-%d-CallbkProc",
mCameraId);
- mCallbackProcessor->run(threadName.string());
+ res = mCallbackProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start callback processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
if (gLogLevel >= 1) {
SharedParameters::Lock l(mParameters);
@@ -478,7 +504,7 @@
CameraService::Client::disconnect();
int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
- CameraServiceProxyWrapper::logClose(mCameraIdStr, closeLatencyMs);
+ mCameraServiceProxyWrapper->logClose(mCameraIdStr, closeLatencyMs);
return res;
}
@@ -2316,6 +2342,10 @@
return INVALID_OPERATION;
}
+status_t Camera2Client::setCameraServiceWatchdog(bool enabled) {
+ return mDevice->setCameraServiceWatchdog(enabled);
+}
+
status_t Camera2Client::setRotateAndCropOverride(uint8_t rotateAndCrop) {
if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index c8dfc46..fe91cba 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -90,12 +90,15 @@
virtual bool supportsCameraMute();
virtual status_t setCameraMute(bool enabled);
+ virtual status_t setCameraServiceWatchdog(bool enabled);
+
/**
* Interface used by CameraService
*/
Camera2Client(const sp<CameraService>& cameraService,
const sp<hardware::ICameraClient>& cameraClient,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
const std::optional<String16>& clientFeatureId,
const String8& cameraDeviceId,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 5e91501..ba26ac4 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -31,7 +31,6 @@
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "api2/CameraDeviceClient.h"
-#include "utils/CameraServiceProxyWrapper.h"
#include <camera_metadata_hidden.h>
@@ -87,6 +86,7 @@
CameraDeviceClient::CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool systemNativeClient,
const std::optional<String16>& clientFeatureId,
@@ -97,9 +97,9 @@
uid_t clientUid,
int servicePid,
bool overrideForPerfClass) :
- Camera2ClientBase(cameraService, remoteCallback, clientPackageName, systemNativeClient,
- clientFeatureId, cameraId, /*API1 camera ID*/ -1, cameraFacing, sensorOrientation,
- clientPid, clientUid, servicePid, overrideForPerfClass),
+ Camera2ClientBase(cameraService, remoteCallback, cameraServiceProxyWrapper, clientPackageName,
+ systemNativeClient, clientFeatureId, cameraId, /*API1 camera ID*/ -1, cameraFacing,
+ sensorOrientation, clientPid, clientUid, servicePid, overrideForPerfClass),
mInputStream(),
mStreamingRequestId(REQUEST_ID_NONE),
mRequestIdCounter(0),
@@ -127,7 +127,12 @@
String8 threadName;
mFrameProcessor = new FrameProcessorBase(mDevice);
threadName = String8::format("CDU-%s-FrameProc", mCameraIdStr.string());
- mFrameProcessor->run(threadName.string());
+ res = mFrameProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start frame processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
@@ -692,7 +697,7 @@
nsecs_t configureEnd = systemTime();
int32_t configureDurationMs = ns2ms(configureEnd) - startTimeMs;
- CameraServiceProxyWrapper::logStreamConfigured(mCameraIdStr, operatingMode,
+ mCameraServiceProxyWrapper->logStreamConfigured(mCameraIdStr, operatingMode,
false /*internalReconfig*/, configureDurationMs);
}
@@ -1730,6 +1735,10 @@
return binder::Status::ok();
}
+status_t CameraDeviceClient::setCameraServiceWatchdog(bool enabled) {
+ return mDevice->setCameraServiceWatchdog(enabled);
+}
+
status_t CameraDeviceClient::setRotateAndCropOverride(uint8_t rotateAndCrop) {
if (rotateAndCrop > ANDROID_SCALER_ROTATE_AND_CROP_AUTO) return BAD_VALUE;
@@ -2057,7 +2066,7 @@
Camera2ClientBase::detachDevice();
int32_t closeLatencyMs = ns2ms(systemTime() - startTime);
- CameraServiceProxyWrapper::logClose(mCameraIdStr, closeLatencyMs);
+ mCameraServiceProxyWrapper->logClose(mCameraIdStr, closeLatencyMs);
}
/** Device-related methods */
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index c5aad6b..06844c6 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -29,6 +29,7 @@
#include "common/FrameProcessorBase.h"
#include "common/Camera2ClientBase.h"
#include "CompositeStream.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include "utils/SessionConfigurationUtils.h"
using android::camera3::OutputStreamInfo;
@@ -178,6 +179,7 @@
CameraDeviceClient(const sp<CameraService>& cameraService,
const sp<hardware::camera2::ICameraDeviceCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool clientPackageOverride,
const std::optional<String16>& clientFeatureId,
@@ -206,6 +208,8 @@
virtual status_t stopWatchingTags(int out);
virtual status_t dumpWatchedEventsToVector(std::vector<std::string> &out);
+ virtual status_t setCameraServiceWatchdog(bool enabled);
+
/**
* Device listener interface
*/
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 9303fd2..4d4d481 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -44,7 +44,12 @@
String8 threadName;
mFrameProcessor = new camera2::FrameProcessorBase(mOfflineSession);
threadName = String8::format("Offline-%s-FrameProc", mCameraIdStr.string());
- mFrameProcessor->run(threadName.string());
+ res = mFrameProcessor->run(threadName.string());
+ if (res != OK) {
+ ALOGE("%s: Unable to start frame processor thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
mFrameProcessor->registerListener(camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MIN_ID,
camera2::FrameProcessorBase::FRAME_PROCESSOR_LISTENER_MAX_ID,
@@ -66,6 +71,10 @@
return OK;
}
+status_t CameraOfflineSessionClient::setCameraServiceWatchdog(bool) {
+ return OK;
+}
+
status_t CameraOfflineSessionClient::setRotateAndCropOverride(uint8_t /*rotateAndCrop*/) {
// Since we're not submitting more capture requests, changes to rotateAndCrop override
// make no difference.
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index f2c42d8..9ea1093 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -84,6 +84,8 @@
bool supportsCameraMute() override;
status_t setCameraMute(bool enabled) override;
+ status_t setCameraServiceWatchdog(bool enabled) override;
+
// permissions management
status_t startCameraOps() override;
status_t finishCameraOps() override;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index a3d24ff..6f43270 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -37,7 +37,6 @@
#include "device3/aidl/AidlCamera3Device.h"
#include "device3/hidl/HidlCamera3Device.h"
#include "utils/CameraThreadState.h"
-#include "utils/CameraServiceProxyWrapper.h"
namespace android {
@@ -51,6 +50,7 @@
Camera2ClientBase<TClientBase>::Camera2ClientBase(
const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool systemNativeClient,
const std::optional<String16>& clientFeatureId,
@@ -67,6 +67,7 @@
clientFeatureId, cameraId, api1CameraId, cameraFacing, sensorOrientation, clientPid,
clientUid, servicePid),
mSharedCameraCallbacks(remoteCallback),
+ mCameraServiceProxyWrapper(cameraServiceProxyWrapper),
mDeviceActive(false), mApi1CameraId(api1CameraId)
{
ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(),
@@ -118,13 +119,13 @@
switch (providerTransport) {
case IPCTransport::HIDL:
mDevice =
- new HidlCamera3Device(TClientBase::mCameraIdStr, mOverrideForPerfClass,
- mLegacyClient);
+ new HidlCamera3Device(mCameraServiceProxyWrapper,
+ TClientBase::mCameraIdStr, mOverrideForPerfClass, mLegacyClient);
break;
case IPCTransport::AIDL:
mDevice =
- new AidlCamera3Device(TClientBase::mCameraIdStr, mOverrideForPerfClass,
- mLegacyClient);
+ new AidlCamera3Device(mCameraServiceProxyWrapper,
+ TClientBase::mCameraIdStr, mOverrideForPerfClass, mLegacyClient);
break;
default:
ALOGE("%s Invalid transport for camera id %s", __FUNCTION__,
@@ -146,10 +147,20 @@
wp<NotificationListener> weakThis(this);
res = mDevice->setNotifyCallback(weakThis);
+ if (res != OK) {
+ ALOGE("%s: Camera %s: Unable to set notify callback: %s (%d)",
+ __FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res);
+ return res;
+ }
/** Start watchdog thread */
mCameraServiceWatchdog = new CameraServiceWatchdog();
- mCameraServiceWatchdog->run("Camera2ClientBaseWatchdog");
+ res = mCameraServiceWatchdog->run("Camera2ClientBaseWatchdog");
+ if (res != OK) {
+ ALOGE("%s: Unable to start camera service watchdog thread: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
return OK;
}
@@ -167,8 +178,8 @@
mCameraServiceWatchdog.clear();
}
- ALOGI("Closed Camera %s. Client was: %s (PID %d, UID %u)",
- TClientBase::mCameraIdStr.string(),
+ ALOGI("%s: Client object's dtor for Camera Id %s completed. Client was: %s (PID %d, UID %u)",
+ __FUNCTION__, TClientBase::mCameraIdStr.string(),
String8(TClientBase::mClientPackageName).string(),
mInitialClientPid, TClientBase::mClientUid);
}
@@ -346,7 +357,7 @@
TClientBase::mCameraIdStr.string(), res);
return res;
}
- CameraServiceProxyWrapper::logActive(TClientBase::mCameraIdStr, maxPreviewFps);
+ mCameraServiceProxyWrapper->logActive(TClientBase::mCameraIdStr, maxPreviewFps);
}
mDeviceActive = true;
@@ -365,7 +376,7 @@
ALOGE("%s: Camera %s: Error finishing streaming ops: %d", __FUNCTION__,
TClientBase::mCameraIdStr.string(), res);
}
- CameraServiceProxyWrapper::logIdle(TClientBase::mCameraIdStr,
+ mCameraServiceProxyWrapper->logIdle(TClientBase::mCameraIdStr,
requestCount, resultErrorCount, deviceError, userTag, videoStabilizationMode,
streamStats);
}
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 3af781b..6e37589 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -19,6 +19,7 @@
#include "common/CameraDeviceBase.h"
#include "camera/CaptureResult.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include "CameraServiceWatchdog.h"
namespace android {
@@ -48,6 +49,7 @@
// TODO: too many params, move into a ClientArgs<T>
Camera2ClientBase(const sp<CameraService>& cameraService,
const sp<TCamCallbacks>& remoteCallback,
+ std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
const String16& clientPackageName,
bool systemNativeClient,
const std::optional<String16>& clientFeatureId,
@@ -136,6 +138,7 @@
pid_t mInitialClientPid;
bool mOverrideForPerfClass = false;
bool mLegacyClient = false;
+ std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
virtual sp<IBinder> asBinderWrapper() {
return IInterface::asBinder(this);
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 7e2f93c..69514f3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -449,6 +449,11 @@
virtual status_t setCameraMute(bool enabled) = 0;
/**
+ * Enable/disable camera service watchdog
+ */
+ virtual status_t setCameraServiceWatchdog(bool enabled) = 0;
+
+ /**
* Get the status tracker of the camera device
*/
virtual wp<camera3::StatusTracker> getStatusTracker() = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 7c2f34f..acee2ab 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -63,7 +63,6 @@
#include "utils/CameraThreadState.h"
#include "utils/SessionConfigurationUtils.h"
#include "utils/TraceHFR.h"
-#include "utils/CameraServiceProxyWrapper.h"
#include <algorithm>
#include <tuple>
@@ -73,7 +72,9 @@
namespace android {
-Camera3Device::Camera3Device(const String8 &id, bool overrideForPerfClass, bool legacyClient):
+Camera3Device::Camera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8 &id, bool overrideForPerfClass, bool legacyClient):
+ mCameraServiceProxyWrapper(cameraServiceProxyWrapper),
mId(id),
mLegacyClient(legacyClient),
mOperatingMode(NO_MODE),
@@ -113,10 +114,6 @@
status_t Camera3Device::initializeCommonLocked() {
- /** Start watchdog thread */
- mCameraServiceWatchdog = new CameraServiceWatchdog();
- mCameraServiceWatchdog->run("CameraServiceWatchdog");
-
/** Start up status tracker thread */
mStatusTracker = new StatusTracker(this);
status_t res = mStatusTracker->run(String8::format("C3Dev-%s-Status", mId.string()).string());
@@ -230,6 +227,15 @@
// Hidl/AidlCamera3DeviceInjectionMethods
mInjectionMethods = createCamera3DeviceInjectionMethods(this);
+ /** Start watchdog thread */
+ mCameraServiceWatchdog = new CameraServiceWatchdog();
+ res = mCameraServiceWatchdog->run("CameraServiceWatchdog");
+ if (res != OK) {
+ SET_ERR_L("Unable to start camera service watchdog thread: %s (%d)",
+ strerror(-res), res);
+ return res;
+ }
+
return OK;
}
@@ -2257,7 +2263,7 @@
ALOGE("%s: Failed to pause streaming: %d", __FUNCTION__, rc);
}
- CameraServiceProxyWrapper::logStreamConfigured(mId, mOperatingMode, true /*internalReconfig*/,
+ mCameraServiceProxyWrapper->logStreamConfigured(mId, mOperatingMode, true /*internalReconfig*/,
ns2ms(systemTime() - startTime));
if (markClientActive) {
@@ -4094,6 +4100,17 @@
}
}
+status_t Camera3Device::setCameraServiceWatchdog(bool enabled) {
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ if (mCameraServiceWatchdog != NULL) {
+ mCameraServiceWatchdog->setEnabled(enabled);
+ }
+
+ return OK;
+}
+
void Camera3Device::RequestThread::cleanUpFailedRequests(bool sendRequestError) {
if (mNextRequests.empty()) {
return;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 3c60ba2..d757eb9 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -49,6 +49,7 @@
#include "utils/TagMonitor.h"
#include "utils/IPCTransport.h"
#include "utils/LatencyHistogram.h"
+#include "utils/CameraServiceProxyWrapper.h"
#include <camera_metadata_hidden.h>
using android::camera3::camera_capture_request_t;
@@ -82,7 +83,8 @@
friend class AidlCamera3Device;
public:
- explicit Camera3Device(const String8& id, bool overrideForPerfClass, bool legacyClient = false);
+ explicit Camera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient = false);
virtual ~Camera3Device();
// Delete and optionally close native handles and clear the input vector afterward
@@ -281,6 +283,11 @@
*/
status_t setCameraMute(bool enabled);
+ /**
+ * Enables/disables camera service watchdog
+ */
+ status_t setCameraServiceWatchdog(bool enabled);
+
// Get the status trackeer for the camera device
wp<camera3::StatusTracker> getStatusTracker() { return mStatusTracker; }
@@ -320,6 +327,8 @@
// Constant to use for stream ID when one doesn't exist
static const int NO_STREAM = -1;
+ std::shared_ptr<CameraServiceProxyWrapper> mCameraServiceProxyWrapper;
+
// A lock to enforce serialization on the input/configure side
// of the public interface.
// Not locked by methods guarded by mOutputLock, since they may act
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 69163a5..e12d8be 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -702,7 +702,8 @@
mTotalBufferCount ++;
res = mPreviewFrameSpacer->run(String8::format("PreviewSpacer-%d", mId).string());
if (res != OK) {
- ALOGE("%s: Unable to start preview spacer", __FUNCTION__);
+ ALOGE("%s: Unable to start preview spacer: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
return res;
}
}
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
index f05520f..973bc04 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
@@ -161,9 +161,11 @@
return (uint64_t)usage;
}
-AidlCamera3Device::AidlCamera3Device(const String8& id, bool overrideForPerfClass,
- bool legacyClient) : Camera3Device(id, overrideForPerfClass, legacyClient) {
- mCallbacks = ndk::SharedRefBase::make<AidlCameraDeviceCallbacks>(this);
+AidlCamera3Device::AidlCamera3Device(
+ std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient) :
+ Camera3Device(cameraServiceProxyWrapper, id, overrideForPerfClass, legacyClient) {
+ mCallbacks = ndk::SharedRefBase::make<AidlCameraDeviceCallbacks>(this);
}
status_t AidlCamera3Device::initialize(sp<CameraProviderManager> manager,
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h
index d20a7eb..ecf42b4 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.h
@@ -39,8 +39,9 @@
using AidlRequestMetadataQueue = AidlMessageQueue<int8_t, SynchronizedReadWrite>;
class AidlCameraDeviceCallbacks;
friend class AidlCameraDeviceCallbacks;
- explicit AidlCamera3Device(const String8& id, bool overrideForPerfClass,
- bool legacyClient = false);
+ explicit AidlCamera3Device(
+ std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient = false);
virtual ~AidlCamera3Device() { }
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h
index 2e98fe0..faac83f 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.h
@@ -31,8 +31,9 @@
public Camera3Device {
public:
- explicit HidlCamera3Device(const String8& id, bool overrideForPerfClass,
- bool legacyClient = false) : Camera3Device(id, overrideForPerfClass, legacyClient) { }
+ explicit HidlCamera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
+ const String8& id, bool overrideForPerfClass, bool legacyClient = false) :
+ Camera3Device(cameraServiceProxyWrapper, id, overrideForPerfClass, legacyClient) { }
virtual ~HidlCamera3Device() {}
diff --git a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
index d3377f4..74b3700 100644
--- a/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/hidl/VndkVersionMetadataTags.h
@@ -31,47 +31,47 @@
std::map<int, std::vector<camera_metadata_tag>> static_api_level_to_keys{
{30, {
ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_MAX_SIZES,
+ ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_ZOOM_RATIO_RANGES,
ANDROID_CONTROL_ZOOM_RATIO_RANGE,
ANDROID_SCALER_AVAILABLE_ROTATE_AND_CROP_MODES,
- ANDROID_CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_ZOOM_RATIO_RANGES,
} },
{31, {
- ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION,
- ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS,
- ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED,
- ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
ANDROID_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION,
- ANDROID_SCALER_DEFAULT_SECURE_IMAGE_SIZE,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_DEPTH_AVAILABLE_DYNAMIC_DEPTH_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
ANDROID_HEIC_AVAILABLE_HEIC_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
- ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,
+ ANDROID_HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_HEIC_AVAILABLE_HEIC_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_LENS_DISTORTION_MAXIMUM_RESOLUTION,
+ ANDROID_LENS_INTRINSIC_CALIBRATION_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_STALL_DURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_MAXIMUM_RESOLUTION,
+ ANDROID_SCALER_DEFAULT_SECURE_IMAGE_SIZE,
+ ANDROID_SCALER_MULTI_RESOLUTION_STREAM_SUPPORTED,
+ ANDROID_SCALER_PHYSICAL_CAMERA_MULTI_RESOLUTION_STREAM_CONFIGURATIONS,
+ ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
ANDROID_SENSOR_INFO_BINNING_FACTOR,
+ ANDROID_SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
+ ANDROID_SENSOR_OPAQUE_RAW_SIZE_MAXIMUM_RESOLUTION,
} },
{32, {
ANDROID_INFO_DEVICE_STATE_ORIENTATIONS,
} },
{33, {
- ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL,
ANDROID_AUTOMOTIVE_LENS_FACING,
ANDROID_AUTOMOTIVE_LOCATION,
+ ANDROID_FLASH_INFO_STRENGTH_DEFAULT_LEVEL,
+ ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL,
+ ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP,
ANDROID_REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE,
ANDROID_SCALER_AVAILABLE_STREAM_USE_CASES,
- ANDROID_REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP,
- ANDROID_FLASH_INFO_STRENGTH_MAXIMUM_LEVEL,
} },
};
@@ -81,9 +81,9 @@
*/
std::map<int, std::vector<camera_metadata_tag>> dynamic_api_level_to_keys{
{30, {
+ ANDROID_CONTROL_EXTENDED_SCENE_MODE,
ANDROID_CONTROL_ZOOM_RATIO,
ANDROID_SCALER_ROTATE_AND_CROP,
- ANDROID_CONTROL_EXTENDED_SCENE_MODE,
} },
{31, {
ANDROID_SENSOR_PIXEL_MODE,
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
index e43b91f..d909624 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/Android.bp
@@ -64,6 +64,7 @@
fuzz_config: {
cc: [
"android-media-fuzzing-reports@google.com",
+ "android-camera-fwk-eng@google.com",
],
componentid: 155276,
libfuzzer_options: [
diff --git a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
index 97d7bf4..71c26ea 100644
--- a/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
+++ b/services/camera/libcameraservice/libcameraservice_fuzzer/camera_service_fuzzer.cpp
@@ -111,12 +111,15 @@
size_t mPreviewBufferCount = 0;
bool mAutoFocusMessage = false;
bool mSnapshotNotification = false;
+ bool mRecordingNotification = false;
mutable Mutex mPreviewLock;
mutable Condition mPreviewCondition;
mutable Mutex mAutoFocusLock;
mutable Condition mAutoFocusCondition;
mutable Mutex mSnapshotLock;
mutable Condition mSnapshotCondition;
+ mutable Mutex mRecordingLock;
+ mutable Condition mRecordingCondition;
void getNumCameras();
void getCameraInformation(int32_t cameraId);
@@ -125,6 +128,7 @@
void invokeDump();
void invokeShellCommand();
void invokeNotifyCalls();
+ void invokeTorchAPIs(int32_t cameraId);
// CameraClient interface
void notifyCallback(int32_t msgType, int32_t, int32_t) override;
@@ -152,6 +156,8 @@
Mutex::Autolock l(mPreviewLock);
++mPreviewBufferCount;
mPreviewCondition.broadcast();
+ mRecordingNotification = true;
+ mRecordingCondition.broadcast();
break;
}
case CAMERA_MSG_COMPRESSED_IMAGE: {
@@ -311,114 +317,153 @@
mCameraService->notifySystemEvent(eventId, args);
}
+void CameraFuzzer::invokeTorchAPIs(int32_t cameraId) {
+ String16 cameraIdStr = String16(String8::format("%d", cameraId));
+ sp<IBinder> binder = new BBinder;
+
+ mCameraService->setTorchMode(cameraIdStr, true, binder);
+ ALOGV("Turned torch on.");
+ int32_t torchStrength = rand() % 5 + 1;
+ ALOGV("Changing torch strength level to %d", torchStrength);
+ mCameraService->turnOnTorchWithStrengthLevel(cameraIdStr, torchStrength, binder);
+ mCameraService->setTorchMode(cameraIdStr, false, binder);
+ ALOGV("Turned torch off.");
+}
+
void CameraFuzzer::invokeCameraAPIs() {
- for (int32_t cameraId = 0; cameraId < mNumCameras; ++cameraId) {
- getCameraInformation(cameraId);
+ /** In order to avoid the timeout issue caused due to multiple iteration of loops, the 'for'
+ * loops are removed and the 'cameraId', 'pictureSize' and 'videoSize' are derived using the
+ * FuzzedDataProvider from the available cameras and vectors of 'pictureSizes' and 'videoSizes'
+ */
+ int32_t cameraId = mFuzzedDataProvider->ConsumeIntegralInRange<int32_t>(0, mNumCameras - 1);
+ getCameraInformation(cameraId);
+ invokeTorchAPIs(cameraId);
- ::android::binder::Status rc;
- sp<ICamera> cameraDevice;
+ ::android::binder::Status rc;
+ sp<ICamera> cameraDevice;
- rc = mCameraService->connect(this, cameraId, String16(),
- android::CameraService::USE_CALLING_UID, android::CameraService::USE_CALLING_PID,
- /*targetSdkVersion*/__ANDROID_API_FUTURE__, &cameraDevice);
- if (!rc.isOk()) {
- // camera not connected
- return;
+ rc = mCameraService->connect(this, cameraId, String16(),
+ android::CameraService::USE_CALLING_UID,
+ android::CameraService::USE_CALLING_PID,
+ /*targetSdkVersion*/ __ANDROID_API_FUTURE__, &cameraDevice);
+ if (!rc.isOk()) {
+ // camera not connected
+ return;
+ }
+ if (cameraDevice) {
+ sp<Surface> previewSurface;
+ sp<SurfaceControl> surfaceControl;
+ CameraParameters params(cameraDevice->getParameters());
+ String8 focusModes(params.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES));
+ bool isAFSupported = false;
+ const char* focusMode = nullptr;
+
+ if (focusModes.contains(CameraParameters::FOCUS_MODE_AUTO)) {
+ isAFSupported = true;
+ } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE)) {
+ isAFSupported = true;
+ focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE;
+ } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO)) {
+ isAFSupported = true;
+ focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO;
+ } else if (focusModes.contains(CameraParameters::FOCUS_MODE_MACRO)) {
+ isAFSupported = true;
+ focusMode = CameraParameters::FOCUS_MODE_MACRO;
}
- if (cameraDevice) {
- sp<Surface> previewSurface;
- sp<SurfaceControl> surfaceControl;
- CameraParameters params(cameraDevice->getParameters());
- String8 focusModes(params.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES));
- bool isAFSupported = false;
- const char *focusMode = nullptr;
+ if (nullptr != focusMode) {
+ params.set(CameraParameters::KEY_FOCUS_MODE, focusMode);
+ cameraDevice->setParameters(params.flatten());
+ }
+ int previewWidth, previewHeight;
+ params.getPreviewSize(&previewWidth, &previewHeight);
- if (focusModes.contains(CameraParameters::FOCUS_MODE_AUTO)) {
- isAFSupported = true;
- } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE)) {
- isAFSupported = true;
- focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE;
- } else if (focusModes.contains(CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO)) {
- isAFSupported = true;
- focusMode = CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO;
- } else if (focusModes.contains(CameraParameters::FOCUS_MODE_MACRO)) {
- isAFSupported = true;
- focusMode = CameraParameters::FOCUS_MODE_MACRO;
- }
- if (nullptr != focusMode) {
- params.set(CameraParameters::KEY_FOCUS_MODE, focusMode);
- cameraDevice->setParameters(params.flatten());
- }
- int previewWidth, previewHeight;
- params.getPreviewSize(&previewWidth, &previewHeight);
+ mComposerClient = new SurfaceComposerClient;
+ mComposerClient->initCheck();
- mComposerClient = new SurfaceComposerClient;
- mComposerClient->initCheck();
-
- bool shouldPassInvalidLayerMetaData = mFuzzedDataProvider->ConsumeBool();
- int layerMetaData;
- if (shouldPassInvalidLayerMetaData) {
- layerMetaData = mFuzzedDataProvider->ConsumeIntegral<int>();
- } else {
- layerMetaData = kLayerMetadata[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ bool shouldPassInvalidLayerMetaData = mFuzzedDataProvider->ConsumeBool();
+ int layerMetaData;
+ if (shouldPassInvalidLayerMetaData) {
+ layerMetaData = mFuzzedDataProvider->ConsumeIntegral<int>();
+ } else {
+ layerMetaData = kLayerMetadata[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
0, kNumLayerMetaData - 1)];
- }
- surfaceControl = mComposerClient->createSurface(
+ }
+ surfaceControl = mComposerClient->createSurface(
String8("Test Surface"), previewWidth, previewHeight,
CameraParameters::previewFormatToEnum(params.getPreviewFormat()), layerMetaData);
- if (surfaceControl.get() != nullptr) {
- SurfaceComposerClient::Transaction{}
+ if (surfaceControl.get()) {
+ SurfaceComposerClient::Transaction{}
.setLayer(surfaceControl, 0x7fffffff)
.show(surfaceControl)
.apply();
- previewSurface = surfaceControl->getSurface();
+ previewSurface = surfaceControl->getSurface();
+ if (previewSurface.get()) {
cameraDevice->setPreviewTarget(previewSurface->getIGraphicBufferProducer());
}
- cameraDevice->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER);
+ }
+ cameraDevice->setPreviewCallbackFlag(CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER);
- Vector<Size> pictureSizes;
- params.getSupportedPictureSizes(pictureSizes);
+ Vector<Size> pictureSizes;
+ params.getSupportedPictureSizes(pictureSizes);
- for (size_t i = 0; i < pictureSizes.size(); ++i) {
- params.setPictureSize(pictureSizes[i].width, pictureSizes[i].height);
- cameraDevice->setParameters(params.flatten());
- cameraDevice->startPreview();
- waitForPreviewStart();
- cameraDevice->autoFocus();
- waitForEvent(mAutoFocusLock, mAutoFocusCondition, mAutoFocusMessage);
- bool shouldPassInvalidCameraMsg = mFuzzedDataProvider->ConsumeBool();
- int msgType;
- if (shouldPassInvalidCameraMsg) {
- msgType = mFuzzedDataProvider->ConsumeIntegral<int>();
- } else {
- msgType = kCameraMsg[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ if (pictureSizes.size()) {
+ Size pictureSize = pictureSizes[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ 0, pictureSizes.size() - 1)];
+ params.setPictureSize(pictureSize.width, pictureSize.height);
+ cameraDevice->setParameters(params.flatten());
+ cameraDevice->startPreview();
+ waitForPreviewStart();
+ cameraDevice->autoFocus();
+ waitForEvent(mAutoFocusLock, mAutoFocusCondition, mAutoFocusMessage);
+ bool shouldPassInvalidCameraMsg = mFuzzedDataProvider->ConsumeBool();
+ int msgType;
+ if (shouldPassInvalidCameraMsg) {
+ msgType = mFuzzedDataProvider->ConsumeIntegral<int>();
+ } else {
+ msgType = kCameraMsg[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
0, kNumCameraMsg - 1)];
- }
- cameraDevice->takePicture(msgType);
-
- waitForEvent(mSnapshotLock, mSnapshotCondition, mSnapshotNotification);
}
+ cameraDevice->takePicture(msgType);
- Vector<Size> videoSizes;
- params.getSupportedVideoSizes(videoSizes);
+ waitForEvent(mSnapshotLock, mSnapshotCondition, mSnapshotNotification);
+ cameraDevice->stopPreview();
+ }
- for (size_t i = 0; i < videoSizes.size(); ++i) {
- params.setVideoSize(videoSizes[i].width, videoSizes[i].height);
+ Vector<Size> videoSizes;
+ params.getSupportedVideoSizes(videoSizes);
- cameraDevice->setParameters(params.flatten());
- cameraDevice->startPreview();
- waitForPreviewStart();
- cameraDevice->setVideoBufferMode(
+ if (videoSizes.size()) {
+ Size videoSize = videoSizes[mFuzzedDataProvider->ConsumeIntegralInRange<size_t>(
+ 0, videoSizes.size() - 1)];
+ params.setVideoSize(videoSize.width, videoSize.height);
+
+ cameraDevice->setParameters(params.flatten());
+ cameraDevice->startPreview();
+ waitForPreviewStart();
+ cameraDevice->setVideoBufferMode(
android::hardware::BnCamera::VIDEO_BUFFER_MODE_BUFFER_QUEUE);
- cameraDevice->setVideoTarget(previewSurface->getIGraphicBufferProducer());
- cameraDevice->startRecording();
- cameraDevice->stopRecording();
+ sp<SurfaceControl> surfaceControlVideo = mComposerClient->createSurface(
+ String8("Test Surface Video"), previewWidth, previewHeight,
+ CameraParameters::previewFormatToEnum(params.getPreviewFormat()),
+ layerMetaData);
+ if (surfaceControlVideo.get()) {
+ SurfaceComposerClient::Transaction{}
+ .setLayer(surfaceControlVideo, 0x7fffffff)
+ .show(surfaceControlVideo)
+ .apply();
+ sp<Surface> previewSurfaceVideo = surfaceControlVideo->getSurface();
+ if (previewSurfaceVideo.get()) {
+ cameraDevice->setVideoTarget(previewSurfaceVideo->getIGraphicBufferProducer());
+ }
}
cameraDevice->stopPreview();
- cameraDevice->disconnect();
+ cameraDevice->startRecording();
+ waitForEvent(mRecordingLock, mRecordingCondition, mRecordingNotification);
+ cameraDevice->stopRecording();
}
+ cameraDevice->disconnect();
}
}
diff --git a/services/camera/libcameraservice/tests/Android.bp b/services/camera/libcameraservice/tests/Android.bp
index 3616572..4d7798c 100644
--- a/services/camera/libcameraservice/tests/Android.bp
+++ b/services/camera/libcameraservice/tests/Android.bp
@@ -27,8 +27,13 @@
"external/dynamic_depth/internal",
],
+ header_libs: [
+ "libmedia_headers",
+ ],
+
shared_libs: [
"libbase",
+ "libbinder",
"libcutils",
"libcameraservice",
"libhidlbase",
@@ -57,6 +62,7 @@
],
srcs: [
+ "CameraPermissionsTest.cpp",
"CameraProviderManagerTest.cpp",
"ClientManagerTest.cpp",
"DepthProcessorTest.cpp",
diff --git a/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
new file mode 100644
index 0000000..4359f9b
--- /dev/null
+++ b/services/camera/libcameraservice/tests/CameraPermissionsTest.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2022 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 <android/hardware/BnCameraServiceListener.h>
+#include <android/hardware/BnCameraServiceProxy.h>
+#include <android/hardware/camera2/BnCameraDeviceCallbacks.h>
+#include <android/hardware/ICameraService.h>
+
+#include <private/android_filesystem_config.h>
+
+#include "../CameraService.h"
+#include "../utils/CameraServiceProxyWrapper.h"
+
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <vector>
+
+using namespace android;
+using namespace android::hardware::camera;
+
+// Empty service listener.
+class TestCameraServiceListener : public hardware::BnCameraServiceListener {
+public:
+ virtual ~TestCameraServiceListener() {};
+
+ virtual binder::Status onStatusChanged(int32_t , const String16&) {
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onPhysicalCameraStatusChanged(int32_t /*status*/,
+ const String16& /*cameraId*/, const String16& /*physicalCameraId*/) {
+ // No op
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onTorchStatusChanged(int32_t /*status*/, const String16& /*cameraId*/) {
+ return binder::Status::ok();
+ };
+
+ virtual binder::Status onCameraAccessPrioritiesChanged() {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCameraOpened(const String16& /*cameraId*/,
+ const String16& /*clientPackageName*/) {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCameraClosed(const String16& /*cameraId*/) {
+ // No op
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onTorchStrengthLevelChanged(const String16& /*cameraId*/,
+ int32_t /*torchStrength*/) {
+ // No op
+ return binder::Status::ok();
+ }
+};
+
+// Empty device callback.
+class TestCameraDeviceCallbacks : public hardware::camera2::BnCameraDeviceCallbacks {
+public:
+ TestCameraDeviceCallbacks() {}
+
+ virtual ~TestCameraDeviceCallbacks() {}
+
+ virtual binder::Status onDeviceError(int /*errorCode*/,
+ const CaptureResultExtras& /*resultExtras*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onDeviceIdle() {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onCaptureStarted(const CaptureResultExtras& /*resultExtras*/,
+ int64_t /*timestamp*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onResultReceived(const CameraMetadata& /*metadata*/,
+ const CaptureResultExtras& /*resultExtras*/,
+ const std::vector<PhysicalCaptureResultInfo>& /*physicalResultInfos*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onPrepared(int /*streamId*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onRepeatingRequestError(
+ int64_t /*lastFrameNumber*/, int32_t /*stoppedSequenceId*/) {
+ return binder::Status::ok();
+ }
+
+ virtual binder::Status onRequestQueueEmpty() {
+ return binder::Status::ok();
+ }
+};
+
+// Override isCameraDisabled from the CameraServiceProxy with a flag.
+class CameraServiceProxyOverride : public ::android::hardware::BnCameraServiceProxy {
+public:
+ CameraServiceProxyOverride() :
+ mCameraServiceProxy(CameraServiceProxyWrapper::getDefaultCameraServiceProxy()),
+ mCameraDisabled(false), mOverrideCameraDisabled(false)
+ { }
+
+ virtual binder::Status getRotateAndCropOverride(const String16& packageName, int lensFacing,
+ int userId, int *ret) override {
+ return mCameraServiceProxy->getRotateAndCropOverride(packageName, lensFacing,
+ userId, ret);
+ }
+
+ virtual binder::Status pingForUserUpdate() override {
+ return mCameraServiceProxy->pingForUserUpdate();
+ }
+
+ virtual binder::Status notifyCameraState(
+ const hardware::CameraSessionStats& cameraSessionStats) override {
+ return mCameraServiceProxy->notifyCameraState(cameraSessionStats);
+ }
+
+ virtual binder::Status isCameraDisabled(bool *ret) override {
+ if (mOverrideCameraDisabled) {
+ *ret = mCameraDisabled;
+ return binder::Status::ok();
+ }
+ return mCameraServiceProxy->isCameraDisabled(ret);
+ }
+
+ void setCameraDisabled(bool cameraDisabled) {
+ mCameraDisabled = cameraDisabled;
+ }
+
+ void setOverrideCameraDisabled(bool overrideCameraDisabled) {
+ mOverrideCameraDisabled = overrideCameraDisabled;
+ }
+
+protected:
+ sp<hardware::ICameraServiceProxy> mCameraServiceProxy;
+ bool mCameraDisabled;
+ bool mOverrideCameraDisabled;
+};
+
+class AutoDisconnectDevice {
+public:
+ AutoDisconnectDevice(sp<hardware::camera2::ICameraDeviceUser> device) :
+ mDevice(device)
+ { }
+
+ ~AutoDisconnectDevice() {
+ if (mDevice != nullptr) {
+ mDevice->disconnect();
+ }
+ }
+
+private:
+ sp<hardware::camera2::ICameraDeviceUser> mDevice;
+};
+
+class CameraPermissionsTest : public ::testing::Test {
+protected:
+ static sp<CameraService> sCameraService;
+ static sp<CameraServiceProxyOverride> sCameraServiceProxy;
+ static std::shared_ptr<CameraServiceProxyWrapper> sCameraServiceProxyWrapper;
+ static uid_t sOldUid;
+
+ static void SetUpTestSuite() {
+ sOldUid = getuid();
+ setuid(AID_CAMERASERVER);
+ sCameraServiceProxy = new CameraServiceProxyOverride();
+ sCameraServiceProxyWrapper =
+ std::make_shared<CameraServiceProxyWrapper>(sCameraServiceProxy);
+ sCameraService = new CameraService(sCameraServiceProxyWrapper);
+ sCameraService->clearCachedVariables();
+ }
+
+ static void TearDownTestSuite() {
+ sCameraServiceProxyWrapper = nullptr;
+ sCameraServiceProxy = nullptr;
+ sCameraService = nullptr;
+ setuid(sOldUid);
+ }
+};
+
+sp<CameraService> CameraPermissionsTest::sCameraService = nullptr;
+sp<CameraServiceProxyOverride> CameraPermissionsTest::sCameraServiceProxy = nullptr;
+std::shared_ptr<CameraServiceProxyWrapper>
+CameraPermissionsTest::sCameraServiceProxyWrapper = nullptr;
+uid_t CameraPermissionsTest::sOldUid = 0;
+
+// Test that camera connections fail with ERROR_DISABLED when the camera is disabled via device
+// policy, and succeed when it isn't.
+TEST_F(CameraPermissionsTest, TestCameraDisabled) {
+ std::vector<hardware::CameraStatus> statuses;
+ sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
+ sCameraService->addListenerTest(serviceListener, &statuses);
+ sCameraServiceProxy->setOverrideCameraDisabled(true);
+
+ sCameraServiceProxy->setCameraDisabled(true);
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> device;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &device);
+ AutoDisconnectDevice autoDisconnect(device);
+ ASSERT_TRUE(!status.isOk()) << "connectDevice returned OK status";
+ ASSERT_EQ(status.serviceSpecificErrorCode(), hardware::ICameraService::ERROR_DISABLED)
+ << "connectDevice returned exception code " << status.exceptionCode();
+ }
+
+ sCameraServiceProxy->setCameraDisabled(false);
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> device;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &device);
+ AutoDisconnectDevice autoDisconnect(device);
+ ASSERT_TRUE(status.isOk());
+ }
+}
+
+// Test that consecutive camera connections succeed.
+TEST_F(CameraPermissionsTest, TestConsecutiveConnections) {
+ std::vector<hardware::CameraStatus> statuses;
+ sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
+ sCameraService->addListenerTest(serviceListener, &statuses);
+ sCameraServiceProxy->setOverrideCameraDisabled(false);
+
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceA);
+ AutoDisconnectDevice autoDisconnectA(deviceA);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceB);
+ AutoDisconnectDevice autoDisconnectB(deviceB);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ }
+}
+
+// Test that consecutive camera connections succeed even when a nonzero oomScoreOffset is provided
+// in the second call.
+TEST_F(CameraPermissionsTest, TestConflictingOomScoreOffset) {
+ std::vector<hardware::CameraStatus> statuses;
+ sp<TestCameraServiceListener> serviceListener = new TestCameraServiceListener();
+ sCameraService->addListenerTest(serviceListener, &statuses);
+ sCameraServiceProxy->setOverrideCameraDisabled(false);
+
+ for (auto s : statuses) {
+ sp<TestCameraDeviceCallbacks> callbacks = new TestCameraDeviceCallbacks();
+ sp<hardware::camera2::ICameraDeviceUser> deviceA, deviceB;
+ binder::Status status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 0/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceA);
+ AutoDisconnectDevice autoDisconnectA(deviceA);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ status =
+ sCameraService->connectDevice(callbacks, String16(s.cameraId), String16(), {},
+ android::CameraService::USE_CALLING_UID, 1/*oomScoreDiff*/,
+ /*targetSdkVersion*/__ANDROID_API_FUTURE__, &deviceB);
+ AutoDisconnectDevice autoDisconnectB(deviceB);
+ ASSERT_TRUE(status.isOk()) << "Exception code " << status.exceptionCode() <<
+ " service specific error code " << status.serviceSpecificErrorCode();
+ }
+}
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index 69175cc..733ecd9 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -29,29 +29,29 @@
using hardware::ICameraServiceProxy;
using hardware::CameraSessionStats;
-Mutex CameraServiceProxyWrapper::sProxyMutex;
-sp<hardware::ICameraServiceProxy> CameraServiceProxyWrapper::sCameraServiceProxy;
-
-Mutex CameraServiceProxyWrapper::mLock;
-std::map<String8, std::shared_ptr<CameraServiceProxyWrapper::CameraSessionStatsWrapper>>
- CameraServiceProxyWrapper::mSessionStatsMap;
-
/**
* CameraSessionStatsWrapper functions
*/
-void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen() {
- Mutex::Autolock l(mLock);
-
- updateProxyDeviceState(mSessionStats);
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::updateProxyDeviceState(
+ sp<hardware::ICameraServiceProxy>& proxyBinder) {
+ if (proxyBinder == nullptr) return;
+ proxyBinder->notifyCameraState(mSessionStats);
}
-void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose(int32_t latencyMs) {
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onOpen(
+ sp<hardware::ICameraServiceProxy>& proxyBinder) {
+ Mutex::Autolock l(mLock);
+ updateProxyDeviceState(proxyBinder);
+}
+
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onClose(
+ sp<hardware::ICameraServiceProxy>& proxyBinder, int32_t latencyMs) {
Mutex::Autolock l(mLock);
mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_CLOSED;
mSessionStats.mLatencyMs = latencyMs;
- updateProxyDeviceState(mSessionStats);
+ updateProxyDeviceState(proxyBinder);
}
void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onStreamConfigured(
@@ -66,12 +66,13 @@
}
}
-void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive(float maxPreviewFps) {
+void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onActive(
+ sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps) {
Mutex::Autolock l(mLock);
mSessionStats.mNewCameraState = CameraSessionStats::CAMERA_STATE_ACTIVE;
mSessionStats.mMaxPreviewFps = maxPreviewFps;
- updateProxyDeviceState(mSessionStats);
+ updateProxyDeviceState(proxyBinder);
// Reset mCreationDuration to -1 to distinguish between 1st session
// after configuration, and all other sessions after configuration.
@@ -79,6 +80,7 @@
}
void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
+ sp<hardware::ICameraServiceProxy>& proxyBinder,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::string& userTag, int32_t videoStabilizationMode,
const std::vector<hardware::CameraStreamStats>& streamStats) {
@@ -91,7 +93,7 @@
mSessionStats.mUserTag = String16(userTag.c_str());
mSessionStats.mVideoStabilizationMode = videoStabilizationMode;
mSessionStats.mStreamStats = streamStats;
- updateProxyDeviceState(mSessionStats);
+ updateProxyDeviceState(proxyBinder);
mSessionStats.mInternalReconfigure = 0;
mSessionStats.mStreamStats.clear();
@@ -103,19 +105,26 @@
sp<ICameraServiceProxy> CameraServiceProxyWrapper::getCameraServiceProxy() {
#ifndef __BRILLO__
- Mutex::Autolock al(sProxyMutex);
- if (sCameraServiceProxy == nullptr) {
- sp<IServiceManager> sm = defaultServiceManager();
- // Use checkService because cameraserver normally starts before the
- // system server and the proxy service. So the long timeout that getService
- // has before giving up is inappropriate.
- sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
- if (binder != nullptr) {
- sCameraServiceProxy = interface_cast<ICameraServiceProxy>(binder);
- }
+ Mutex::Autolock al(mProxyMutex);
+ if (mCameraServiceProxy == nullptr) {
+ mCameraServiceProxy = getDefaultCameraServiceProxy();
}
#endif
- return sCameraServiceProxy;
+ return mCameraServiceProxy;
+}
+
+sp<hardware::ICameraServiceProxy> CameraServiceProxyWrapper::getDefaultCameraServiceProxy() {
+#ifndef __BRILLO__
+ sp<IServiceManager> sm = defaultServiceManager();
+ // Use checkService because cameraserver normally starts before the
+ // system server and the proxy service. So the long timeout that getService
+ // has before giving up is inappropriate.
+ sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
+ if (binder != nullptr) {
+ return interface_cast<ICameraServiceProxy>(binder);
+ }
+#endif
+ return nullptr;
}
void CameraServiceProxyWrapper::pingCameraServiceProxy() {
@@ -138,12 +147,6 @@
return ret;
}
-void CameraServiceProxyWrapper::updateProxyDeviceState(const CameraSessionStats& sessionStats) {
- sp<ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
- if (proxyBinder == nullptr) return;
- proxyBinder->notifyCameraState(sessionStats);
-}
-
void CameraServiceProxyWrapper::logStreamConfigured(const String8& id,
int operatingMode, bool internalConfig, int32_t latencyMs) {
std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
@@ -175,7 +178,8 @@
}
ALOGV("%s: id %s", __FUNCTION__, id.c_str());
- sessionStats->onActive(maxPreviewFps);
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onActive(proxyBinder, maxPreviewFps);
}
void CameraServiceProxyWrapper::logIdle(const String8& id,
@@ -205,7 +209,8 @@
streamStats[i].mStartLatencyMs);
}
- sessionStats->onIdle(requestCount, resultErrorCount, deviceError, userTag,
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onIdle(proxyBinder, requestCount, resultErrorCount, deviceError, userTag,
videoStabilizationMode, streamStats);
}
@@ -235,7 +240,8 @@
ALOGV("%s: id %s, facing %d, effectiveApiLevel %d, isNdk %d, latencyMs %d",
__FUNCTION__, id.c_str(), facing, effectiveApiLevel, isNdk, latencyMs);
- sessionStats->onOpen();
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onOpen(proxyBinder);
}
void CameraServiceProxyWrapper::logClose(const String8& id, int32_t latencyMs) {
@@ -259,7 +265,8 @@
}
ALOGV("%s: id %s, latencyMs %d", __FUNCTION__, id.c_str(), latencyMs);
- sessionStats->onClose(latencyMs);
+ sp<hardware::ICameraServiceProxy> proxyBinder = getCameraServiceProxy();
+ sessionStats->onClose(proxyBinder, latencyMs);
}
bool CameraServiceProxyWrapper::isCameraDisabled() {
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index e34a8f0..6af56c3 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -32,72 +32,80 @@
class CameraServiceProxyWrapper {
private:
// Guard mCameraServiceProxy
- static Mutex sProxyMutex;
+ Mutex mProxyMutex;
// Cached interface to the camera service proxy in system service
- static sp<hardware::ICameraServiceProxy> sCameraServiceProxy;
+ sp<hardware::ICameraServiceProxy> mCameraServiceProxy;
- struct CameraSessionStatsWrapper {
+ class CameraSessionStatsWrapper {
+ private:
hardware::CameraSessionStats mSessionStats;
Mutex mLock; // lock for per camera session stats
+ /**
+ * Update the session stats of a given camera device (open/close/active/idle) with
+ * the camera proxy service in the system service
+ */
+ void updateProxyDeviceState(sp<hardware::ICameraServiceProxy>& proxyBinder);
+
+ public:
CameraSessionStatsWrapper(const String16& cameraId, int facing, int newCameraState,
const String16& clientName, int apiLevel, bool isNdk, int32_t latencyMs) :
mSessionStats(cameraId, facing, newCameraState, clientName, apiLevel, isNdk, latencyMs)
- {}
+ { }
- void onOpen();
- void onClose(int32_t latencyMs);
+ void onOpen(sp<hardware::ICameraServiceProxy>& proxyBinder);
+ void onClose(sp<hardware::ICameraServiceProxy>& proxyBinder, int32_t latencyMs);
void onStreamConfigured(int operatingMode, bool internalReconfig, int32_t latencyMs);
- void onActive(float maxPreviewFps);
- void onIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ void onActive(sp<hardware::ICameraServiceProxy>& proxyBinder, float maxPreviewFps);
+ void onIdle(sp<hardware::ICameraServiceProxy>& proxyBinder,
+ int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::string& userTag, int32_t videoStabilizationMode,
const std::vector<hardware::CameraStreamStats>& streamStats);
};
// Lock for camera session stats map
- static Mutex mLock;
+ Mutex mLock;
// Map from camera id to the camera's session statistics
- static std::map<String8, std::shared_ptr<CameraSessionStatsWrapper>> mSessionStatsMap;
+ std::map<String8, std::shared_ptr<CameraSessionStatsWrapper>> mSessionStatsMap;
- /**
- * Update the session stats of a given camera device (open/close/active/idle) with
- * the camera proxy service in the system service
- */
- static void updateProxyDeviceState(
- const hardware::CameraSessionStats& sessionStats);
-
- static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
+ sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
public:
+ CameraServiceProxyWrapper(sp<hardware::ICameraServiceProxy> serviceProxy = nullptr) :
+ mCameraServiceProxy(serviceProxy)
+ { }
+
+ static sp<hardware::ICameraServiceProxy> getDefaultCameraServiceProxy();
+
// Open
- static void logOpen(const String8& id, int facing,
+ void logOpen(const String8& id, int facing,
const String16& clientPackageName, int apiLevel, bool isNdk,
int32_t latencyMs);
// Close
- static void logClose(const String8& id, int32_t latencyMs);
+ void logClose(const String8& id, int32_t latencyMs);
// Stream configuration
- static void logStreamConfigured(const String8& id, int operatingMode, bool internalReconfig,
+ void logStreamConfigured(const String8& id, int operatingMode, bool internalReconfig,
int32_t latencyMs);
// Session state becomes active
- static void logActive(const String8& id, float maxPreviewFps);
+ void logActive(const String8& id, float maxPreviewFps);
// Session state becomes idle
- static void logIdle(const String8& id,
+ void logIdle(const String8& id,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
const std::string& userTag, int32_t videoStabilizationMode,
const std::vector<hardware::CameraStreamStats>& streamStats);
// Ping camera service proxy for user update
- static void pingCameraServiceProxy();
+ void pingCameraServiceProxy();
// Return the current top activity rotate and crop override.
- static int getRotateAndCropOverride(String16 packageName, int lensFacing, int userId);
+ int getRotateAndCropOverride(String16 packageName, int lensFacing, int userId);
// Detect if the camera is disabled by device policy.
- static bool isCameraDisabled();
+ bool isCameraDisabled();
};
} // android
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 3f18b95..bbfa0b7 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -228,9 +228,6 @@
if (mMmapStream != nullptr) {
// Needs to be explicitly cleared or CTS will fail but it is not clear why.
mMmapStream.clear();
- // Apparently the above close is asynchronous. An attempt to open a new device
- // right after a close can fail. Also some callbacks may still be in flight!
- // FIXME Make closing synchronous.
AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
}
}
@@ -362,12 +359,8 @@
asyncTask.detach();
}
-void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
- android::Vector<float> values) {
- // TODO Do we really need a different volume for each channel?
- // We get called with an array filled with a single value!
- float volume = values[0];
- ALOGD("%s() volume[0] = %f", __func__, volume);
+void AAudioServiceEndpointMMAP::onVolumeChanged(float volume) {
+ ALOGD("%s() volume = %f", __func__, volume);
std::lock_guard<std::mutex> lock(mLockStreams);
for(const auto& stream : mRegisteredStreams) {
stream->onVolumeChanged(volume);
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index 3e7f2c7..3658c58 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -77,8 +77,7 @@
// -------------- Callback functions for MmapStreamCallback ---------------------
void onTearDown(audio_port_handle_t portHandle) override;
- void onVolumeChanged(audio_channel_mask_t channels,
- android::Vector<float> values) override;
+ void onVolumeChanged(float volume) override;
void onRoutingChanged(audio_port_handle_t portHandle) override;
// ------------------------------------------------------------------------------
diff --git a/services/tuner/main_tunerservice.cpp b/services/tuner/main_tunerservice.cpp
index a014dea..43ff95c 100644
--- a/services/tuner/main_tunerservice.cpp
+++ b/services/tuner/main_tunerservice.cpp
@@ -40,6 +40,7 @@
CHECK(status == STATUS_OK);
}
+ ProcessState::self()->setThreadPoolMaxThreadCount(8);
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
return EXIT_FAILURE; // should not reached