Merge "cameraserver: Fix stream specific HAL buffer manager for HIDL devices" into main
diff --git a/aidl/android/media/audio/IHalAdapterVendorExtension.aidl b/aidl/android/media/audio/IHalAdapterVendorExtension.aidl
index b7a7678..48fb291 100644
--- a/aidl/android/media/audio/IHalAdapterVendorExtension.aidl
+++ b/aidl/android/media/audio/IHalAdapterVendorExtension.aidl
@@ -23,6 +23,8 @@
* is optional. Vendors may provide an implementation on the system_ext
* partition. The default instance of this interface, if provided, must be
* registered prior to the moment when the audio server connects to HAL modules.
+ * Vendors need to set the system property `ro.audio.ihaladaptervendorextension_enabled`
+ * to `true` for the framework to bind to this service.
*
* {@hide}
*/
diff --git a/camera/Android.bp b/camera/Android.bp
index 4c5b160..d0f8e7e 100644
--- a/camera/Android.bp
+++ b/camera/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_camera_framework",
default_applicable_licenses: ["frameworks_av_camera_license"],
}
@@ -65,6 +66,7 @@
name: "camera_headers",
export_include_dirs: ["include"],
}
+
cc_library {
name: "libcamera_client",
@@ -120,10 +122,14 @@
"frameworks/native/include/media/openmax",
],
export_include_dirs: [
- "include",
- "include/camera"
+ "include",
+ "include/camera",
],
- export_shared_lib_headers: ["libcamera_metadata", "libnativewindow", "libgui"],
+ export_shared_lib_headers: [
+ "libcamera_metadata",
+ "libnativewindow",
+ "libgui",
+ ],
cflags: [
"-Werror",
@@ -153,7 +159,7 @@
export_include_dirs: [
"include",
- "include/camera"
+ "include/camera",
],
}
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 0eeeb7f..4bea896 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -278,14 +278,28 @@
CameraMetadataNative createDefaultRequest(@utf8InCpp String cameraId, int templateId);
/**
- * Check whether a particular session configuration with optional session parameters
- * has camera device support.
- *
- * @param cameraId The camera id to query session configuration on
- * @param sessionConfiguration Specific session configuration to be verified.
- * @return true - in case the stream combination is supported.
- * false - in case there is no device support.
- */
+ * Check whether a particular session configuration with optional session parameters
+ * has camera device support.
+ *
+ * @param cameraId The camera id to query session configuration for
+ * @param sessionConfiguration Specific session configuration to be verified.
+ * @return true - in case the stream combination is supported.
+ * false - in case there is no device support.
+ */
boolean isSessionConfigurationWithParametersSupported(@utf8InCpp String cameraId,
in SessionConfiguration sessionConfiguration);
+
+ /**
+ * Get the camera characteristics for a particular session configuration for
+ * the given camera device.
+ *
+ * @param cameraId ID of the device for which the session characteristics must be fetched.
+ * @param sessionConfiguration session configuration for which the characteristics
+ * must be fetched.
+ * @return - characteristics associated with the given session.
+ */
+ CameraMetadataNative getSessionCharacteristics(@utf8InCpp String cameraId,
+ int targetSdkVersion,
+ boolean overrideToPortrait,
+ in SessionConfiguration sessionConfiguration);
}
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 843e0d4..8e1fcc0 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -99,15 +99,6 @@
*/
boolean isSessionConfigurationSupported(in SessionConfiguration sessionConfiguration);
- /**
- * Get the camera characteristics for a particular session configuration
- *
- * @param sessionConfiguration Specific session configuration for which the characteristics
- * are fetched.
- * @return - characteristics associated with the given session.
- */
- CameraMetadataNative getSessionCharacteristics(in SessionConfiguration sessionConfiguration);
-
void deleteStream(int streamId);
/**
diff --git a/camera/camera_platform.aconfig b/camera/camera_platform.aconfig
index b395b97..1f50570 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -24,6 +24,13 @@
flag {
namespace: "camera_platform"
+ name: "watch_foreground_changes"
+ description: "Request AppOps to notify changes in the foreground status of the client"
+ bug: "290086710"
+}
+
+flag {
+ namespace: "camera_platform"
name: "log_ultrawide_usage"
description: "Enable measuring how much usage there is for ultrawide-angle cameras"
bug: "300515796"
@@ -105,3 +112,24 @@
description: "Enable returning graphics buffers to buffer queues without holding the in-flight mutex"
bug: "315526878"
}
+
+flag {
+ namespace: "camera_platform"
+ name: "camera_device_setup"
+ description: "Create an intermediate Camera Device class for limited CameraDevice access."
+ bug: "320741775"
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "camera_privacy_allowlist"
+ description: "Allowlisting to exempt safety-relevant cameras from privacy control for automotive devices"
+ bug: "282814430"
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "extension_10_bit"
+ description: "Enables 10-bit support in the camera extensions."
+ bug: "316375635"
+}
diff --git a/camera/cameraserver/Android.bp b/camera/cameraserver/Android.bp
index 13b705c..6862cb1 100644
--- a/camera/cameraserver/Android.bp
+++ b/camera/cameraserver/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_camera_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_camera_license"
diff --git a/camera/ndk/Android.bp b/camera/ndk/Android.bp
index 165395a..421469a 100644
--- a/camera/ndk/Android.bp
+++ b/camera/ndk/Android.bp
@@ -17,6 +17,7 @@
// frameworks/av/include.
package {
+ default_team: "trendy_team_camera_framework",
default_applicable_licenses: ["frameworks_av_camera_ndk_license"],
}
diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp
index 5d3b65b..8c3424f 100644
--- a/camera/ndk/impl/ACameraManager.cpp
+++ b/camera/ndk/impl/ACameraManager.cpp
@@ -255,6 +255,7 @@
template<class T>
void CameraManagerGlobal::registerAvailCallback(const T *callback) {
Mutex::Autolock _l(mLock);
+ getCameraServiceLocked();
Callback cb(callback);
auto pair = mCallbacks.insert(cb);
// Send initial callbacks if callback is newly registered
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 2c68cef..1ed17a3 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -76,6 +76,7 @@
ACAMERA_AUTOMOTIVE_LENS,
ACAMERA_EXTENSION,
ACAMERA_JPEGR,
+ ACAMERA_EFV,
ACAMERA_SECTION_COUNT,
ACAMERA_VENDOR = 0x8000
@@ -123,6 +124,7 @@
ACAMERA_AUTOMOTIVE_LENS_START = ACAMERA_AUTOMOTIVE_LENS << 16,
ACAMERA_EXTENSION_START = ACAMERA_EXTENSION << 16,
ACAMERA_JPEGR_START = ACAMERA_JPEGR << 16,
+ ACAMERA_EFV_START = ACAMERA_EFV << 16,
ACAMERA_VENDOR_START = ACAMERA_VENDOR << 16
} acamera_metadata_section_start_t;
@@ -4705,18 +4707,21 @@
* </ul>
* <p>should be interpreted in the effective after raw crop field-of-view coordinate system.
* In this coordinate system,
- * {preCorrectionActiveArraySize.left, preCorrectionActiveArraySize.top} corresponds to the
+ * {ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.left,
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.top} corresponds to the
* the top left corner of the cropped RAW frame and
- * {preCorrectionActiveArraySize.right, preCorrectionActiveArraySize.bottom} corresponds to
+ * {ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.right,
+ * ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE.bottom} corresponds to
* the bottom right corner. Client applications must use the values of the keys
* in the CaptureResult metadata if present.</p>
- * <p>Crop regions (android.scaler.CropRegion), AE/AWB/AF regions and face coordinates still
+ * <p>Crop regions ACAMERA_SCALER_CROP_REGION, AE/AWB/AF regions and face coordinates still
* use the ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE coordinate system as usual.</p>
*
* @see ACAMERA_LENS_DISTORTION
* @see ACAMERA_LENS_INTRINSIC_CALIBRATION
* @see ACAMERA_LENS_POSE_ROTATION
* @see ACAMERA_LENS_POSE_TRANSLATION
+ * @see ACAMERA_SCALER_CROP_REGION
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see ACAMERA_STATISTICS_HOT_PIXEL_MAP
@@ -11524,6 +11529,7 @@
+
__END_DECLS
#endif /* _NDK_CAMERA_METADATA_TAGS_H */
diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
index 3aa7817..099786b 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp
@@ -396,6 +396,7 @@
template <class T>
void CameraManagerGlobal::registerAvailCallback(const T *callback) {
+ getCameraService();
Mutex::Autolock _l(mLock);
Callback cb(callback);
auto res = mCallbacks.insert(cb);
diff --git a/camera/tests/Android.bp b/camera/tests/Android.bp
index 65b8b41..9aaac6a 100644
--- a/camera/tests/Android.bp
+++ b/camera/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_camera_framework",
// See: http://go/android-license-faq
default_applicable_licenses: [
"frameworks_av_camera_license",
diff --git a/camera/tests/fuzzer/Android.bp b/camera/tests/fuzzer/Android.bp
index b74b7a1..bd97c39 100644
--- a/camera/tests/fuzzer/Android.bp
+++ b/camera/tests/fuzzer/Android.bp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
package {
+ default_team: "trendy_team_camera_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_camera_license"
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index ddd2bf1..3092091 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -27,15 +27,50 @@
}
flag {
+ name: "in_process_sw_audio_codec"
+ namespace: "codec_fwk"
+ description: "Feature flag for in-process software audio codec API"
+ bug: "297922713"
+}
+
+flag {
+ name: "in_process_sw_audio_codec_support"
+ namespace: "codec_fwk"
+ description: "Feature flag for in-process software audio codec support"
+ bug: "325520135"
+}
+
+flag {
+ name: "large_audio_frame_finish"
+ namespace: "codec_fwk"
+ description: "Implementation flag for large audio frame finishing tasks"
+ bug: "325512893"
+}
+
+flag {
name: "null_output_surface"
namespace: "codec_fwk"
- description: "Feature flag for null output Surface support"
+ description: "Feature flag for null output Surface API"
bug: "297920102"
}
flag {
+ name: "null_output_surface_support"
+ namespace: "codec_fwk"
+ description: "Feature flag for null output Surface support"
+ bug: "325550522"
+}
+
+flag {
name: "region_of_interest"
namespace: "codec_fwk"
- description: "Feature flag for region of interest support"
+ description: "Feature flag for region of interest API"
bug: "299191092"
}
+
+flag {
+ name: "region_of_interest_support"
+ namespace: "codec_fwk"
+ description: "Feature flag for region of interest support"
+ bug: "325549730"
+}
diff --git a/media/audio/aconfig/Android.bp b/media/audio/aconfig/Android.bp
index 39a1544..6d21e97 100644
--- a/media/audio/aconfig/Android.bp
+++ b/media/audio/aconfig/Android.bp
@@ -134,11 +134,11 @@
defaults: ["audio-aconfig-cc-defaults"],
}
-filegroup {
+aconfig_declarations_group {
name: "audio-framework-aconfig",
- srcs: [
- ":android.media.audio-aconfig-java{.generated_srcjars}",
- ":android.media.audiopolicy-aconfig-java{.generated_srcjars}",
- ":android.media.midi-aconfig-java{.generated_srcjars}",
+ java_aconfig_libraries: [
+ "android.media.audio-aconfig-java",
+ "android.media.audiopolicy-aconfig-java",
+ "android.media.midi-aconfig-java",
],
}
diff --git a/media/audioserver/Android.bp b/media/audioserver/Android.bp
index 2030dc7..479e13a 100644
--- a/media/audioserver/Android.bp
+++ b/media/audioserver/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -71,7 +72,6 @@
"frameworks/av/services/medialog",
"frameworks/av/services/oboeservice", // TODO oboeservice is the old folder name for aaudioservice. It will be changed.
-
],
init_rc: ["audioserver.rc"],
diff --git a/media/codec2/hal/common/MultiAccessUnitHelper.cpp b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
index cd9fd9f..9221a24 100644
--- a/media/codec2/hal/common/MultiAccessUnitHelper.cpp
+++ b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
@@ -177,33 +177,29 @@
std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) {
c2_status_t c2res = C2_OK;
std::lock_guard<std::mutex> l(mLock);
- for (std::unique_ptr<C2Work>& w : *c2flushedWorks) {
+ for (auto iterWork = c2flushedWorks->begin() ; iterWork != c2flushedWorks->end(); ) {
bool foundFlushedFrame = false;
std::list<MultiAccessUnitInfo>::iterator frame =
mFrameHolder.begin();
while (frame != mFrameHolder.end() && !foundFlushedFrame) {
auto it = frame->mComponentFrameIds.find(
- w->input.ordinal.frameIndex.peekull());
+ (*iterWork)->input.ordinal.frameIndex.peekull());
if (it != frame->mComponentFrameIds.end()) {
- LOG(DEBUG) << "Multi access-unit flush"
- << w->input.ordinal.frameIndex.peekull()
+ LOG(DEBUG) << "Multi access-unit flush "
+ << (*iterWork)->input.ordinal.frameIndex.peekull()
<< " with " << frame->inOrdinal.frameIndex.peekull();
- w->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
- bool removeEntry = w->worklets.empty()
- || !w->worklets.front()
- || (w->worklets.front()->output.flags
- & C2FrameData::FLAG_INCOMPLETE) == 0;
- if (removeEntry) {
- frame->mComponentFrameIds.erase(it);
- }
- foundFlushedFrame = true;
- }
- if (frame->mComponentFrameIds.empty()) {
+ (*iterWork)->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
frame = mFrameHolder.erase(frame);
+ foundFlushedFrame = true;
} else {
++frame;
}
}
+ if (!foundFlushedFrame) {
+ iterWork = c2flushedWorks->erase(iterWork);
+ } else {
+ ++iterWork;
+ }
}
return c2res;
}
@@ -297,13 +293,15 @@
std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au)));
LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal "
<< inputOrdinal.frameIndex.peekull()
- << " total offset " << offset << " info.size " << info.size
- << " : TS " << newWork->input.ordinal.timestamp.peekull();
+ << " info.size " << info.size
+ << " : TS " << newWork->input.ordinal.timestamp.peekull()
+ << " with index " << newFrameIdx - 1;
// add to worklist
sliceWork.push_back(std::move(newWork));
processedWork->push_back(std::move(sliceWork));
offset += info.size;
}
+ mFrameIndex--;
if (!sendEos && (w->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
if (!processedWork->empty()) {
std::list<std::unique_ptr<C2Work>> &sliceWork = processedWork->back();
@@ -498,7 +496,7 @@
}
frame.mLargeWork = std::move(work);
frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
- finalizeWork(frame);
+ finalizeWork(frame, (*worklet)->output.flags, true);
addWork(frame.mLargeWork);
frame.reset();
return C2_OK;
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/Android.bp
index 2054fe6..ccdde5e 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/audio/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/audio/Android.bp
index 624aad2..2b1bca0 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/audio/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/audio/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/common/Android.bp
index 0f07077..564de47 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/component/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/component/Android.bp
index cc019da..0640f02 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/component/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/component/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/master/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/master/Android.bp
index 40f5201..5e52fde 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/master/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/master/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
index 47ceed5..a34cef1 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/master/VtsHalMediaC2V1_0TargetMasterTest.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "codec2_hidl_hal_master_test"
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
@@ -82,6 +83,20 @@
}
}
+TEST_P(Codec2MasterHalTest, MustUseAidlBeyond202404) {
+ static int sBoardFirstApiLevel = android::base::GetIntProperty("ro.board.first_api_level", 0);
+ static int sBoardApiLevel = android::base::GetIntProperty("ro.board.api_level", 0);
+ if (sBoardFirstApiLevel < 202404 && sBoardApiLevel < 202404) {
+ GTEST_SKIP() << "board first level less than 202404:"
+ << " ro.board.first_api_level = " << sBoardFirstApiLevel
+ << " ro.board.api_level = " << sBoardApiLevel;
+ }
+ ALOGV("HidlCodecAllowed Test");
+
+ EXPECT_NE(mClient->getAidlBase(), nullptr) << "android.hardware.media.c2 MUST use AIDL "
+ << "for chipsets launching at 202404 or above";
+}
+
} // anonymous namespace
INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2MasterHalTest,
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/Android.bp b/media/codec2/hal/hidl/1.0/vts/functional/video/Android.bp
index ecc4f9d..d04c2f6 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/Android.bp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_media_codec_framework",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 7b1721e..40656ff 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -483,6 +483,130 @@
return heapSeqNum;
}
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+typedef WrapperObject<std::vector<std::unique_ptr<CodecCryptoInfo>>> CryptoInfosWrapper;
+status_t CCodecBufferChannel::attachEncryptedBuffers(
+ const sp<hardware::HidlMemory> &memory,
+ size_t offset,
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ AString* errorDetailMsg) {
+ static const C2MemoryUsage kDefaultReadWriteUsage{
+ C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+ if (!hasCryptoOrDescrambler()) {
+ ALOGE("attachEncryptedBuffers requires Crypto/descrambler object");
+ return -ENOSYS;
+ }
+ size_t size = 0;
+ CHECK(buffer->meta()->findSize("ssize", &size));
+ if (size == 0) {
+ buffer->setRange(0, 0);
+ return OK;
+ }
+ sp<RefBase> obj;
+ CHECK(buffer->meta()->findObject("cryptoInfos", &obj));
+ sp<CryptoInfosWrapper> cryptoInfos{(CryptoInfosWrapper *)obj.get()};
+ CHECK(buffer->meta()->findObject("accessUnitInfo", &obj));
+ sp<BufferInfosWrapper> bufferInfos{(BufferInfosWrapper *)obj.get()};
+ if (secure || (mCrypto == nullptr)) {
+ if (cryptoInfos->value.size() != 1) {
+ ALOGE("Cannot decrypt multiple access units");
+ return -ENOSYS;
+ }
+ // we are dealing with just one cryptoInfo or descrambler.
+ std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[0]);
+ if (info == nullptr) {
+ ALOGE("Cannot decrypt, CryptoInfos are null.");
+ return -ENOSYS;
+ }
+ return attachEncryptedBuffer(
+ memory,
+ secure,
+ info->mKey,
+ info->mIv,
+ info->mMode,
+ info->mPattern,
+ offset,
+ info->mSubSamples,
+ info->mNumSubSamples,
+ buffer,
+ errorDetailMsg);
+ }
+ std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool;
+ std::shared_ptr<C2LinearBlock> block;
+ c2_status_t err = pool->fetchLinearBlock(
+ size,
+ kDefaultReadWriteUsage,
+ &block);
+ if (err != C2_OK) {
+ ALOGI("[%s] attachEncryptedBuffers: fetchLinearBlock failed: size = %zu (%s) err = %d",
+ mName, size, secure ? "secure" : "non-secure", err);
+ return NO_MEMORY;
+ }
+ ensureDecryptDestination(size);
+ C2WriteView wView = block->map().get();
+ if (wView.error() != C2_OK) {
+ ALOGI("[%s] attachEncryptedBuffers: block map error: %d (non-secure)",
+ mName, wView.error());
+ return UNKNOWN_ERROR;
+ }
+
+ ssize_t result = -1;
+ ssize_t codecDataOffset = 0;
+ size_t inBufferOffset = 0;
+ size_t outBufferSize = 0;
+ uint32_t cryptoInfoIdx = 0;
+ int32_t heapSeqNum = getHeapSeqNum(memory);
+ hardware::drm::V1_0::SharedBuffer src{(uint32_t)heapSeqNum, offset, size};
+ hardware::drm::V1_0::DestinationBuffer dst;
+ dst.type = DrmBufferType::SHARED_MEMORY;
+ IMemoryToSharedBuffer(
+ mDecryptDestination, mHeapSeqNum, &dst.nonsecureMemory);
+ for (int i = 0; i < bufferInfos->value.size(); i++) {
+ if (bufferInfos->value[i].mSize > 0) {
+ std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[cryptoInfoIdx++]);
+ result = mCrypto->decrypt(
+ (uint8_t*)info->mKey,
+ (uint8_t*)info->mIv,
+ info->mMode,
+ info->mPattern,
+ src,
+ inBufferOffset,
+ info->mSubSamples,
+ info->mNumSubSamples,
+ dst,
+ errorDetailMsg);
+ inBufferOffset += bufferInfos->value[i].mSize;
+ if (result < 0) {
+ ALOGI("[%s] attachEncryptedBuffers: decrypt failed: result = %zd",
+ mName, result);
+ return result;
+ }
+ if (wView.error() == C2_OK) {
+ if (wView.size() < result) {
+ ALOGI("[%s] attachEncryptedBuffers: block size too small:"
+ "size=%u result=%zd (non-secure)", mName, wView.size(), result);
+ return UNKNOWN_ERROR;
+ }
+ memcpy(wView.data(), mDecryptDestination->unsecurePointer(), result);
+ bufferInfos->value[i].mSize = result;
+ wView.setOffset(wView.offset() + result);
+ }
+ outBufferSize += result;
+ }
+ }
+ if (wView.error() == C2_OK) {
+ wView.setOffset(0);
+ }
+ std::shared_ptr<C2Buffer> c2Buffer{C2Buffer::CreateLinearBuffer(
+ block->share(codecDataOffset, outBufferSize - codecDataOffset, C2Fence{}))};
+ if (!buffer->copy(c2Buffer)) {
+ ALOGI("[%s] attachEncryptedBuffers: buffer copy failed", mName);
+ return -ENOSYS;
+ }
+ return OK;
+}
+
status_t CCodecBufferChannel::attachEncryptedBuffer(
const sp<hardware::HidlMemory> &memory,
bool secure,
@@ -777,6 +901,138 @@
return queueInputBufferInternal(buffer, block, bufferSize);
}
+status_t CCodecBufferChannel::queueSecureInputBuffers(
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ AString *errorDetailMsg) {
+ QueueGuard guard(mSync);
+ if (!guard.isRunning()) {
+ ALOGD("[%s] No more buffers should be queued at current state.", mName);
+ return -ENOSYS;
+ }
+
+ if (!hasCryptoOrDescrambler()) {
+ ALOGE("queueSecureInputBuffers requires a Crypto/descrambler Object");
+ return -ENOSYS;
+ }
+ sp<RefBase> obj;
+ CHECK(buffer->meta()->findObject("cryptoInfos", &obj));
+ sp<CryptoInfosWrapper> cryptoInfos{(CryptoInfosWrapper *)obj.get()};
+ CHECK(buffer->meta()->findObject("accessUnitInfo", &obj));
+ sp<BufferInfosWrapper> bufferInfos{(BufferInfosWrapper *)obj.get()};
+ if (secure || mCrypto == nullptr) {
+ if (cryptoInfos->value.size() != 1) {
+ ALOGE("Cannot decrypt multiple access units on native handles");
+ return -ENOSYS;
+ }
+ std::unique_ptr<CodecCryptoInfo> info = std::move(cryptoInfos->value[0]);
+ if (info == nullptr) {
+ ALOGE("Cannot decrypt, CryptoInfos are null");
+ return -ENOSYS;
+ }
+ return queueSecureInputBuffer(
+ buffer,
+ secure,
+ info->mKey,
+ info->mIv,
+ info->mMode,
+ info->mPattern,
+ info->mSubSamples,
+ info->mNumSubSamples,
+ errorDetailMsg);
+ }
+ sp<EncryptedLinearBlockBuffer> encryptedBuffer((EncryptedLinearBlockBuffer *)buffer.get());
+
+ std::shared_ptr<C2LinearBlock> block;
+ size_t allocSize = buffer->size();
+ size_t bufferSize = 0;
+ c2_status_t blockRes = C2_OK;
+ bool copied = false;
+ ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf(
+ "CCodecBufferChannel::decrypt(%s)", mName).c_str());
+ if (mSendEncryptedInfoBuffer) {
+ static const C2MemoryUsage kDefaultReadWriteUsage{
+ C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
+ constexpr int kAllocGranule0 = 1024 * 64;
+ constexpr int kAllocGranule1 = 1024 * 1024;
+ std::shared_ptr<C2BlockPool> pool = mBlockPools.lock()->inputPool;
+ // round up encrypted sizes to limit fragmentation and encourage buffer reuse
+ if (allocSize <= kAllocGranule1) {
+ bufferSize = align(allocSize, kAllocGranule0);
+ } else {
+ bufferSize = align(allocSize, kAllocGranule1);
+ }
+ blockRes = pool->fetchLinearBlock(
+ bufferSize, kDefaultReadWriteUsage, &block);
+
+ if (blockRes == C2_OK) {
+ C2WriteView view = block->map().get();
+ if (view.error() == C2_OK && view.size() == bufferSize) {
+ copied = true;
+ // TODO: only copy clear sections
+ memcpy(view.data(), buffer->data(), allocSize);
+ }
+ }
+ }
+
+ if (!copied) {
+ block.reset();
+ }
+ // size of cryptoInfo and accessUnitInfo should be the same?
+ ssize_t result = -1;
+ ssize_t codecDataOffset = 0;
+ size_t inBufferOffset = 0;
+ size_t outBufferSize = 0;
+ uint32_t cryptoInfoIdx = 0;
+ {
+ // scoped this block to enable destruction of mappedBlock
+ std::unique_ptr<EncryptedLinearBlockBuffer::MappedBlock> mappedBlock = nullptr;
+ hardware::drm::V1_0::DestinationBuffer destination;
+ destination.type = DrmBufferType::SHARED_MEMORY;
+ IMemoryToSharedBuffer(
+ mDecryptDestination, mHeapSeqNum, &destination.nonsecureMemory);
+ encryptedBuffer->getMappedBlock(&mappedBlock);
+ hardware::drm::V1_0::SharedBuffer source;
+ encryptedBuffer->fillSourceBuffer(&source);
+ for (int i = 0 ; i < bufferInfos->value.size(); i++) {
+ if (bufferInfos->value[i].mSize > 0) {
+ std::unique_ptr<CodecCryptoInfo> info =
+ std::move(cryptoInfos->value[cryptoInfoIdx++]);
+ if (info->mNumSubSamples == 1
+ && info->mSubSamples[0].mNumBytesOfClearData == 0
+ && info->mSubSamples[0].mNumBytesOfEncryptedData == 0) {
+ // no data so we only populate the bufferInfo
+ result = 0;
+ } else {
+ result = mCrypto->decrypt(
+ (uint8_t*)info->mKey,
+ (uint8_t*)info->mIv,
+ info->mMode,
+ info->mPattern,
+ source,
+ inBufferOffset,
+ info->mSubSamples,
+ info->mNumSubSamples,
+ destination,
+ errorDetailMsg);
+ inBufferOffset += bufferInfos->value[i].mSize;
+ if (result < 0) {
+ ALOGI("[%s] decrypt failed: result=%zd", mName, result);
+ return result;
+ }
+ if (destination.type == DrmBufferType::SHARED_MEMORY && mappedBlock) {
+ mappedBlock->copyDecryptedContent(mDecryptDestination, result);
+ }
+ bufferInfos->value[i].mSize = result;
+ outBufferSize += result;
+ }
+ }
+ }
+ buffer->setRange(codecDataOffset, outBufferSize - codecDataOffset);
+ }
+ return queueInputBufferInternal(buffer, block, bufferSize);
+}
+
void CCodecBufferChannel::feedInputBufferIfAvailable() {
QueueGuard guard(mSync);
if (!guard.isRunning()) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 8dc9fb6..b470655 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -73,6 +73,10 @@
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
AString *errorDetailMsg) override;
+ status_t queueSecureInputBuffers(
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ AString *errorDetailMsg) override;
status_t attachBuffer(
const std::shared_ptr<C2Buffer> &c2Buffer,
const sp<MediaCodecBuffer> &buffer) override;
@@ -88,6 +92,12 @@
size_t numSubSamples,
const sp<MediaCodecBuffer> &buffer,
AString* errorDetailMsg) override;
+ status_t attachEncryptedBuffers(
+ const sp<hardware::HidlMemory> &memory,
+ size_t offset,
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ AString* errorDetailMsg) override;
status_t renderOutputBuffer(
const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
void pollForRenderedBuffers() override;
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 8a48777..d313f33 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -160,6 +160,12 @@
SkipCutBuffer(skip, cut, num16BitChannels),
mFrontPaddingDelay(0), mSize(0) {
}
+ void clearAll() {
+ mInfos.clear();
+ mFrontPaddingDelay = 0;
+ mSize = 0;
+ SkipCutBuffer::clear();
+ }
virtual ~MultiAccessUnitSkipCutBuffer() {
@@ -1378,7 +1384,7 @@
(void)flushedWork;
mImpl.flush();
if (mSkipCutBuffer != nullptr) {
- mSkipCutBuffer->clear();
+ mSkipCutBuffer->clearAll();
}
}
@@ -1536,7 +1542,7 @@
void LinearOutputBuffers::flush(
const std::list<std::unique_ptr<C2Work>> &flushedWork) {
if (mSkipCutBuffer != nullptr) {
- mSkipCutBuffer->clear();
+ mSkipCutBuffer->clearAll();
}
FlexOutputBuffers::flush(flushedWork);
}
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 4f466c5..9c514f2 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -1036,6 +1036,37 @@
return const_cast<native_handle_t *>(mBlock->handle());
}
+void EncryptedLinearBlockBuffer::getMappedBlock(
+ std::unique_ptr<MappedBlock> * const mappedBlock) const {
+ if (mappedBlock) {
+ mappedBlock->reset(new EncryptedLinearBlockBuffer::MappedBlock(mBlock));
+ }
+ return;
+}
+
+EncryptedLinearBlockBuffer::MappedBlock::MappedBlock(
+ const std::shared_ptr<C2LinearBlock> &block) : mView(block->map().get()) {
+}
+
+bool EncryptedLinearBlockBuffer::MappedBlock::copyDecryptedContent(
+ const sp<IMemory> &decrypted, size_t length) {
+ if (mView.error() != C2_OK) {
+ return false;
+ }
+ if (mView.size() < length) {
+ ALOGE("View size(%d) less than decrypted length(%zu)",
+ mView.size(), length);
+ return false;
+ }
+ memcpy(mView.data(), decrypted->unsecurePointer(), length);
+ mView.setOffset(mView.offset() + length);
+ return true;
+}
+
+EncryptedLinearBlockBuffer::MappedBlock::~MappedBlock() {
+ mView.setOffset(0);
+}
+
using ::aidl::android::hardware::graphics::common::Cta861_3;
using ::aidl::android::hardware::graphics::common::Smpte2086;
diff --git a/media/codec2/sfplugin/Codec2Buffer.h b/media/codec2/sfplugin/Codec2Buffer.h
index b73acab..5e96921 100644
--- a/media/codec2/sfplugin/Codec2Buffer.h
+++ b/media/codec2/sfplugin/Codec2Buffer.h
@@ -384,6 +384,17 @@
*/
native_handle_t *handle() const;
+ class MappedBlock {
+ public:
+ explicit MappedBlock(const std::shared_ptr<C2LinearBlock> &block);
+ virtual ~MappedBlock();
+ bool copyDecryptedContent(const sp<IMemory> &decrypted, size_t length);
+ private:
+ C2WriteView mView;
+ };
+
+ void getMappedBlock(std::unique_ptr<MappedBlock> * const mappedBlock) const;
+
private:
std::shared_ptr<C2LinearBlock> mBlock;
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index 71ffefb..971b5a5 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -939,7 +939,7 @@
return 0;
}
-bool EXtractMetadataFromCodec2GrallocHandle(
+bool ExtractMetadataFromCodec2GrallocHandle(
const C2Handle *const handle,
uint32_t *width, uint32_t *height, uint32_t *format, uint64_t *usage, uint32_t *stride) {
if (handle == nullptr) {
@@ -959,7 +959,7 @@
(void)C2HandleAhwb::Import(handle, width, height, format, usage, stride, &origId);
return true;
}
- ALOGE("EXtractMetadata from non compatible handle");
+ ALOGE("ExtractMetadata from non compatible handle");
return false;
}
diff --git a/media/libaaudio/examples/Android.bp b/media/libaaudio/examples/Android.bp
index e2c1878..aa3ae5e 100644
--- a/media/libaaudio/examples/Android.bp
+++ b/media/libaaudio/examples/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libaaudio/examples/input_monitor/Android.bp b/media/libaaudio/examples/input_monitor/Android.bp
index 72adfd7..52a5914 100644
--- a/media/libaaudio/examples/input_monitor/Android.bp
+++ b/media/libaaudio/examples/input_monitor/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -11,7 +12,10 @@
name: "input_monitor",
gtest: false,
srcs: ["src/input_monitor.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
}
@@ -20,7 +24,10 @@
name: "input_monitor_callback",
gtest: false,
srcs: ["src/input_monitor_callback.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
}
diff --git a/media/libaaudio/examples/loopback/Android.bp b/media/libaaudio/examples/loopback/Android.bp
index b18aeec..6552113 100644
--- a/media/libaaudio/examples/loopback/Android.bp
+++ b/media/libaaudio/examples/loopback/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -11,13 +12,16 @@
name: "aaudio_loopback",
gtest: false,
srcs: ["src/loopback.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
static_libs: ["libsndfile"],
include_dirs: ["external/oboe/apps/OboeTester/app/src/main/cpp"],
shared_libs: [
"libaaudio",
"libaudioutils",
- "liblog"
- ],
+ "liblog",
+ ],
header_libs: ["libaaudio_example_utils"],
}
diff --git a/media/libaaudio/examples/write_sine/Android.bp b/media/libaaudio/examples/write_sine/Android.bp
index 1c7e0f1..fe78112 100644
--- a/media/libaaudio/examples/write_sine/Android.bp
+++ b/media/libaaudio/examples/write_sine/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -10,7 +11,10 @@
cc_test {
name: "write_sine",
srcs: ["src/write_sine.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
}
@@ -18,7 +22,10 @@
cc_test {
name: "write_sine_callback",
srcs: ["src/write_sine_callback.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: ["libaaudio"],
header_libs: ["libaaudio_example_utils"],
}
diff --git a/media/libaaudio/fuzzer/Android.bp b/media/libaaudio/fuzzer/Android.bp
index 46c4148..6d94f38 100644
--- a/media/libaaudio/fuzzer/Android.bp
+++ b/media/libaaudio/fuzzer/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index fcb376c..d2cb265 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -128,7 +129,7 @@
tidy_checks_as_errors: tidy_errors,
tidy_flags: [
"-format-style=file",
- ]
+ ],
}
cc_library {
@@ -250,7 +251,7 @@
tidy_checks_as_errors: tidy_errors,
tidy_flags: [
"-format-style=file",
- ]
+ ],
}
aidl_interface {
@@ -274,8 +275,7 @@
"shared-file-region-aidl",
"framework-permission-aidl",
],
- backend:
- {
+ backend: {
java: {
sdk_version: "module_current",
},
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index a39e90e..430ba83 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -18,7 +18,6 @@
//#define LOG_NDEBUG 0
#include <log/log.h>
-#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <algorithm>
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 30efeb0..5ec8276 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -183,9 +184,9 @@
defaults: ["libaaudio_tests_defaults"],
srcs: ["test_full_queue.cpp"],
shared_libs: [
- "libaaudio",
- "liblog"
- ],
+ "libaaudio",
+ "liblog",
+ ],
}
cc_test {
@@ -271,7 +272,7 @@
{
name: "native-test-timeout",
value: "2m",
- }
+ },
],
},
}
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 369e917..6cc5d63 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -52,7 +53,7 @@
"AudioPolicy.cpp",
"AudioProductStrategy.cpp",
"AudioVolumeGroup.cpp",
- "PolicyAidlConversion.cpp"
+ "PolicyAidlConversion.cpp",
],
defaults: [
"latest_android_media_audio_common_types_cpp_export_shared",
@@ -333,6 +334,7 @@
},
},
}
+
aidl_interface {
name: "audiopolicy-types-aidl",
unstable: true,
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 565427b..98a1fde 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -1699,29 +1699,42 @@
}
status_t AudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+ status_t result = NO_ERROR;
AutoMutex lock(mLock);
- ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d mRoutedDeviceId %d",
- __func__, mPortId, deviceId, mSelectedDeviceId, mRoutedDeviceId);
+ ALOGV("%s(%d): deviceId=%d mSelectedDeviceId=%d",
+ __func__, mPortId, deviceId, mSelectedDeviceId);
if (mSelectedDeviceId != deviceId) {
mSelectedDeviceId = deviceId;
if (mStatus == NO_ERROR) {
- // allow track invalidation when track is not playing to propagate
- // the updated mSelectedDeviceId
- if (isPlaying_l()) {
- if (mSelectedDeviceId != mRoutedDeviceId) {
- android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
- mProxy->interrupt();
+ if (isOffloadedOrDirect_l()) {
+ if (mState == STATE_STOPPED || mState == STATE_FLUSHED) {
+ ALOGD("%s(%d): creating a new AudioTrack", __func__, mPortId);
+ result = restoreTrack_l("setOutputDevice", true /* forceRestore */);
+ } else {
+ ALOGW("%s(%d). Offloaded or Direct track is not STOPPED or FLUSHED. "
+ "State: %s.",
+ __func__, mPortId, stateToString(mState));
+ result = INVALID_OPERATION;
}
} else {
- // if the track is idle, try to restore now and
- // defer to next start if not possible
- if (restoreTrack_l("setOutputDevice") != OK) {
- android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+ // allow track invalidation when track is not playing to propagate
+ // the updated mSelectedDeviceId
+ if (isPlaying_l()) {
+ if (mSelectedDeviceId != mRoutedDeviceId) {
+ android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+ mProxy->interrupt();
+ }
+ } else {
+ // if the track is idle, try to restore now and
+ // defer to next start if not possible
+ if (restoreTrack_l("setOutputDevice") != OK) {
+ android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
+ }
}
}
}
}
- return NO_ERROR;
+ return result;
}
audio_port_handle_t AudioTrack::getOutputDevice() {
@@ -2836,7 +2849,7 @@
return 0;
}
-status_t AudioTrack::restoreTrack_l(const char *from)
+status_t AudioTrack::restoreTrack_l(const char *from, bool forceRestore)
{
status_t result = NO_ERROR; // logged: make sure to set this before returning.
const int64_t beginNs = systemTime();
@@ -2857,7 +2870,8 @@
// output parameters and new IAudioFlinger in createTrack_l()
AudioSystem::clearAudioConfigCache();
- if (isOffloadedOrDirect_l() || mDoNotReconnect) {
+ if (!forceRestore &&
+ (isOffloadedOrDirect_l() || mDoNotReconnect)) {
// FIXME re-creation of offloaded and direct tracks is not yet implemented;
// Disabled since (1) timestamp correction is not implemented for non-PCM and
// (2) We pre-empt existing direct tracks on resource constraint, so these tracks
diff --git a/media/libaudioclient/aidl/fuzzer/Android.bp b/media/libaudioclient/aidl/fuzzer/Android.bp
index 6093933..02c5a3f 100644
--- a/media/libaudioclient/aidl/fuzzer/Android.bp
+++ b/media/libaudioclient/aidl/fuzzer/Android.bp
@@ -1,4 +1,8 @@
/*
+package {
+ default_team: "trendy_team_media_framework_audio",
+}
+
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -71,7 +75,7 @@
"libbinder_headers",
"libmedia_headers",
],
- fuzz_config: {
+ fuzz_config: {
cc: [
"android-media-fuzzing-reports@google.com",
],
@@ -90,6 +94,6 @@
srcs: ["audioflinger_aidl_fuzzer.cpp"],
defaults: [
"libaudioclient_aidl_fuzzer_defaults",
- "service_fuzzer_defaults"
+ "service_fuzzer_defaults",
],
}
diff --git a/media/libaudioclient/fuzzer/Android.bp b/media/libaudioclient/fuzzer/Android.bp
index fd3b0a8..f2ad91c 100644
--- a/media/libaudioclient/fuzzer/Android.bp
+++ b/media/libaudioclient/fuzzer/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 523383f..19780ae 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -1220,7 +1220,7 @@
void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
// FIXME enum is faster than strcmp() for parameter 'from'
- status_t restoreTrack_l(const char *from);
+ status_t restoreTrack_l(const char *from, bool forceRestore = false);
uint32_t getUnderrunCount_l() const;
diff --git a/media/libaudioclient/tests/Android.bp b/media/libaudioclient/tests/Android.bp
index 0da242d..b667c8d 100644
--- a/media/libaudioclient/tests/Android.bp
+++ b/media/libaudioclient/tests/Android.bp
@@ -134,6 +134,10 @@
"libaudiopolicy",
],
data: ["bbb*.raw"],
+ srcs: [
+ "audio_test_utils.cpp",
+ "test_execution_tracer.cpp",
+ ],
test_config_template: "audio_test_template.xml",
}
@@ -142,7 +146,6 @@
defaults: ["libaudioclient_gtests_defaults"],
srcs: [
"audiorecord_tests.cpp",
- "audio_test_utils.cpp",
],
}
@@ -151,7 +154,6 @@
defaults: ["libaudioclient_gtests_defaults"],
srcs: [
"audiotrack_tests.cpp",
- "audio_test_utils.cpp",
],
}
@@ -160,7 +162,6 @@
defaults: ["libaudioclient_gtests_defaults"],
srcs: [
"audioeffect_tests.cpp",
- "audio_test_utils.cpp",
],
}
@@ -173,7 +174,6 @@
],
srcs: [
"audioeffect_analyser.cpp",
- "audio_test_utils.cpp",
],
static_libs: [
"libpffft",
@@ -185,7 +185,6 @@
defaults: ["libaudioclient_gtests_defaults"],
srcs: [
"audiorouting_tests.cpp",
- "audio_test_utils.cpp",
],
}
@@ -194,14 +193,15 @@
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"],
+ srcs: [
+ "trackplayerbase_tests.cpp",
+ ],
}
cc_test {
@@ -209,6 +209,5 @@
defaults: ["libaudioclient_gtests_defaults"],
srcs: [
"audiosystem_tests.cpp",
- "audio_test_utils.cpp",
],
}
diff --git a/media/libaudioclient/tests/audioclient_serialization_tests.cpp b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
index 707b9b3..5debabc 100644
--- a/media/libaudioclient/tests/audioclient_serialization_tests.cpp
+++ b/media/libaudioclient/tests/audioclient_serialization_tests.cpp
@@ -15,18 +15,23 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "AudioClientSerializationUnitTests"
+#define LOG_TAG "AudioClientSerializationTests"
#include <cstdint>
#include <cstdlib>
#include <ctime>
-
-#include <gtest/gtest.h>
+#include <vector>
#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <gtest/gtest.h>
+#include <media/AudioPolicy.h>
+#include <media/AudioProductStrategy.h>
+#include <media/AudioVolumeGroup.h>
+#include <media/VolumeGroupAttributes.h>
+#include <system/audio.h>
#include <xsdc/XsdcSupport.h>
-#include "audio_test_utils.h"
+#include "test_execution_tracer.h"
using namespace android;
namespace xsd {
@@ -310,3 +315,9 @@
// audioStream
INSTANTIATE_TEST_SUITE_P(SerializationParameterizedTests, AudioAttributesParameterizedTest,
::testing::Combine(testing::ValuesIn(kStreamtypes)));
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libaudioclient/tests/audioeffect_analyser.cpp b/media/libaudioclient/tests/audioeffect_analyser.cpp
index 94accae..f4d37bc 100644
--- a/media/libaudioclient/tests/audioeffect_analyser.cpp
+++ b/media/libaudioclient/tests/audioeffect_analyser.cpp
@@ -14,23 +14,26 @@
* limitations under the License.
*/
-// #define LOG_NDEBUG 0
-#define LOG_TAG "AudioEffectAnalyser"
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <gtest/gtest.h>
-#include <media/AudioEffect.h>
-#include <system/audio_effects/effect_bassboost.h>
-#include <system/audio_effects/effect_equalizer.h>
#include <fstream>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
+// #define LOG_NDEBUG 0
+#define LOG_TAG "AudioEffectAnalyser"
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <media/AudioEffect.h>
+#include <system/audio_effects/effect_bassboost.h>
+#include <system/audio_effects/effect_equalizer.h>
+
#include "audio_test_utils.h"
#include "pffft.hpp"
+#include "test_execution_tracer.h"
#define CHECK_OK(expr, msg) \
mStatus = (expr); \
@@ -417,3 +420,10 @@
prevGain = diffB;
}
}
+
+int main(int argc, char** argv) {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libaudioclient/tests/audioeffect_tests.cpp b/media/libaudioclient/tests/audioeffect_tests.cpp
index e12ae23..59d0c6a 100644
--- a/media/libaudioclient/tests/audioeffect_tests.cpp
+++ b/media/libaudioclient/tests/audioeffect_tests.cpp
@@ -15,8 +15,9 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "AudioEffectUnitTests"
+#define LOG_TAG "AudioEffectTests"
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <media/AudioEffect.h>
#include <system/audio_effects/effect_hapticgenerator.h>
@@ -24,6 +25,7 @@
#include <system/audio_effects/effect_visualizer.h>
#include "audio_test_utils.h"
+#include "test_execution_tracer.h"
using namespace android;
@@ -563,3 +565,10 @@
EXPECT_TRUE(cb->receivedFramesProcessed)
<< "AudioEffect frames processed callback not received";
}
+
+int main(int argc, char** argv) {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libaudioclient/tests/audiorecord_tests.cpp b/media/libaudioclient/tests/audiorecord_tests.cpp
index 61edd4d..0bf2e82 100644
--- a/media/libaudioclient/tests/audiorecord_tests.cpp
+++ b/media/libaudioclient/tests/audiorecord_tests.cpp
@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
#include "audio_test_utils.h"
+#include "test_execution_tracer.h"
using namespace android;
@@ -261,26 +262,6 @@
AUDIO_SOURCE_UNPROCESSED)),
GetRecordTestName);
-namespace {
-
-class TestExecutionTracer : public ::testing::EmptyTestEventListener {
- public:
- void OnTestStart(const ::testing::TestInfo& test_info) override {
- TraceTestState("Started", test_info);
- }
- void OnTestEnd(const ::testing::TestInfo& test_info) override {
- TraceTestState("Finished", test_info);
- }
- void OnTestPartResult(const ::testing::TestPartResult& result) override { LOG(INFO) << result; }
-
- private:
- static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
- LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
- }
-};
-
-} // namespace
-
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/media/libaudioclient/tests/audiorouting_tests.cpp b/media/libaudioclient/tests/audiorouting_tests.cpp
index c101f00..8f76f9b 100644
--- a/media/libaudioclient/tests/audiorouting_tests.cpp
+++ b/media/libaudioclient/tests/audiorouting_tests.cpp
@@ -17,13 +17,12 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioRoutingTest"
-#include <string.h>
-
#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
#include "audio_test_utils.h"
+#include "test_execution_tracer.h"
using namespace android;
@@ -267,21 +266,6 @@
playback->stop();
}
-class TestExecutionTracer : public ::testing::EmptyTestEventListener {
- public:
- void OnTestStart(const ::testing::TestInfo& test_info) override {
- TraceTestState("Started", test_info);
- }
- void OnTestEnd(const ::testing::TestInfo& test_info) override {
- TraceTestState("Completed", test_info);
- }
-
- private:
- static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
- ALOGI("%s %s::%s", state.c_str(), test_info.test_suite_name(), test_info.name());
- }
-};
-
int main(int argc, char** argv) {
android::ProcessState::self()->startThreadPool();
::testing::InitGoogleTest(&argc, argv);
diff --git a/media/libaudioclient/tests/audiosystem_tests.cpp b/media/libaudioclient/tests/audiosystem_tests.cpp
index d9789f1..03c15f4 100644
--- a/media/libaudioclient/tests/audiosystem_tests.cpp
+++ b/media/libaudioclient/tests/audiosystem_tests.cpp
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-#define LOG_TAG "AudioSystemTest"
-
#include <string.h>
#include <set>
+#define LOG_TAG "AudioSystemTest"
+
#include <gtest/gtest.h>
#include <log/log.h>
#include <media/AidlConversionCppNdk.h>
#include <media/IAudioFlinger.h>
#include "audio_test_utils.h"
+#include "test_execution_tracer.h"
using android::media::audio::common::AudioDeviceAddress;
using android::media::audio::common::AudioDeviceDescription;
@@ -706,21 +707,6 @@
}
}
-class TestExecutionTracer : public ::testing::EmptyTestEventListener {
- public:
- void OnTestStart(const ::testing::TestInfo& test_info) override {
- TraceTestState("Started", test_info);
- }
- void OnTestEnd(const ::testing::TestInfo& test_info) override {
- TraceTestState("Completed", test_info);
- }
-
- private:
- static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
- ALOGI("%s %s::%s", state.c_str(), test_info.test_suite_name(), test_info.name());
- }
-};
-
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
diff --git a/media/libaudioclient/tests/audiotrack_tests.cpp b/media/libaudioclient/tests/audiotrack_tests.cpp
index 2b68225..0282bd7 100644
--- a/media/libaudioclient/tests/audiotrack_tests.cpp
+++ b/media/libaudioclient/tests/audiotrack_tests.cpp
@@ -15,10 +15,13 @@
*/
//#define LOG_NDEBUG 0
+#define LOG_TAG "AudioTrackTests"
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include "audio_test_utils.h"
+#include "test_execution_tracer.h"
using namespace android;
@@ -209,3 +212,10 @@
AUDIO_OUTPUT_FLAG_RAW | AUDIO_OUTPUT_FLAG_FAST,
AUDIO_OUTPUT_FLAG_DEEP_BUFFER),
::testing::Values(AUDIO_SESSION_NONE)));
+
+int main(int argc, char** argv) {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libaudioclient/tests/test_execution_tracer.cpp b/media/libaudioclient/tests/test_execution_tracer.cpp
new file mode 100644
index 0000000..797bb4b
--- /dev/null
+++ b/media/libaudioclient/tests/test_execution_tracer.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 "TestExecutionTracer"
+
+#include "test_execution_tracer.h"
+
+#include <android-base/logging.h>
+
+void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) {
+ TraceTestState("Started", test_info);
+}
+
+void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) {
+ TraceTestState("Finished", test_info);
+}
+
+void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) {
+ if (result.failed()) {
+ LOG(ERROR) << result;
+ } else {
+ LOG(INFO) << result;
+ }
+}
+
+// static
+void TestExecutionTracer::TraceTestState(const std::string& state,
+ const ::testing::TestInfo& test_info) {
+ LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+}
diff --git a/media/libaudioclient/tests/test_execution_tracer.h b/media/libaudioclient/tests/test_execution_tracer.h
new file mode 100644
index 0000000..9031aaf
--- /dev/null
+++ b/media/libaudioclient/tests/test_execution_tracer.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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 <gtest/gtest.h>
+
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+ public:
+ void OnTestStart(const ::testing::TestInfo& test_info) override;
+ void OnTestEnd(const ::testing::TestInfo& test_info) override;
+ void OnTestPartResult(const ::testing::TestPartResult& result) override;
+
+ private:
+ static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info);
+};
diff --git a/media/libaudioclient/tests/trackplayerbase_tests.cpp b/media/libaudioclient/tests/trackplayerbase_tests.cpp
index c9b704d..7317bf0 100644
--- a/media/libaudioclient/tests/trackplayerbase_tests.cpp
+++ b/media/libaudioclient/tests/trackplayerbase_tests.cpp
@@ -16,10 +16,12 @@
#define LOG_TAG "TrackPlayerBaseTest"
+#include <binder/ProcessState.h>
#include <gtest/gtest.h>
-
#include <media/TrackPlayerBase.h>
+#include "test_execution_tracer.h"
+
using namespace android;
using namespace android::media;
@@ -159,3 +161,10 @@
INSTANTIATE_TEST_SUITE_P(TrackPlayerTest, PauseTestParam,
::testing::Values(std::make_tuple(1.0, 75.0, 2, 24000)));
+
+int main(int argc, char** argv) {
+ android::ProcessState::self()->startThreadPool();
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ return RUN_ALL_TESTS();
+}
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index a601ad5..14765f6b 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -274,15 +274,16 @@
return parseAndGetVendorParameters(mVendorExt, mModule, parameterKeys, values);
}
-status_t DeviceHalAidl::getInputBufferSize(const struct audio_config* config, size_t* size) {
+status_t DeviceHalAidl::getInputBufferSize(struct audio_config* config, size_t* size) {
ALOGD("%p %s::%s", this, getClassName().c_str(), __func__);
TIME_CHECK();
if (mModule == nullptr) return NO_INIT;
if (config == nullptr || size == nullptr) {
return BAD_VALUE;
}
+ constexpr bool isInput = true;
AudioConfig aidlConfig = VALUE_OR_RETURN_STATUS(
- ::aidl::android::legacy2aidl_audio_config_t_AudioConfig(*config, true /*isInput*/));
+ ::aidl::android::legacy2aidl_audio_config_t_AudioConfig(*config, isInput));
AudioDevice aidlDevice;
aidlDevice.type.type = AudioDeviceType::IN_DEFAULT;
AudioSource aidlSource = AudioSource::DEFAULT;
@@ -296,6 +297,9 @@
0 /*handle*/, aidlDevice, aidlFlags, aidlSource,
&cleanups, &aidlConfig, &mixPortConfig, &aidlPatch));
}
+ *config = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_AudioConfig_audio_config_t(aidlConfig, isInput));
+ if (mixPortConfig.id == 0) return BAD_VALUE; // HAL suggests a different config.
*size = aidlConfig.frameCount *
getFrameSizeInBytes(aidlConfig.base.format, aidlConfig.base.channelMask);
// Do not disarm cleanups to release temporary port configs.
@@ -401,8 +405,7 @@
*static_cast<StreamCallbackBase*>(this)),
StreamCallbackBaseHelper<StreamOutHalInterfaceLatencyModeCallback>(
*static_cast<StreamCallbackBase*>(this)) {}
- ndk::ScopedAStatus onCodecFormatChanged(const std::vector<uint8_t>& in_audioMetadata) override {
- std::basic_string<uint8_t> halMetadata(in_audioMetadata.begin(), in_audioMetadata.end());
+ ndk::ScopedAStatus onCodecFormatChanged(const std::vector<uint8_t>& halMetadata) override {
return StreamCallbackBaseHelper<StreamOutHalInterfaceEventCallback>::runCb(
[&halMetadata](auto cb) { cb->onCodecFormatChanged(halMetadata); });
}
diff --git a/media/libaudiohal/impl/DeviceHalAidl.h b/media/libaudiohal/impl/DeviceHalAidl.h
index f705db7..d925b46 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.h
+++ b/media/libaudiohal/impl/DeviceHalAidl.h
@@ -113,7 +113,7 @@
status_t getParameters(const String8& keys, String8 *values) override;
// Returns audio input buffer size according to parameters passed.
- status_t getInputBufferSize(const struct audio_config* config, size_t* size) override;
+ status_t getInputBufferSize(struct audio_config* config, size_t* size) override;
// Creates and opens the audio hardware output stream. The stream is closed
// by releasing all references to the returned object.
diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp
index e8e1f46..478e0f0 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalHidl.cpp
@@ -236,7 +236,7 @@
}
status_t DeviceHalHidl::getInputBufferSize(
- const struct audio_config *config, size_t *size) {
+ struct audio_config *config, size_t *size) {
TIME_CHECK();
if (mDevice == 0) return NO_INIT;
AudioConfig hidlConfig;
diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h
index 7a712df..1362dab 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.h
+++ b/media/libaudiohal/impl/DeviceHalHidl.h
@@ -67,7 +67,7 @@
status_t getParameters(const String8& keys, String8 *values) override;
// Returns audio input buffer size according to parameters passed.
- status_t getInputBufferSize(const struct audio_config* config, size_t* size) override;
+ status_t getInputBufferSize(struct audio_config* config, size_t* size) override;
// Creates and opens the audio hardware output stream. The stream is closed
// by releasing all references to the returned object.
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
index 01fc7fb..347afa6 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
@@ -26,6 +26,7 @@
#include <aidl/android/hardware/audio/core/IModule.h>
#include <aidl/android/media/audio/BnHalAdapterVendorExtension.h>
#include <android/binder_manager.h>
+#include <cutils/properties.h>
#include <media/AidlConversionNdkCpp.h>
#include <media/AidlConversionUtil.h>
#include <utils/Log.h>
@@ -121,8 +122,8 @@
std::shared_ptr<IHalAdapterVendorExtension> getService(bool reset = false) {
std::lock_guard l(mLock);
if (reset || !mVendorExt.has_value()) {
- auto serviceName = std::string(IHalAdapterVendorExtension::descriptor) + "/default";
- if (AServiceManager_isDeclared(serviceName.c_str())) {
+ if (property_get_bool("ro.audio.ihaladaptervendorextension_enabled", false)) {
+ auto serviceName = std::string(IHalAdapterVendorExtension::descriptor) + "/default";
mVendorExt = std::shared_ptr<IHalAdapterVendorExtension>(
IHalAdapterVendorExtension::fromBinder(ndk::SpAIBinder(
AServiceManager_waitForService(serviceName.c_str()))));
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index c4841c5..0c0184e 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -69,9 +69,6 @@
void* pReplyData);
private:
- const aidl::android::media::audio::common::AudioFormatDescription kDefaultFormatDescription = {
- .type = aidl::android::media::audio::common::AudioFormatType::PCM,
- .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT};
const bool mIsProxyEffect;
static constexpr int kDefaultframeCount = 0x100;
@@ -81,13 +78,16 @@
return pt ? std::to_string(*pt) : "nullptr";
}
- using AudioChannelLayout = aidl::android::media::audio::common::AudioChannelLayout;
const aidl::android::media::audio::common::AudioConfig kDefaultAudioConfig = {
.base = {.sampleRate = 44100,
- .channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
- AudioChannelLayout::LAYOUT_STEREO),
- .format = kDefaultFormatDescription},
+ .channelMask = aidl::android::media::audio::common::AudioChannelLayout::make<
+ aidl::android::media::audio::common::AudioChannelLayout::layoutMask>(
+ aidl::android::media::audio::common::AudioChannelLayout::
+ LAYOUT_STEREO),
+ .format = {.type = aidl::android::media::audio::common::AudioFormatType::PCM,
+ .pcm = aidl::android::media::audio::common::PcmType::FLOAT_32_BIT}},
.frameCount = kDefaultframeCount};
+
// command handler map
typedef status_t (EffectConversionHelperAidl::*CommandHandler)(uint32_t /* cmdSize */,
const void* /* pCmdData */,
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
index ebda86a..b1b1dfe 100644
--- a/media/libaudiohal/impl/EffectHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -53,6 +53,7 @@
#include "effectsAidlConversion/AidlConversionVisualizer.h"
using ::aidl::android::aidl_utils::statusTFromBinderStatus;
+using ::aidl::android::hardware::audio::effect::CommandId;
using ::aidl::android::hardware::audio::effect::Descriptor;
using ::aidl::android::hardware::audio::effect::IEffect;
using ::aidl::android::hardware::audio::effect::IFactory;
@@ -285,6 +286,7 @@
status_t EffectHalAidl::close() {
TIME_CHECK();
+ mEffect->command(CommandId::STOP);
return statusTFromBinderStatus(mEffect->close());
}
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 72eadc6..77c75db 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -840,7 +840,7 @@
const android::hardware::hidl_vec<uint8_t>& audioMetadata) override {
sp<StreamOutHalHidl> stream = mStream.promote();
if (stream != nullptr) {
- std::basic_string<uint8_t> metadataBs(audioMetadata.begin(), audioMetadata.end());
+ std::vector<uint8_t> metadataBs(audioMetadata.begin(), audioMetadata.end());
stream->onCodecFormatChanged(metadataBs);
}
return Void();
@@ -967,7 +967,7 @@
callback->onError();
}
-void StreamOutHalHidl::onCodecFormatChanged(const std::basic_string<uint8_t>& metadataBs) {
+void StreamOutHalHidl::onCodecFormatChanged(const std::vector<uint8_t>& metadataBs) {
sp<StreamOutHalInterfaceEventCallback> callback = mEventCallback.load().promote();
if (callback == nullptr) return;
ALOGV("asyncCodecFormatCallback %s", __func__);
diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h
index 5361047..48da633 100644
--- a/media/libaudiohal/impl/StreamHalHidl.h
+++ b/media/libaudiohal/impl/StreamHalHidl.h
@@ -194,7 +194,7 @@
status_t setEventCallback(const sp<StreamOutHalInterfaceEventCallback>& callback) override;
// Methods used by StreamCodecFormatCallback (HIDL).
- void onCodecFormatChanged(const std::basic_string<uint8_t>& metadataBs);
+ void onCodecFormatChanged(const std::vector<uint8_t>& metadataBs);
status_t setLatencyMode(audio_latency_mode_t mode) override;
status_t getRecommendedLatencyModes(std::vector<audio_latency_mode_t> *modes) override;
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
index 49e6827..d1794f0 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.cpp
@@ -16,17 +16,17 @@
#include <cstdint>
#include <cstring>
-#include <optional>
#define LOG_TAG "AidlConversionSpatializer"
//#define LOG_NDEBUG 0
#include <aidl/android/hardware/audio/effect/DefaultExtension.h>
#include <aidl/android/hardware/audio/effect/VendorExtension.h>
#include <error/expected_utils.h>
-#include <media/AidlConversionNdk.h>
+#include <media/AidlConversionCppNdk.h>
#include <media/AidlConversionEffect.h>
+#include <media/AidlConversionNdk.h>
+#include <system/audio_effects/aidl_effects_utils.h>
#include <system/audio_effects/effect_spatializer.h>
-
#include <utils/Log.h>
#include "AidlConversionSpatializer.h"
@@ -34,38 +34,321 @@
namespace android {
namespace effect {
-using ::aidl::android::aidl_utils::statusTFromBinderStatus;
-using ::aidl::android::hardware::audio::effect::DefaultExtension;
-using ::aidl::android::hardware::audio::effect::Parameter;
-using ::aidl::android::hardware::audio::effect::VendorExtension;
-using ::android::status_t;
+using aidl::android::getParameterSpecificField;
+using aidl::android::aidl_utils::statusTFromBinderStatus;
+using aidl::android::hardware::audio::common::SourceMetadata;
+using aidl::android::hardware::audio::effect::DefaultExtension;
+using aidl::android::hardware::audio::effect::Parameter;
+using aidl::android::hardware::audio::effect::Range;
+using aidl::android::hardware::audio::effect::Spatializer;
+using aidl::android::hardware::audio::effect::VendorExtension;
+using aidl::android::media::audio::common::AudioChannelLayout;
+using aidl::android::media::audio::common::HeadTracking;
+using aidl::android::media::audio::common::Spatialization;
+using aidl::android::media::audio::common::toString;
+using android::status_t;
using utils::EffectParamReader;
using utils::EffectParamWriter;
+bool AidlConversionSpatializer::isSpatializerParameterSupported() {
+ return mIsSpatializerAidlParamSupported.value_or(
+ (mIsSpatializerAidlParamSupported =
+ [&]() {
+ ::aidl::android::hardware::audio::effect::Parameter aidlParam;
+ auto id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::vendor);
+ // No range defined in descriptor capability means no Spatializer AIDL
+ // implementation BAD_VALUE return from getParameter indicates the
+ // parameter is not supported by HAL
+ return mDesc.capability.range.getTag() == Range::spatializer &&
+ mEffect->getParameter(id, &aidlParam).getStatus() !=
+ android::BAD_VALUE;
+ }())
+ .value());
+}
+
status_t AidlConversionSpatializer::setParameter(EffectParamReader& param) {
- Parameter aidlParam = VALUE_OR_RETURN_STATUS(
- ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param));
+ Parameter aidlParam;
+ if (isSpatializerParameterSupported()) {
+ uint32_t command = 0;
+ if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) ||
+ OK != param.readFromParameter(&command)) {
+ ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+ return BAD_VALUE;
+ }
+
+ switch (command) {
+ case SPATIALIZER_PARAM_LEVEL: {
+ Spatialization::Level level = Spatialization::Level::NONE;
+ if (OK != param.readFromValue(&level)) {
+ ALOGE("%s invalid level value %s", __func__, param.toString().c_str());
+ return BAD_VALUE;
+ }
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+ spatializationLevel, level);
+ break;
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_MODE: {
+ HeadTracking::Mode mode = HeadTracking::Mode::DISABLED;
+ if (OK != param.readFromValue(&mode)) {
+ ALOGE("%s invalid mode value %s", __func__, param.toString().c_str());
+ return BAD_VALUE;
+ }
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingMode,
+ mode);
+ break;
+ }
+ case SPATIALIZER_PARAM_HEAD_TO_STAGE: {
+ const size_t valueSize = param.getValueSize();
+ if (valueSize / sizeof(float) > 6 || valueSize % sizeof(float) != 0) {
+ ALOGE("%s invalid parameter value size %zu", __func__, valueSize);
+ return BAD_VALUE;
+ }
+ std::array<float, 6> headToStage = {};
+ for (size_t i = 0; i < valueSize / sizeof(float); i++) {
+ if (OK != param.readFromValue(&headToStage[i])) {
+ ALOGE("%s failed to read headToStage from %s", __func__,
+ param.toString().c_str());
+ return BAD_VALUE;
+ }
+ }
+ HeadTracking::SensorData sensorData =
+ HeadTracking::SensorData::make<HeadTracking::SensorData::headToStage>(
+ headToStage);
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+ headTrackingSensorData, sensorData);
+ break;
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: {
+ int32_t modeInt32 = 0;
+ int32_t sensorId = -1;
+ if (OK != param.readFromValue(&modeInt32) || OK != param.readFromValue(&sensorId)) {
+ ALOGE("%s %d invalid parameter value %s", __func__, __LINE__,
+ param.toString().c_str());
+ return BAD_VALUE;
+ }
+
+ const auto mode = static_cast<HeadTracking::ConnectionMode>(modeInt32);
+ if (mode < *ndk::enum_range<HeadTracking::ConnectionMode>().begin() ||
+ mode > *ndk::enum_range<HeadTracking::ConnectionMode>().end()) {
+ ALOGE("%s %d invalid mode %d", __func__, __LINE__, modeInt32);
+ return BAD_VALUE;
+ }
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer,
+ headTrackingConnectionMode, mode);
+ if (status_t status = statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+ status != OK) {
+ ALOGE("%s failed to set headTrackingConnectionMode %s", __func__,
+ toString(mode).c_str());
+ return status;
+ }
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ aidlParam = MAKE_SPECIFIC_PARAMETER(Spatializer, spatializer, headTrackingSensorId,
+ sensorId);
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
+ }
+ default: {
+ ALOGE("%s %d invalid command %u", __func__, __LINE__, command);
+ return BAD_VALUE;
+ }
+ }
+ } else {
+ aidlParam = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::legacy2aidl_EffectParameterReader_Parameter(param));
+ }
+
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
return statusTFromBinderStatus(mEffect->setParameter(aidlParam));
}
status_t AidlConversionSpatializer::getParameter(EffectParamWriter& param) {
- DefaultExtension defaultExt;
- // read parameters into DefaultExtension vector<uint8_t>
- defaultExt.bytes.resize(param.getParameterSize());
- if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) {
- ALOGE("%s invalid param %s", __func__, param.toString().c_str());
- param.setStatus(BAD_VALUE);
- return BAD_VALUE;
- }
+ if (isSpatializerParameterSupported()) {
+ uint32_t command = 0;
+ if (!param.validateParamValueSize(sizeof(uint32_t), sizeof(int8_t)) ||
+ OK != param.readFromParameter(&command)) {
+ ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+ return BAD_VALUE;
+ }
- VendorExtension idTag;
- idTag.extension.setParcelable(defaultExt);
- Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag);
- Parameter aidlParam;
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
- // copy the AIDL extension data back to effect_param_t
- return VALUE_OR_RETURN_STATUS(
- ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param));
+ switch (command) {
+ case SPATIALIZER_PARAM_SUPPORTED_LEVELS: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::spatializationLevel);
+ if (!range) {
+ return BAD_VALUE;
+ }
+ for (const auto level : ::ndk::enum_range<Spatialization::Level>()) {
+ const auto spatializer =
+ Spatializer::make<Spatializer::spatializationLevel>(level);
+ if (spatializer >= range->min && spatializer <= range->max) {
+ if (status_t status = param.writeToValue(&level); status != OK) {
+ ALOGI("%s %d: write level %s to value failed %d", __func__, __LINE__,
+ toString(level).c_str(), status);
+ return status;
+ }
+ }
+ }
+ return OK;
+ }
+ case SPATIALIZER_PARAM_LEVEL: {
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::spatializationLevel);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto level = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer, Spatializer::spatializationLevel,
+ Spatialization::Level));
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return param.writeToValue(&level);
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::spatializationLevel);
+ if (!range) {
+ ALOGE("%s %d: range not defined for spatializationLevel", __func__, __LINE__);
+ return BAD_VALUE;
+ }
+ const auto& nonSupport = Spatializer::make<Spatializer::spatializationLevel>(
+ Spatialization::Level::NONE);
+ const bool support = (range->min > range->max ||
+ (range->min == nonSupport && range->max == nonSupport))
+ ? false
+ : true;
+ return param.writeToValue(&support);
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_MODE: {
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::headTrackingMode);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer, Spatializer::headTrackingMode,
+ HeadTracking::Mode));
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return param.writeToValue(&mode);
+ }
+ case SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS: {
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::supportedChannelLayout);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto& supportedLayouts = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer, Spatializer::supportedChannelLayout,
+ std::vector<AudioChannelLayout>));
+ for (const auto& layout : supportedLayouts) {
+ audio_channel_mask_t mask = VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
+ layout, false /* isInput */));
+ if (status_t status = param.writeToValue(&mask); status != OK) {
+ ALOGI("%s %d: write mask %s to value failed %d", __func__, __LINE__,
+ layout.toString().c_str(), status);
+ return status;
+ }
+ }
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return OK;
+ }
+ case SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::spatializationMode);
+ if (!range) {
+ return BAD_VALUE;
+ }
+ for (const auto mode : ::ndk::enum_range<Spatialization::Mode>()) {
+ if (const auto spatializer =
+ Spatializer::make<Spatializer::spatializationMode>(mode);
+ spatializer >= range->min && spatializer <= range->max) {
+ if (status_t status = param.writeToValue(&mode); status != OK) {
+ ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+ toString(mode).c_str(), status);
+ return status;
+ }
+ }
+ }
+ return OK;
+ }
+ case SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION: {
+ const auto& range = getRange<Range::spatializer, Range::SpatializerRange>(
+ mDesc.capability, Spatializer::headTrackingConnectionMode);
+ if (!range) {
+ return BAD_VALUE;
+ }
+ for (const auto mode : ::ndk::enum_range<HeadTracking::ConnectionMode>()) {
+ if (const auto spatializer =
+ Spatializer::make<Spatializer::headTrackingConnectionMode>(mode);
+ spatializer < range->min || spatializer > range->max) {
+ continue;
+ }
+ if (status_t status = param.writeToValue(&mode); status != OK) {
+ ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+ toString(mode).c_str(), status);
+ return status;
+ }
+ }
+ return OK;
+ }
+ case SPATIALIZER_PARAM_HEADTRACKING_CONNECTION: {
+ status_t status = OK;
+ Parameter aidlParam;
+ Parameter::Id id = MAKE_SPECIFIC_PARAMETER_ID(
+ Spatializer, spatializerTag, Spatializer::headTrackingConnectionMode);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto mode = VALUE_OR_RETURN_STATUS(GET_PARAMETER_SPECIFIC_FIELD(
+ aidlParam, Spatializer, spatializer,
+ Spatializer::headTrackingConnectionMode, HeadTracking::ConnectionMode));
+
+ id = MAKE_SPECIFIC_PARAMETER_ID(Spatializer, spatializerTag,
+ Spatializer::headTrackingSensorId);
+ RETURN_STATUS_IF_ERROR(
+ statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ const auto sensorId = VALUE_OR_RETURN_STATUS(
+ GET_PARAMETER_SPECIFIC_FIELD(aidlParam, Spatializer, spatializer,
+ Spatializer::headTrackingSensorId, int32_t));
+ uint32_t modeInt32 = static_cast<int32_t>(mode);
+ if (status = param.writeToValue(&modeInt32); status != OK) {
+ ALOGI("%s %d: write mode %s to value failed %d", __func__, __LINE__,
+ toString(mode).c_str(), status);
+ return status;
+ }
+ if (status = param.writeToValue(&sensorId); status != OK) {
+ ALOGI("%s %d: write sensorId %d to value failed %d", __func__, __LINE__,
+ sensorId, status);
+ return status;
+ }
+ ALOGI("%s %d: %s", __func__, __LINE__, aidlParam.toString().c_str());
+ return OK;
+ }
+ default: {
+ ALOGE("%s %d invalid command %u", __func__, __LINE__, command);
+ return BAD_VALUE;
+ }
+ }
+ } else {
+ Parameter aidlParam;
+ DefaultExtension defaultExt;
+ // read parameters into DefaultExtension vector<uint8_t>
+ defaultExt.bytes.resize(param.getParameterSize());
+ if (OK != param.readFromParameter(defaultExt.bytes.data(), param.getParameterSize())) {
+ ALOGE("%s %d invalid param %s", __func__, __LINE__, param.toString().c_str());
+ param.setStatus(BAD_VALUE);
+ return BAD_VALUE;
+ }
+
+ VendorExtension idTag;
+ idTag.extension.setParcelable(defaultExt);
+ Parameter::Id id = UNION_MAKE(Parameter::Id, vendorEffectTag, idTag);
+ RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getParameter(id, &aidlParam)));
+ ALOGI("%s %d: %s", __func__, __LINE__,
+ aidlParam.get<Parameter::specific>().toString().c_str());
+ // copy the AIDL extension data back to effect_param_t
+ return VALUE_OR_RETURN_STATUS(
+ ::aidl::android::aidl2legacy_Parameter_EffectParameterWriter(aidlParam, param));
+ }
}
} // namespace effect
diff --git a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
index 7c60b14..444e5a7 100644
--- a/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
+++ b/media/libaudiohal/impl/effectsAidlConversion/AidlConversionSpatializer.h
@@ -32,6 +32,8 @@
~AidlConversionSpatializer() {}
private:
+ std::optional<bool> mIsSpatializerAidlParamSupported;
+ bool isSpatializerParameterSupported();
status_t setParameter(utils::EffectParamReader& param) override;
status_t getParameter(utils::EffectParamWriter& param) override;
};
diff --git a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
index bb5f851..7f6c1fb 100644
--- a/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h
@@ -78,8 +78,9 @@
virtual status_t getParameters(const String8& keys, String8 *values) = 0;
// Returns audio input buffer size according to parameters passed.
- virtual status_t getInputBufferSize(const struct audio_config *config,
- size_t *size) = 0;
+ // If there is no possibility for the HAL to open an input with the provided
+ // parameters, the method will return BAD_VALUE and modify the provided `config`.
+ virtual status_t getInputBufferSize(struct audio_config *config, size_t *size) = 0;
// Creates and opens the audio hardware output stream. The stream is closed
// by releasing all references to the returned object.
diff --git a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
index a780a17..37615af 100644
--- a/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/StreamHalInterface.h
@@ -116,7 +116,7 @@
class StreamOutHalInterfaceEventCallback : public virtual RefBase {
public:
- virtual void onCodecFormatChanged(const std::basic_string<uint8_t>& metadataBs) = 0;
+ virtual void onCodecFormatChanged(const std::vector<uint8_t>& metadataBs) = 0;
protected:
StreamOutHalInterfaceEventCallback() = default;
diff --git a/media/libaudiohal/tests/Android.bp b/media/libaudiohal/tests/Android.bp
index fa12cc4..b9af0bf 100644
--- a/media/libaudiohal/tests/Android.bp
+++ b/media/libaudiohal/tests/Android.bp
@@ -50,6 +50,7 @@
shared_libs: [
"libvibrator",
],
+ test_config_template: "AudioHalTestTemplate.xml",
}
cc_test {
diff --git a/media/libaudiohal/tests/AudioHalTestTemplate.xml b/media/libaudiohal/tests/AudioHalTestTemplate.xml
new file mode 100644
index 0000000..b1cb2f0
--- /dev/null
+++ b/media/libaudiohal/tests/AudioHalTestTemplate.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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="Runs {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" value="{MODULE}->/data/local/tmp/{MODULE}" />
+ </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}" />
+ <option name="native-test-timeout" value="10m" />
+ </test>
+</configuration>
diff --git a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
index 0cb654c..d783c64 100644
--- a/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
+++ b/media/libaudiohal/tests/EffectsFactoryHalInterface_test.cpp
@@ -21,10 +21,13 @@
#include <cstdint>
#include <cstring>
#include <memory>
+#include <string>
#include <utility>
#define LOG_TAG "EffectsFactoryHalInterfaceTest"
#include <aidl/android/media/audio/common/AudioUuid.h>
+#include <android/media/audio/common/HeadTracking.h>
+#include <android/media/audio/common/Spatialization.h>
#include <gtest/gtest.h>
#include <media/AidlConversionCppNdk.h>
#include <media/audiohal/EffectsFactoryHalInterface.h>
@@ -40,15 +43,18 @@
#include <system/audio_effects/effect_hapticgenerator.h>
#include <system/audio_effects/effect_loudnessenhancer.h>
#include <system/audio_effects/effect_ns.h>
+#include <system/audio_effects/effect_spatializer.h>
#include <utils/RefBase.h>
#include <vibrator/ExternalVibrationUtils.h>
namespace android {
-using ::aidl::android::media::audio::common::AudioUuid;
-using ::android::audio::utils::toString;
+using aidl::android::media::audio::common::AudioUuid;
+using android::audio::utils::toString;
using effect::utils::EffectParamReader;
using effect::utils::EffectParamWriter;
+using media::audio::common::HeadTracking;
+using media::audio::common::Spatialization;
// EffectsFactoryHalInterface
TEST(libAudioHalTest, createEffectsFactoryHalInterface) {
@@ -144,34 +150,68 @@
EXPECT_NE(0, version.getMajorVersion());
}
+enum ParamSetGetType { SET_N_GET, SET_ONLY, GET_ONLY };
class EffectParamCombination {
public:
template <typename P, typename V>
- void init(const P& p, const V& v, size_t len) {
- setBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4);
- getBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
- expectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
- parameterSet =
- std::make_shared<EffectParamReader>(createEffectParam(setBuffer.data(), p, v));
- parameterGet =
- std::make_shared<EffectParamReader>(createEffectParam(getBuffer.data(), p, v));
- parameterExpect =
- std::make_shared<EffectParamReader>(createEffectParam(expectBuffer.data(), p, v));
- valueSize = len;
+ void init(const P& p, const V& v, size_t len, ParamSetGetType type) {
+ if (type != GET_ONLY) {
+ mSetBuffer.resize(sizeof(effect_param_t) + sizeof(p) + sizeof(v) + 4);
+ mParameterSet =
+ std::make_shared<EffectParamReader>(createEffectParam(mSetBuffer.data(), p, v));
+ }
+
+ if (type != SET_ONLY) {
+ mGetBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
+ mExpectBuffer.resize(sizeof(effect_param_t) + sizeof(p) + len + 4);
+ mParameterGet =
+ std::make_shared<EffectParamReader>(createEffectParam(mGetBuffer.data(), p, v));
+ mParameterExpect = std::make_shared<EffectParamReader>(
+ createEffectParam(mExpectBuffer.data(), p, v));
+ mValueSize = len;
+ }
+ mType = type;
}
- std::shared_ptr<EffectParamReader> parameterSet; /* setParameter */
- std::shared_ptr<EffectParamReader> parameterGet; /* getParameter */
- std::shared_ptr<EffectParamReader> parameterExpect; /* expected from getParameter */
- size_t valueSize; /* ValueSize expect to write in reply data buffer */
+ std::shared_ptr<EffectParamReader> mParameterSet; /* setParameter */
+ std::shared_ptr<EffectParamReader> mParameterGet; /* getParameter */
+ std::shared_ptr<EffectParamReader> mParameterExpect; /* expected from getParameter */
+ size_t mValueSize = 0ul; /* ValueSize expect to write in reply data buffer */
+ ParamSetGetType mType = SET_N_GET;
+
+ std::string toString() {
+ uint32_t command = 0;
+ std::string str = "Command: ";
+ if (mType != GET_ONLY) {
+ str += (OK == mParameterSet->readFromParameter(&command) ? std::to_string(command)
+ : mParameterSet->toString());
+ } else {
+ str += (OK == mParameterGet->readFromParameter(&command) ? std::to_string(command)
+ : mParameterSet->toString());
+ }
+ str += "_";
+ str += toString(mType);
+ return str;
+ }
+
+ static std::string toString(ParamSetGetType type) {
+ switch (type) {
+ case SET_N_GET:
+ return "Type:SetAndGet";
+ case SET_ONLY:
+ return "Type:SetOnly";
+ case GET_ONLY:
+ return "Type:GetOnly";
+ }
+ }
private:
- std::vector<uint8_t> setBuffer;
- std::vector<uint8_t> getBuffer;
- std::vector<uint8_t> expectBuffer;
+ std::vector<uint8_t> mSetBuffer;
+ std::vector<uint8_t> mGetBuffer;
+ std::vector<uint8_t> mExpectBuffer;
template <typename P, typename V>
- EffectParamReader createEffectParam(void* buf, const P& p, const V& v) {
+ static EffectParamReader createEffectParam(void* buf, const P& p, const V& v) {
effect_param_t* paramRet = (effect_param_t*)buf;
paramRet->psize = sizeof(P);
paramRet->vsize = sizeof(V);
@@ -184,48 +224,106 @@
};
template <typename P, typename V>
-std::shared_ptr<EffectParamCombination> createEffectParamCombination(const P& p, const V& v,
- size_t len) {
+std::shared_ptr<EffectParamCombination> createEffectParamCombination(
+ const P& p, const V& v, size_t len, ParamSetGetType type = SET_N_GET) {
auto comb = std::make_shared<EffectParamCombination>();
- comb->init(p, v, len);
+ comb->init(p, v, len, type);
return comb;
}
-enum ParamName { TUPLE_UUID, TUPLE_PARAM_COMBINATION };
-using EffectParamTestTuple =
- std::tuple<const effect_uuid_t* /* type UUID */, std::shared_ptr<EffectParamCombination>>;
-
+enum ParamName { TUPLE_UUID, TUPLE_IS_INPUT, TUPLE_PARAM_COMBINATION };
+using EffectParamTestTuple = std::tuple<const effect_uuid_t* /* type UUID */, bool /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>>;
static const effect_uuid_t EXTEND_EFFECT_TYPE_UUID = {
0xfa81dbde, 0x588b, 0x11ed, 0x9b6a, {0x02, 0x42, 0xac, 0x12, 0x00, 0x02}};
constexpr std::array<uint8_t, 10> kVendorExtensionData({0xff, 0x5, 0x50, 0xab, 0xcd, 0x00, 0xbd,
0xdb, 0xee, 0xff});
-std::vector<EffectParamTestTuple> testPairs = {
- std::make_tuple(FX_IID_AEC,
- createEffectParamCombination(AEC_PARAM_ECHO_DELAY, 0xff /* echoDelayMs */,
- sizeof(int32_t) /* returnValueSize */)),
- std::make_tuple(FX_IID_AGC,
- createEffectParamCombination(AGC_PARAM_TARGET_LEVEL, 20 /* targetLevel */,
- sizeof(int16_t) /* returnValueSize */)),
- std::make_tuple(SL_IID_BASSBOOST,
- createEffectParamCombination(BASSBOOST_PARAM_STRENGTH, 20 /* strength */,
- sizeof(int16_t) /* returnValueSize */)),
- std::make_tuple(EFFECT_UIID_DOWNMIX,
- createEffectParamCombination(DOWNMIX_PARAM_TYPE, DOWNMIX_TYPE_FOLD,
- sizeof(int16_t) /* returnValueSize */)),
- std::make_tuple(SL_IID_DYNAMICSPROCESSING,
- createEffectParamCombination(
- std::array<uint32_t, 2>({DP_PARAM_INPUT_GAIN, 0 /* channel */}),
- 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)),
+static std::vector<EffectParamTestTuple> testPairs = {
std::make_tuple(
- FX_IID_LOUDNESS_ENHANCER,
- createEffectParamCombination(LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */,
- sizeof(int32_t) /* returnValueSize */)),
- std::make_tuple(FX_IID_NS,
- createEffectParamCombination(NS_PARAM_LEVEL, 1 /* level */,
- sizeof(int32_t) /* returnValueSize */)),
- std::make_tuple(&EXTEND_EFFECT_TYPE_UUID,
- createEffectParamCombination(8, kVendorExtensionData,
- sizeof(kVendorExtensionData)))};
+ FX_IID_AEC, true /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(AEC_PARAM_ECHO_DELAY, 0xff /* echoDelayMs */,
+ sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_AGC, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(AGC_PARAM_TARGET_LEVEL, 20 /* targetLevel */,
+ sizeof(int16_t) /* returnValueSize */)}),
+ std::make_tuple(
+ SL_IID_BASSBOOST, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(BASSBOOST_PARAM_STRENGTH, 20 /* strength */,
+ sizeof(int16_t) /* returnValueSize */)}),
+ std::make_tuple(
+ EFFECT_UIID_DOWNMIX, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(DOWNMIX_PARAM_TYPE, DOWNMIX_TYPE_FOLD,
+ sizeof(int16_t) /* returnValueSize */)}),
+ std::make_tuple(
+ SL_IID_DYNAMICSPROCESSING, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ std::array<uint32_t, 2>({DP_PARAM_INPUT_GAIN, 0 /* channel */}),
+ 30 /* gainDb */, sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_LOUDNESS_ENHANCER, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ LOUDNESS_ENHANCER_PARAM_TARGET_GAIN_MB, 5 /* gain */,
+ sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_NS, true /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ NS_PARAM_LEVEL, 1 /* level */, sizeof(int32_t) /* returnValueSize */)}),
+ std::make_tuple(
+ FX_IID_SPATIALIZER, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{
+ createEffectParamCombination(SPATIALIZER_PARAM_LEVEL,
+ SPATIALIZATION_LEVEL_MULTICHANNEL,
+ sizeof(uint8_t), SET_N_GET),
+ createEffectParamCombination(SPATIALIZER_PARAM_HEADTRACKING_MODE,
+ HeadTracking::Mode::RELATIVE_WORLD,
+ sizeof(uint8_t), SET_N_GET),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_HEAD_TO_STAGE,
+ std::array<float, 6>{.55f, 0.2f, 1.f, .999f, .43f, 19.f},
+ sizeof(std::array<float, 6>), SET_ONLY),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_HEADTRACKING_CONNECTION,
+ std::array<uint32_t, 2>{
+ static_cast<uint32_t>(HeadTracking::ConnectionMode::
+ DIRECT_TO_SENSOR_TUNNEL),
+ 0x5e /* sensorId */},
+ sizeof(std::array<uint32_t, 2>), SET_N_GET),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_SUPPORTED_LEVELS,
+ std::array<Spatialization::Level, 3>{
+ Spatialization::Level::NONE,
+ Spatialization::Level::MULTICHANNEL,
+ Spatialization::Level::BED_PLUS_OBJECTS},
+ sizeof(std::array<uint8_t, 3>), GET_ONLY),
+ createEffectParamCombination(SPATIALIZER_PARAM_HEADTRACKING_SUPPORTED, true,
+ sizeof(bool), GET_ONLY),
+ createEffectParamCombination(SPATIALIZER_PARAM_SUPPORTED_CHANNEL_MASKS,
+ AUDIO_CHANNEL_OUT_5POINT1, sizeof(uint8_t),
+ GET_ONLY),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_SUPPORTED_SPATIALIZATION_MODES,
+ std::array<Spatialization::Mode, 2>{
+ Spatialization::Mode::BINAURAL,
+ Spatialization::Mode::TRANSAURAL},
+ sizeof(std::array<uint8_t, 2>), GET_ONLY),
+ createEffectParamCombination(
+ SPATIALIZER_PARAM_SUPPORTED_HEADTRACKING_CONNECTION,
+ std::array<HeadTracking::ConnectionMode, 3>{
+ HeadTracking::ConnectionMode::FRAMEWORK_PROCESSED,
+ HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_SW,
+ HeadTracking::ConnectionMode::DIRECT_TO_SENSOR_TUNNEL},
+ sizeof(std::array<uint8_t, 3>), GET_ONLY),
+ }),
+ std::make_tuple(
+ &EXTEND_EFFECT_TYPE_UUID, false /* isInput */,
+ std::vector<std::shared_ptr<EffectParamCombination>>{createEffectParamCombination(
+ uint32_t{8}, kVendorExtensionData, sizeof(kVendorExtensionData))}),
+};
class libAudioHalEffectParamTest : public ::testing::TestWithParam<EffectParamTestTuple> {
public:
@@ -233,13 +331,8 @@
: mParamTuple(GetParam()),
mFactory(EffectsFactoryHalInterface::create()),
mTypeUuid(std::get<TUPLE_UUID>(mParamTuple)),
- mCombination(std::get<TUPLE_PARAM_COMBINATION>(mParamTuple)),
- mExpectedValue([&]() {
- std::vector<uint8_t> expectData(mCombination->valueSize);
- mCombination->parameterExpect->readFromValue(expectData.data(),
- mCombination->valueSize);
- return expectData;
- }()),
+ mCombinations(std::get<TUPLE_PARAM_COMBINATION>(mParamTuple)),
+ mIsInput(std::get<TUPLE_IS_INPUT>(mParamTuple)),
mDescs([&]() {
std::vector<effect_descriptor_t> descs;
if (mFactory && mTypeUuid && OK == mFactory->getDescriptors(mTypeUuid, &descs)) {
@@ -263,7 +356,8 @@
uint32_t reply = 0;
uint32_t replySize = sizeof(reply);
ASSERT_EQ(OK, interface->command(EFFECT_CMD_INIT, 0, nullptr, &replySize, &reply));
- ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(mEffectConfig),
+
+ ASSERT_EQ(OK, interface->command(EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
&mEffectConfig, &replySize, &reply));
}
@@ -284,60 +378,85 @@
}
void setAndGetParameter(const sp<EffectHalInterface>& interface) {
- uint32_t replySize = sizeof(uint32_t);
- uint8_t reply[replySize];
- auto parameterSet = mCombination->parameterSet;
- ASSERT_EQ(OK,
- interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)parameterSet->getTotalSize(),
- const_cast<effect_param_t*>(¶meterSet->getEffectParam()),
- &replySize, &reply))
- << parameterSet->toString();
- ASSERT_EQ(replySize, sizeof(uint32_t));
+ for (const auto combination : mCombinations) {
+ uint32_t replySize = kSetParamReplySize;
+ uint8_t reply[replySize];
+ const auto type = combination->mType;
+ if (type != GET_ONLY) {
+ const auto& set = combination->mParameterSet;
+ ASSERT_EQ(OK,
+ interface->command(EFFECT_CMD_SET_PARAM, (uint32_t)set->getTotalSize(),
+ const_cast<effect_param_t*>(&set->getEffectParam()),
+ &replySize, &reply))
+ << set->toString();
+ ASSERT_EQ(replySize, kSetParamReplySize);
+ }
- effect_param_t* getParam =
- const_cast<effect_param_t*>(&mCombination->parameterGet->getEffectParam());
- size_t maxReplySize = mCombination->valueSize + sizeof(effect_param_t) +
- sizeof(parameterSet->getPaddedParameterSize());
- replySize = maxReplySize;
- EXPECT_EQ(OK,
- interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)parameterSet->getTotalSize(),
- const_cast<effect_param_t*>(¶meterSet->getEffectParam()),
- &replySize, getParam));
- EffectParamReader parameterGet(*getParam);
- EXPECT_EQ(replySize, parameterGet.getTotalSize()) << parameterGet.toString();
- if (mCombination->valueSize) {
- std::vector<uint8_t> response(mCombination->valueSize);
- EXPECT_EQ(OK, parameterGet.readFromValue(response.data(), mCombination->valueSize))
- << " try get valueSize " << mCombination->valueSize << " from "
- << parameterGet.toString() << " set " << parameterSet->toString();
- EXPECT_EQ(response, mExpectedValue);
+ if (type != SET_ONLY) {
+ auto get = combination->mParameterGet;
+ auto expect = combination->mParameterExpect;
+ effect_param_t* getParam = const_cast<effect_param_t*>(&get->getEffectParam());
+ size_t maxReplySize = combination->mValueSize + sizeof(effect_param_t) +
+ sizeof(expect->getPaddedParameterSize());
+ replySize = maxReplySize;
+ EXPECT_EQ(OK,
+ interface->command(EFFECT_CMD_GET_PARAM, (uint32_t)expect->getTotalSize(),
+ const_cast<effect_param_t*>(&expect->getEffectParam()),
+ &replySize, getParam));
+
+ EffectParamReader getReader(*getParam);
+ EXPECT_EQ(replySize, getReader.getTotalSize()) << getReader.toString();
+ if (combination->mValueSize) {
+ std::vector<uint8_t> expectedData(combination->mValueSize);
+ EXPECT_EQ(OK, expect->readFromValue(expectedData.data(), expectedData.size()))
+ << combination->toString();
+ std::vector<uint8_t> response(combination->mValueSize);
+ EXPECT_EQ(OK, getReader.readFromValue(response.data(), combination->mValueSize))
+ << " try get valueSize " << combination->mValueSize << " from:\n"
+ << getReader.toString() << "\nexpect:\n"
+ << expect->toString();
+ EXPECT_EQ(expectedData, response) << combination->toString();
+ }
+ }
}
}
+ static constexpr size_t kSetParamReplySize = sizeof(uint32_t);
const EffectParamTestTuple mParamTuple;
const sp<EffectsFactoryHalInterface> mFactory;
const effect_uuid_t* mTypeUuid;
- std::shared_ptr<EffectParamCombination> mCombination;
- const std::vector<uint8_t> mExpectedValue;
+ std::vector<std::shared_ptr<EffectParamCombination>> mCombinations{};
+ const bool mIsInput;
const std::vector<effect_descriptor_t> mDescs;
- std::vector<sp<EffectHalInterface>> mHalInterfaces;
- effect_config_t mEffectConfig = {.inputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_READ,
- .format = AUDIO_FORMAT_PCM_FLOAT,
- .bufferProvider.getBuffer = nullptr,
- .bufferProvider.releaseBuffer = nullptr,
- .bufferProvider.cookie = nullptr,
- .mask = EFFECT_CONFIG_ALL,
- .samplingRate = 48000,
- .channels = AUDIO_CHANNEL_IN_STEREO},
-
- .outputCfg = {.accessMode = EFFECT_BUFFER_ACCESS_WRITE,
- .format = AUDIO_FORMAT_PCM_FLOAT,
- .bufferProvider.getBuffer = nullptr,
- .bufferProvider.releaseBuffer = nullptr,
- .bufferProvider.cookie = nullptr,
- .mask = EFFECT_CONFIG_ALL,
- .samplingRate = 48000,
- .channels = AUDIO_CHANNEL_OUT_STEREO}};
+ std::vector<sp<EffectHalInterface>> mHalInterfaces{};
+ effect_config_t mEffectConfig = {
+ .inputCfg =
+ {
+ .buffer = {.frameCount = 0x100},
+ .samplingRate = 48000,
+ .channels = mIsInput ? AUDIO_CHANNEL_IN_VOICE_CALL_MONO
+ : AUDIO_CHANNEL_IN_STEREO,
+ .bufferProvider = {.getBuffer = nullptr,
+ .releaseBuffer = nullptr,
+ .cookie = nullptr},
+ .format = AUDIO_FORMAT_PCM_FLOAT,
+ .accessMode = EFFECT_BUFFER_ACCESS_READ,
+ .mask = EFFECT_CONFIG_ALL,
+ },
+ .outputCfg =
+ {
+ .buffer = {.frameCount = 0x100},
+ .samplingRate = 48000,
+ .channels = mIsInput ? AUDIO_CHANNEL_IN_VOICE_CALL_MONO
+ : AUDIO_CHANNEL_OUT_STEREO,
+ .bufferProvider = {.getBuffer = nullptr,
+ .releaseBuffer = nullptr,
+ .cookie = nullptr},
+ .format = AUDIO_FORMAT_PCM_FLOAT,
+ .accessMode = EFFECT_BUFFER_ACCESS_WRITE,
+ .mask = EFFECT_CONFIG_ALL,
+ },
+ };
};
TEST_P(libAudioHalEffectParamTest, setAndGetParam) {
@@ -392,7 +511,8 @@
AudioUuid uuid = ::aidl::android::legacy2aidl_audio_uuid_t_AudioUuid(
*std::get<TUPLE_UUID>(info.param))
.value();
- std::string name = "UUID_" + toString(uuid);
+ std::string name = "UUID_" + toString(uuid) + "_";
+ name += std::get<TUPLE_IS_INPUT>(info.param) ? "_input" : "_output";
std::replace_if(
name.begin(), name.end(), [](const char c) { return !std::isalnum(c); }, '_');
return name;
@@ -404,6 +524,4 @@
return RUN_ALL_TESTS();
}
-// TODO: b/263986405 Add multi-thread testing
-
} // namespace android
diff --git a/media/libaudioprocessing/tests/Android.bp b/media/libaudioprocessing/tests/Android.bp
index ad402db..a33bf55 100644
--- a/media/libaudioprocessing/tests/Android.bp
+++ b/media/libaudioprocessing/tests/Android.bp
@@ -1,6 +1,7 @@
// Build the unit tests for libaudioprocessing
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libaudioprocessing/tests/fuzzer/Android.bp b/media/libaudioprocessing/tests/fuzzer/Android.bp
index 8fb6fff..b96ec6b 100644
--- a/media/libaudioprocessing/tests/fuzzer/Android.bp
+++ b/media/libaudioprocessing/tests/fuzzer/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -8,23 +9,23 @@
}
cc_fuzz {
- name: "libaudioprocessing_resampler_fuzzer",
- srcs: [
- "libaudioprocessing_resampler_fuzzer.cpp",
- ],
- defaults: ["libaudioprocessing_test_defaults"],
- static_libs: [
- "libsndfile",
- ],
+ name: "libaudioprocessing_resampler_fuzzer",
+ srcs: [
+ "libaudioprocessing_resampler_fuzzer.cpp",
+ ],
+ defaults: ["libaudioprocessing_test_defaults"],
+ static_libs: [
+ "libsndfile",
+ ],
}
cc_fuzz {
- name: "libaudioprocessing_record_buffer_converter_fuzzer",
- srcs: [
- "libaudioprocessing_record_buffer_converter_fuzzer.cpp",
- ],
- defaults: ["libaudioprocessing_test_defaults"],
- static_libs: [
- "libsndfile",
- ],
+ name: "libaudioprocessing_record_buffer_converter_fuzzer",
+ srcs: [
+ "libaudioprocessing_record_buffer_converter_fuzzer.cpp",
+ ],
+ defaults: ["libaudioprocessing_test_defaults"],
+ static_libs: [
+ "libsndfile",
+ ],
}
diff --git a/media/libeffects/downmix/Android.bp b/media/libeffects/downmix/Android.bp
index b56872c..0b25327 100644
--- a/media/libeffects/downmix/Android.bp
+++ b/media/libeffects/downmix/Android.bp
@@ -1,5 +1,6 @@
// Multichannel downmix effect library
package {
+ default_team: "trendy_team_media_framework_audio",
default_applicable_licenses: [
"frameworks_av_media_libeffects_downmix_license",
],
@@ -60,7 +61,7 @@
],
header_libs: [
"libaudioeffects",
- "libhardware_headers"
+ "libhardware_headers",
],
shared_libs: [
"libaudioutils",
diff --git a/media/libeffects/downmix/benchmark/Android.bp b/media/libeffects/downmix/benchmark/Android.bp
index 10f14e2..5b62a0c 100644
--- a/media/libeffects/downmix/benchmark/Android.bp
+++ b/media/libeffects/downmix/benchmark/Android.bp
@@ -1,5 +1,6 @@
// Build testbench for downmix module.
package {
+ default_team: "trendy_team_media_framework_audio",
// 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_libeffects_downmix_license"
diff --git a/media/libeffects/downmix/tests/Android.bp b/media/libeffects/downmix/tests/Android.bp
index 392a6fa..77d8f83 100644
--- a/media/libeffects/downmix/tests/Android.bp
+++ b/media/libeffects/downmix/tests/Android.bp
@@ -1,5 +1,6 @@
// Build testbench for downmix module.
package {
+ default_team: "trendy_team_media_framework_audio",
// 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_libeffects_downmix_license"
@@ -14,7 +15,7 @@
//
// Use "atest downmix_tests" to run.
cc_test {
- name:"downmix_tests",
+ name: "downmix_tests",
gtest: true,
host_supported: true,
vendor: true,
@@ -45,7 +46,7 @@
// test application and outputs then compares files in a local directory
// on device (/data/local/tmp/downmixtest/).
cc_test {
- name:"downmixtest",
+ name: "downmixtest",
host_supported: false,
proprietary: true,
diff --git a/media/libeffects/factory/Android.bp b/media/libeffects/factory/Android.bp
index d94093e..ad5188f 100644
--- a/media/libeffects/factory/Android.bp
+++ b/media/libeffects/factory/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -20,10 +21,10 @@
name: "libeffects",
vendor: true,
srcs: [
- "EffectsFactory.c",
- "EffectsConfigLoader.c",
- "EffectsFactoryState.c",
- "EffectsXmlConfigLoader.cpp",
+ "EffectsFactory.c",
+ "EffectsConfigLoader.c",
+ "EffectsFactoryState.c",
+ "EffectsXmlConfigLoader.cpp",
],
shared_libs: [
@@ -34,7 +35,7 @@
],
cflags: ["-fvisibility=hidden"],
- local_include_dirs:["include/media"],
+ local_include_dirs: ["include/media"],
header_libs: [
"libaudioeffects",
@@ -61,5 +62,8 @@
"libeffectsconfig",
"libeffects",
],
- local_include_dirs:[".", "include"],
+ local_include_dirs: [
+ ".",
+ "include",
+ ],
}
diff --git a/media/libeffects/lvm/benchmarks/Android.bp b/media/libeffects/lvm/benchmarks/Android.bp
index c21c5f2..8036983 100644
--- a/media/libeffects/lvm/benchmarks/Android.bp
+++ b/media/libeffects/lvm/benchmarks/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libeffects/lvm/lib/Android.bp b/media/libeffects/lvm/lib/Android.bp
index 7998879..c1a77f0 100644
--- a/media/libeffects/lvm/lib/Android.bp
+++ b/media/libeffects/lvm/lib/Android.bp
@@ -1,5 +1,6 @@
// Music bundle
package {
+ default_team: "trendy_team_media_framework_audio",
default_applicable_licenses: [
"frameworks_av_media_libeffects_lvm_lib_license",
],
diff --git a/media/libeffects/lvm/tests/Android.bp b/media/libeffects/lvm/tests/Android.bp
index 0568fbd..c32e91e 100644
--- a/media/libeffects/lvm/tests/Android.bp
+++ b/media/libeffects/lvm/tests/Android.bp
@@ -1,6 +1,7 @@
// Build the unit tests for effects
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -12,7 +13,7 @@
cc_test {
name: "EffectReverbTest",
defaults: [
- "libeffects-test-defaults",
+ "libeffects-test-defaults",
],
srcs: [
"EffectReverbTest.cpp",
@@ -29,7 +30,7 @@
cc_test {
name: "EffectBundleTest",
defaults: [
- "libeffects-test-defaults",
+ "libeffects-test-defaults",
],
srcs: [
"EffectBundleTest.cpp",
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index a9d0cc2..aa18deb 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -284,15 +284,15 @@
// roundoff
int maxLevelRound = (int)(totalEnergyEstimation + 0.99);
- if (maxLevelRound + mVolume > 0) {
- gainCorrection = maxLevelRound + mVolume;
+ if (maxLevelRound + mVolumedB > 0) {
+ gainCorrection = maxLevelRound + mVolumedB;
}
- params.VC_EffectLevel = mVolume - gainCorrection;
+ params.VC_EffectLevel = mVolumedB - gainCorrection;
if (params.VC_EffectLevel < -96) {
params.VC_EffectLevel = -96;
}
- LOG(INFO) << "\tVol: " << mVolume << ", GainCorrection: " << gainCorrection
+ LOG(INFO) << "\tVol: " << mVolumedB << ", GainCorrection: " << gainCorrection
<< ", Actual vol: " << params.VC_EffectLevel;
/* Activate the initial settings */
@@ -576,25 +576,25 @@
RetCode BundleContext::setVolumeLevel(float level) {
if (mMuteEnabled) {
- mLevelSaved = level;
+ mLevelSaveddB = level;
} else {
- mVolume = level;
+ mVolumedB = level;
}
LOG(INFO) << __func__ << " success with level " << level;
return limitLevel();
}
float BundleContext::getVolumeLevel() const {
- return (mMuteEnabled ? mLevelSaved : mVolume);
+ return (mMuteEnabled ? mLevelSaveddB : mVolumedB);
}
RetCode BundleContext::setVolumeMute(bool mute) {
mMuteEnabled = mute;
if (mMuteEnabled) {
- mLevelSaved = mVolume;
- mVolume = -96;
+ mLevelSaveddB = mVolumedB;
+ mVolumedB = -96;
} else {
- mVolume = mLevelSaved;
+ mVolumedB = mLevelSaveddB;
}
return limitLevel();
}
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index af46818..d823030 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -124,8 +124,8 @@
bool mVirtualizerTempDisabled = false;
::aidl::android::media::audio::common::AudioDeviceDescription mForceDevice;
// Volume
- float mLevelSaved = 0; /* for when mute is set, level must be saved */
- float mVolume = 0;
+ float mLevelSaveddB = 0; /* for when mute is set, level must be saved */
+ float mVolumedB = 0;
bool mMuteEnabled = false; /* Must store as mute = -96dB level */
RetCode initControlParameter(LVM_ControlParams_t& params) const;
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h b/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h
index 143329d..daabdb7 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleTypes.h
@@ -129,8 +129,7 @@
.implementor = "NXP Software Ltd."},
.capability = kVirtualizerCap};
-static const std::vector<Range::VolumeRange> kVolumeRanges = {
- MAKE_RANGE(Volume, levelDb, -9600, 0)};
+static const std::vector<Range::VolumeRange> kVolumeRanges = {MAKE_RANGE(Volume, levelDb, -96, 0)};
static const Capability kVolumeCap = {.range = kVolumeRanges};
static const std::string kVolumeEffectName = "Volume";
static const Descriptor kVolumeDesc = {
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
index 62837b9..781aad6 100644
--- a/media/libeffects/lvm/wrapper/Android.bp
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -1,5 +1,6 @@
// music bundle wrapper
package {
+ default_team: "trendy_team_media_framework_audio",
default_applicable_licenses: [
"frameworks_av_media_libeffects_lvm_wrapper_license",
],
diff --git a/media/libeffects/preprocessing/Android.bp b/media/libeffects/preprocessing/Android.bp
index 994b061..d658536 100644
--- a/media/libeffects/preprocessing/Android.bp
+++ b/media/libeffects/preprocessing/Android.bp
@@ -1,5 +1,6 @@
// audio preprocessing wrapper
package {
+ default_team: "trendy_team_media_framework_audio",
default_applicable_licenses: [
"frameworks_av_media_libeffects_preprocessing_license",
],
diff --git a/media/libeffects/preprocessing/benchmarks/Android.bp b/media/libeffects/preprocessing/benchmarks/Android.bp
index fbbcab4..ca99bf8 100644
--- a/media/libeffects/preprocessing/benchmarks/Android.bp
+++ b/media/libeffects/preprocessing/benchmarks/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// 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_libeffects_preprocessing_license"
diff --git a/media/libeffects/preprocessing/tests/Android.bp b/media/libeffects/preprocessing/tests/Android.bp
index d80b135..ad8d84d 100644
--- a/media/libeffects/preprocessing/tests/Android.bp
+++ b/media/libeffects/preprocessing/tests/Android.bp
@@ -1,5 +1,6 @@
// audio preprocessing unit test
package {
+ default_team: "trendy_team_media_framework_audio",
// 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_libeffects_preprocessing_license"
diff --git a/media/libeffects/spatializer/benchmarks/Android.bp b/media/libeffects/spatializer/benchmarks/Android.bp
index ab7e468..2d07a9b 100644
--- a/media/libeffects/spatializer/benchmarks/Android.bp
+++ b/media/libeffects/spatializer/benchmarks/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libeffects/spatializer/tests/Android.bp b/media/libeffects/spatializer/tests/Android.bp
index 704e873..ddfcff3 100644
--- a/media/libeffects/spatializer/tests/Android.bp
+++ b/media/libeffects/spatializer/tests/Android.bp
@@ -1,6 +1,7 @@
// Build the unit tests for spatializer effect
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -12,7 +13,7 @@
cc_test {
name: "SpatializerTest",
defaults: [
- "libeffects-test-defaults",
+ "libeffects-test-defaults",
],
host_supported: false,
srcs: [
diff --git a/media/libeffects/visualizer/aidl/Visualizer.cpp b/media/libeffects/visualizer/aidl/Visualizer.cpp
index fa651a6..9b1bac6 100644
--- a/media/libeffects/visualizer/aidl/Visualizer.cpp
+++ b/media/libeffects/visualizer/aidl/Visualizer.cpp
@@ -59,7 +59,8 @@
const std::string VisualizerImpl::kEffectName = "Visualizer";
const std::vector<Range::VisualizerRange> VisualizerImpl::kRanges = {
MAKE_RANGE(Visualizer, latencyMs, 0, VisualizerContext::kMaxLatencyMs),
- MAKE_RANGE(Visualizer, captureSamples, 0, VisualizerContext::kMaxCaptureBufSize),
+ MAKE_RANGE(Visualizer, captureSamples, VisualizerContext::kMinCaptureBufSize,
+ VisualizerContext::kMaxCaptureBufSize),
/* get only parameters, set invalid range (min > max) to indicate not support set */
MAKE_RANGE(Visualizer, measurement, Visualizer::Measurement({.rms = 1, .peak = 1}),
Visualizer::Measurement({.rms = 0, .peak = 0})),
diff --git a/media/libeffects/visualizer/aidl/VisualizerContext.cpp b/media/libeffects/visualizer/aidl/VisualizerContext.cpp
index 5d2bb3a..c763b1a 100644
--- a/media/libeffects/visualizer/aidl/VisualizerContext.cpp
+++ b/media/libeffects/visualizer/aidl/VisualizerContext.cpp
@@ -93,7 +93,7 @@
mCaptureSamples = samples;
return RetCode::SUCCESS;
}
-int VisualizerContext::getCaptureSamples() {
+int32_t VisualizerContext::getCaptureSamples() {
std::lock_guard lg(mMutex);
return mCaptureSamples;
}
diff --git a/media/libeffects/visualizer/aidl/VisualizerContext.h b/media/libeffects/visualizer/aidl/VisualizerContext.h
index 958035f..b03e038 100644
--- a/media/libeffects/visualizer/aidl/VisualizerContext.h
+++ b/media/libeffects/visualizer/aidl/VisualizerContext.h
@@ -18,6 +18,7 @@
#include <android-base/thread_annotations.h>
#include <audio_effects/effect_dynamicsprocessing.h>
+#include <system/audio_effects/effect_visualizer.h>
#include "effect-impl/EffectContext.h"
@@ -25,8 +26,11 @@
class VisualizerContext final : public EffectContext {
public:
- static const uint32_t kMaxCaptureBufSize = 65536;
- static const uint32_t kMaxLatencyMs = 3000; // 3 seconds of latency for audio pipeline
+ // need align the min/max capture size to VISUALIZER_CAPTURE_SIZE_MIN and
+ // VISUALIZER_CAPTURE_SIZE_MAX because of limitation in audio_utils fixedfft.
+ static constexpr int32_t kMinCaptureBufSize = VISUALIZER_CAPTURE_SIZE_MIN;
+ static constexpr int32_t kMaxCaptureBufSize = VISUALIZER_CAPTURE_SIZE_MAX;
+ static constexpr uint32_t kMaxLatencyMs = 3000; // 3 seconds of latency for audio pipeline
VisualizerContext(int statusDepth, const Parameter::Common& common);
~VisualizerContext();
@@ -38,8 +42,8 @@
// keep all parameters and reset buffer.
void reset();
- RetCode setCaptureSamples(int captureSize);
- int getCaptureSamples();
+ RetCode setCaptureSamples(int32_t captureSize);
+ int32_t getCaptureSamples();
RetCode setMeasurementMode(Visualizer::MeasurementMode mode);
Visualizer::MeasurementMode getMeasurementMode();
RetCode setScalingMode(Visualizer::ScalingMode mode);
@@ -86,7 +90,7 @@
// capture buf with 8 bits mono PCM samples
std::array<uint8_t, kMaxCaptureBufSize> mCaptureBuf GUARDED_BY(mMutex);
uint32_t mDownstreamLatency GUARDED_BY(mMutex) = 0;
- uint32_t mCaptureSamples GUARDED_BY(mMutex) = kMaxCaptureBufSize;
+ int32_t mCaptureSamples GUARDED_BY(mMutex) = kMaxCaptureBufSize;
// to avoid recomputing it every time a buffer is processed
uint8_t mChannelCount GUARDED_BY(mMutex) = 0;
diff --git a/media/libheadtracking/Android.bp b/media/libheadtracking/Android.bp
index 9955862..70a242d 100644
--- a/media/libheadtracking/Android.bp
+++ b/media/libheadtracking/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
@@ -11,18 +12,18 @@
name: "libheadtracking",
host_supported: true,
srcs: [
- "HeadTrackingProcessor.cpp",
- "ModeSelector.cpp",
- "Pose.cpp",
- "PoseBias.cpp",
- "PoseDriftCompensator.cpp",
- "PosePredictor.cpp",
- "PoseRateLimiter.cpp",
- "QuaternionUtil.cpp",
- "ScreenHeadFusion.cpp",
- "StillnessDetector.cpp",
- "Twist.cpp",
- "VectorRecorder.cpp",
+ "HeadTrackingProcessor.cpp",
+ "ModeSelector.cpp",
+ "Pose.cpp",
+ "PoseBias.cpp",
+ "PoseDriftCompensator.cpp",
+ "PosePredictor.cpp",
+ "PoseRateLimiter.cpp",
+ "QuaternionUtil.cpp",
+ "ScreenHeadFusion.cpp",
+ "StillnessDetector.cpp",
+ "Twist.cpp",
+ "VectorRecorder.cpp",
],
shared_libs: [
"libaudioutils",
@@ -51,7 +52,7 @@
cc_library {
name: "libheadtracking-binding",
srcs: [
- "SensorPoseProvider.cpp",
+ "SensorPoseProvider.cpp",
],
shared_libs: [
"libbase",
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 89348a4..3ab32f0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -111,9 +111,12 @@
// To collect the encoder usage for the battery app
static void addBatteryData(uint32_t params) {
sp<IBinder> binder =
- defaultServiceManager()->getService(String16("media.player"));
+ defaultServiceManager()->waitForService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
- CHECK(service.get() != NULL);
+ if (service.get() == nullptr) {
+ ALOGE("%s: Failed to get media.player service", __func__);
+ return;
+ }
service->addBatteryData(params);
}
@@ -1453,29 +1456,44 @@
}
status_t StagefrightRecorder::setupAACRecording() {
- // FIXME:
- // Add support for OUTPUT_FORMAT_AAC_ADIF
- CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_AAC_ADTS);
+ // TODO(b/324512842): Add support for OUTPUT_FORMAT_AAC_ADIF
+ if (mOutputFormat != OUTPUT_FORMAT_AAC_ADTS) {
+ ALOGE("Invalid output format %d used for AAC recording", mOutputFormat);
+ return BAD_VALUE;
+ }
- CHECK(mAudioEncoder == AUDIO_ENCODER_AAC ||
- mAudioEncoder == AUDIO_ENCODER_HE_AAC ||
- mAudioEncoder == AUDIO_ENCODER_AAC_ELD);
- CHECK(mAudioSource != AUDIO_SOURCE_CNT);
+ if (mAudioEncoder != AUDIO_ENCODER_AAC
+ && mAudioEncoder != AUDIO_ENCODER_HE_AAC
+ && mAudioEncoder != AUDIO_ENCODER_AAC_ELD) {
+ ALOGE("Invalid encoder %d used for AAC recording", mAudioEncoder);
+ return BAD_VALUE;
+ }
+
+ if (mAudioSource == AUDIO_SOURCE_CNT) {
+ ALOGE("Audio source hasn't been set correctly");
+ return BAD_VALUE;
+ }
mWriter = new AACWriter(mOutputFd);
return setupRawAudioRecording();
}
status_t StagefrightRecorder::setupOggRecording() {
- CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_OGG);
+ if (mOutputFormat != OUTPUT_FORMAT_OGG) {
+ ALOGE("Invalid output format %d used for OGG recording", mOutputFormat);
+ return BAD_VALUE;
+ }
mWriter = new OggWriter(mOutputFd);
return setupRawAudioRecording();
}
status_t StagefrightRecorder::setupAMRRecording() {
- CHECK(mOutputFormat == OUTPUT_FORMAT_AMR_NB ||
- mOutputFormat == OUTPUT_FORMAT_AMR_WB);
+ if (mOutputFormat != OUTPUT_FORMAT_AMR_NB
+ && mOutputFormat != OUTPUT_FORMAT_AMR_WB) {
+ ALOGE("Invalid output format %d used for AMR recording", mOutputFormat);
+ return BAD_VALUE;
+ }
if (mOutputFormat == OUTPUT_FORMAT_AMR_NB) {
if (mAudioEncoder != AUDIO_ENCODER_DEFAULT &&
@@ -1528,7 +1546,10 @@
}
status_t StagefrightRecorder::setupRTPRecording() {
- CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_RTP_AVP);
+ if (mOutputFormat != OUTPUT_FORMAT_RTP_AVP) {
+ ALOGE("Invalid output format %d used for RTP recording", mOutputFormat);
+ return BAD_VALUE;
+ }
if ((mAudioSource != AUDIO_SOURCE_CNT
&& mVideoSource != VIDEO_SOURCE_LIST_END)
@@ -1571,7 +1592,10 @@
}
status_t StagefrightRecorder::setupMPEG2TSRecording() {
- CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS);
+ if (mOutputFormat != OUTPUT_FORMAT_MPEG2TS) {
+ ALOGE("Invalid output format %d used for MPEG2TS recording", mOutputFormat);
+ return BAD_VALUE;
+ }
sp<MediaWriter> writer = new MPEG2TSWriter(mOutputFd);
diff --git a/media/libshmem/Android.bp b/media/libshmem/Android.bp
index 6e48078..486a34f 100644
--- a/media/libshmem/Android.bp
+++ b/media/libshmem/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/libstagefright/CryptoAsync.cpp b/media/libstagefright/CryptoAsync.cpp
index 8b5c8ed..0fc78ec 100644
--- a/media/libstagefright/CryptoAsync.cpp
+++ b/media/libstagefright/CryptoAsync.cpp
@@ -30,6 +30,36 @@
namespace android {
+CryptoAsync::CryptoAsyncInfo::CryptoAsyncInfo(const std::unique_ptr<CodecCryptoInfo> &info) {
+ if (info == nullptr) {
+ return;
+ }
+ size_t key_len = (info->mKey != nullptr)? 16 : 0;
+ size_t iv_len = (info->mIv != nullptr)? 16 : 0;
+ mNumSubSamples = info->mNumSubSamples;
+ mMode = info->mMode;
+ mPattern = info->mPattern;
+ if (key_len > 0) {
+ mKeyBuffer = ABuffer::CreateAsCopy((void*)info->mKey, key_len);
+ mKey = (uint8_t*)(mKeyBuffer.get() != nullptr ? mKeyBuffer.get()->data() : nullptr);
+ }
+ if (iv_len > 0) {
+ mIvBuffer = ABuffer::CreateAsCopy((void*)info->mIv, iv_len);
+ mIv = (uint8_t*)(mIvBuffer.get() != nullptr ? mIvBuffer.get()->data() : nullptr);
+ }
+ mSubSamplesBuffer =
+ new ABuffer(sizeof(CryptoPlugin::SubSample) * mNumSubSamples);
+ if (mSubSamplesBuffer.get()) {
+ CryptoPlugin::SubSample * samples =
+ (CryptoPlugin::SubSample *)(mSubSamplesBuffer.get()->data());
+ for (int s = 0 ; s < mNumSubSamples ; s++) {
+ samples[s].mNumBytesOfClearData = info->mSubSamples[s].mNumBytesOfClearData;
+ samples[s].mNumBytesOfEncryptedData = info->mSubSamples[s].mNumBytesOfEncryptedData;
+ }
+ mSubSamples = (CryptoPlugin::SubSample *)mSubSamplesBuffer.get()->data();
+ }
+}
+
CryptoAsync::~CryptoAsync() {
}
@@ -79,23 +109,27 @@
sp<ABuffer> keyBuffer;
sp<ABuffer> ivBuffer;
sp<ABuffer> subSamplesBuffer;
- msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks);
- msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks);
- msg->findBuffer("key", &keyBuffer);
- msg->findBuffer("iv", &ivBuffer);
- msg->findBuffer("subSamples", &subSamplesBuffer);
- msg->findInt32("secure", &secure);
- msg->findSize("numSubSamples", &numSubSamples);
- msg->findObject("buffer", &obj);
- msg->findInt32("mode", (int32_t*)&mode);
AString errorDetailMsg;
- const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr;
- const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr;
- const CryptoPlugin::SubSample * subSamples =
- (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data());
+ msg->findObject("buffer", &obj);
+ msg->findInt32("secure", &secure);
sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());
- err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode,
- pattern, subSamples, numSubSamples, &errorDetailMsg);
+ if (buffer->meta()->findObject("cryptoInfos", &obj)) {
+ err = channel->queueSecureInputBuffers(buffer, secure, &errorDetailMsg);
+ } else {
+ msg->findInt32("encryptBlocks", (int32_t*)&pattern.mEncryptBlocks);
+ msg->findInt32("skipBlocks", (int32_t*)&pattern.mSkipBlocks);
+ msg->findBuffer("key", &keyBuffer);
+ msg->findBuffer("iv", &ivBuffer);
+ msg->findBuffer("subSamples", &subSamplesBuffer);
+ msg->findSize("numSubSamples", &numSubSamples);
+ msg->findInt32("mode", (int32_t*)&mode);
+ const uint8_t * key = keyBuffer.get() != nullptr ? keyBuffer.get()->data() : nullptr;
+ const uint8_t * iv = ivBuffer.get() != nullptr ? ivBuffer.get()->data() : nullptr;
+ const CryptoPlugin::SubSample * subSamples =
+ (CryptoPlugin::SubSample *)(subSamplesBuffer.get()->data());
+ err = channel->queueSecureInputBuffer(buffer, secure, key, iv, mode,
+ pattern, subSamples, numSubSamples, &errorDetailMsg);
+ }
if (err != OK) {
std::list<sp<AMessage>> errorList;
msg->removeEntryByName("buffer");
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index d50c06b..a18dbfe 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -1640,6 +1640,11 @@
ALOGV("buffer->range_length:%lld", (long long)buffer->range_length());
if (buffer->meta_data().findInt64(kKeySampleFileOffset, &offset)) {
ALOGV("offset:%lld, old_offset:%lld", (long long)offset, (long long)old_offset);
+ if (mMaxOffsetAppend > offset) {
+ // This has already been appended, skip updating mOffset value.
+ *bytesWritten = buffer->range_length();
+ return offset;
+ }
if (old_offset == offset) {
mOffset += buffer->range_length();
} else {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 4d6a67b..e79e55b 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -298,7 +298,6 @@
return -EINVAL;
}
msg->setInt32("flags", bufferFlags);
- msg->setObject("accessUnitInfo", bufferInfos);
}
return OK;
}
@@ -3297,6 +3296,58 @@
return err;
}
+status_t MediaCodec::queueSecureInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<BufferInfosWrapper> &auInfo,
+ const sp<CryptoInfosWrapper> &cryptoInfos,
+ AString *errorDetailMsg) {
+ if (errorDetailMsg != NULL) {
+ errorDetailMsg->clear();
+ }
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+ uint32_t bufferFlags = 0;
+ uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG;
+ uint32_t andFlags = flagsinAllAU;
+ if (auInfo == nullptr
+ || auInfo->value.empty()
+ || cryptoInfos == nullptr
+ || cryptoInfos->value.empty()) {
+ ALOGE("ERROR: Large Audio frame with no BufferInfo/CryptoInfo");
+ return BAD_VALUE;
+ }
+ int infoIdx = 0;
+ std::vector<AccessUnitInfo> &accessUnitInfo = auInfo->value;
+ int64_t minTimeUs = accessUnitInfo.front().mTimestamp;
+ bool foundEndOfStream = false;
+ for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) {
+ bufferFlags |= accessUnitInfo[infoIdx].mFlags;
+ andFlags &= accessUnitInfo[infoIdx].mFlags;
+ if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+ foundEndOfStream = true;
+ }
+ }
+ bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU));
+ if (infoIdx != accessUnitInfo.size()) {
+ ALOGE("queueInputBuffers has incorrect access-units");
+ return -EINVAL;
+ }
+ msg->setSize("index", index);
+ msg->setSize("offset", offset);
+ msg->setSize("ssize", size);
+ msg->setInt64("timeUs", minTimeUs);
+ msg->setInt32("flags", bufferFlags);
+ msg->setObject("accessUnitInfo", auInfo);
+ msg->setObject("cryptoInfos", cryptoInfos);
+ msg->setPointer("errorDetailMsg", errorDetailMsg);
+
+ sp<AMessage> response;
+ status_t err = PostAndAwaitResponse(msg, &response);
+
+ return err;
+}
+
status_t MediaCodec::queueBuffer(
size_t index,
const std::shared_ptr<C2Buffer> &buffer,
@@ -3318,6 +3369,7 @@
if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
return err;
}
+ msg->setObject("accessUnitInfo", bufferInfos);
if (tunings && tunings->countEntries() > 0) {
msg->setMessage("tunings", tunings);
}
@@ -3332,13 +3384,9 @@
size_t index,
const sp<hardware::HidlMemory> &buffer,
size_t offset,
- const CryptoPlugin::SubSample *subSamples,
- size_t numSubSamples,
- const uint8_t key[16],
- const uint8_t iv[16],
- CryptoPlugin::Mode mode,
- const CryptoPlugin::Pattern &pattern,
+ size_t size,
const sp<BufferInfosWrapper> &bufferInfos,
+ const sp<CryptoInfosWrapper> &cryptoInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
@@ -3347,6 +3395,9 @@
if (bufferInfos == nullptr || bufferInfos->value.empty()) {
return BAD_VALUE;
}
+ if (cryptoInfos == nullptr || cryptoInfos->value.empty()) {
+ return BAD_VALUE;
+ }
status_t err = OK;
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->setSize("index", index);
@@ -3354,13 +3405,9 @@
new WrapperObject<sp<hardware::HidlMemory>>{buffer}};
msg->setObject("memory", memory);
msg->setSize("offset", offset);
- msg->setPointer("subSamples", (void *)subSamples);
- msg->setSize("numSubSamples", numSubSamples);
- msg->setPointer("key", (void *)key);
- msg->setPointer("iv", (void *)iv);
- msg->setInt32("mode", mode);
- msg->setInt32("encryptBlocks", pattern.mEncryptBlocks);
- msg->setInt32("skipBlocks", pattern.mSkipBlocks);
+ msg->setSize("ssize", size);
+ msg->setObject("cryptoInfos", cryptoInfos);
+ msg->setObject("accessUnitInfo", bufferInfos);
if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
return err;
}
@@ -6070,22 +6117,26 @@
mErrorLog.log(LOG_TAG, "queuing secure buffer without mCrypto or mDescrambler!");
return -EINVAL;
}
- CHECK(msg->findPointer("subSamples", (void **)&subSamples));
- CHECK(msg->findSize("numSubSamples", &numSubSamples));
- CHECK(msg->findPointer("key", (void **)&key));
- CHECK(msg->findPointer("iv", (void **)&iv));
- CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
- CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
+ sp<RefBase> obj;
+ if (msg->findObject("cryptoInfos", &obj)) {
+ CHECK(msg->findSize("ssize", &size));
+ } else {
+ CHECK(msg->findPointer("subSamples", (void **)&subSamples));
+ CHECK(msg->findSize("numSubSamples", &numSubSamples));
+ CHECK(msg->findPointer("key", (void **)&key));
+ CHECK(msg->findPointer("iv", (void **)&iv));
+ CHECK(msg->findInt32("encryptBlocks", (int32_t *)&pattern.mEncryptBlocks));
+ CHECK(msg->findInt32("skipBlocks", (int32_t *)&pattern.mSkipBlocks));
- int32_t tmp;
- CHECK(msg->findInt32("mode", &tmp));
+ int32_t tmp;
+ CHECK(msg->findInt32("mode", &tmp));
- mode = (CryptoPlugin::Mode)tmp;
-
- size = 0;
- for (size_t i = 0; i < numSubSamples; ++i) {
- size += subSamples[i].mNumBytesOfClearData;
- size += subSamples[i].mNumBytesOfEncryptedData;
+ mode = (CryptoPlugin::Mode)tmp;
+ size = 0;
+ for (size_t i = 0; i < numSubSamples; ++i) {
+ size += subSamples[i].mNumBytesOfClearData;
+ size += subSamples[i].mNumBytesOfEncryptedData;
+ }
}
}
@@ -6112,7 +6163,7 @@
status_t err = OK;
sp<RefBase> obj;
if (msg->findObject("accessUnitInfo", &obj)) {
- buffer->meta()->setObject("accessUnitInfo", obj);
+ buffer->meta()->setObject("accessUnitInfo", obj);
}
buffer->meta()->setInt64("timeUs", timeUs);
if (flags & BUFFER_FLAG_EOS) {
@@ -6150,35 +6201,48 @@
return err;
};
auto buildCryptoInfoAMessage = [&](const sp<AMessage> & cryptoInfo, int32_t action) {
- size_t key_len = (key != nullptr)? 16 : 0;
- size_t iv_len = (iv != nullptr)? 16 : 0;
- sp<ABuffer> shared_key;
- sp<ABuffer> shared_iv;
- if (key_len > 0) {
- shared_key = ABuffer::CreateAsCopy((void*)key, key_len);
- }
- if (iv_len > 0) {
- shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len);
- }
- sp<ABuffer> subSamples_buffer =
- new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples);
- CryptoPlugin::SubSample * samples =
- (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data());
- for (int s = 0 ; s < numSubSamples ; s++) {
- samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData;
- samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData;
- }
// set decrypt Action
cryptoInfo->setInt32("action", action);
cryptoInfo->setObject("buffer", buffer);
cryptoInfo->setInt32("secure", mFlags & kFlagIsSecure);
- cryptoInfo->setBuffer("key", shared_key);
- cryptoInfo->setBuffer("iv", shared_iv);
- cryptoInfo->setInt32("mode", (int)mode);
- cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks);
- cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks);
- cryptoInfo->setBuffer("subSamples", subSamples_buffer);
- cryptoInfo->setSize("numSubSamples", numSubSamples);
+ sp<RefBase> obj;
+ if (msg->findObject("cryptoInfos", &obj)) {
+ sp<CryptoInfosWrapper> infos{(CryptoInfosWrapper*)obj.get()};
+ sp<CryptoInfosWrapper> asyncInfos{
+ new CryptoInfosWrapper(std::vector<std::unique_ptr<CodecCryptoInfo>>())};
+ for (std::unique_ptr<CodecCryptoInfo> &info : infos->value) {
+ if (info) {
+ asyncInfos->value.emplace_back(new CryptoAsync::CryptoAsyncInfo(info));
+ }
+ }
+ buffer->meta()->setObject("cryptoInfos", asyncInfos);
+ } else {
+ size_t key_len = (key != nullptr)? 16 : 0;
+ size_t iv_len = (iv != nullptr)? 16 : 0;
+ sp<ABuffer> shared_key;
+ sp<ABuffer> shared_iv;
+ if (key_len > 0) {
+ shared_key = ABuffer::CreateAsCopy((void*)key, key_len);
+ }
+ if (iv_len > 0) {
+ shared_iv = ABuffer::CreateAsCopy((void*)iv, iv_len);
+ }
+ sp<ABuffer> subSamples_buffer =
+ new ABuffer(sizeof(CryptoPlugin::SubSample) * numSubSamples);
+ CryptoPlugin::SubSample * samples =
+ (CryptoPlugin::SubSample *)(subSamples_buffer.get()->data());
+ for (int s = 0 ; s < numSubSamples ; s++) {
+ samples[s].mNumBytesOfClearData = subSamples[s].mNumBytesOfClearData;
+ samples[s].mNumBytesOfEncryptedData = subSamples[s].mNumBytesOfEncryptedData;
+ }
+ cryptoInfo->setBuffer("key", shared_key);
+ cryptoInfo->setBuffer("iv", shared_iv);
+ cryptoInfo->setInt32("mode", (int)mode);
+ cryptoInfo->setInt32("encryptBlocks", pattern.mEncryptBlocks);
+ cryptoInfo->setInt32("skipBlocks", pattern.mSkipBlocks);
+ cryptoInfo->setBuffer("subSamples", subSamples_buffer);
+ cryptoInfo->setSize("numSubSamples", numSubSamples);
+ }
};
if (c2Buffer || memory) {
sp<AMessage> tunings = NULL;
@@ -6188,15 +6252,37 @@
status_t err = OK;
if (c2Buffer) {
err = mBufferChannel->attachBuffer(c2Buffer, buffer);
+ // to prevent unnecessary copy for single info case.
+ if (msg->findObject("accessUnitInfo", &obj)) {
+ sp<BufferInfosWrapper> infos{(BufferInfosWrapper*)(obj.get())};
+ if (infos->value.size() == 1) {
+ msg->removeEntryByName("accessUnitInfo");
+ }
+ }
} else if (memory) {
AString errorDetailMsg;
- err = mBufferChannel->attachEncryptedBuffer(
- memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
- offset, subSamples, numSubSamples, buffer, &errorDetailMsg);
+ if (msg->findObject("cryptoInfos", &obj)) {
+ buffer->meta()->setSize("ssize", size);
+ buffer->meta()->setObject("cryptoInfos", obj);
+ if (msg->findObject("accessUnitInfo", &obj)) {
+ // the reference will be same here and
+ // setBufferParams
+ buffer->meta()->setObject("accessUnitInfo", obj);
+ }
+ err = mBufferChannel->attachEncryptedBuffers(
+ memory,
+ offset,
+ buffer,
+ (mFlags & kFlagIsSecure),
+ &errorDetailMsg);
+ } else {
+ err = mBufferChannel->attachEncryptedBuffer(
+ memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
+ offset, subSamples, numSubSamples, buffer, &errorDetailMsg);
+ }
if (err != OK && hasCryptoOrDescrambler()
&& (mFlags & kFlagUseCryptoAsync)) {
// create error detail
- AString errorDetailMsg;
sp<AMessage> cryptoErrorInfo = new AMessage();
buildCryptoInfoAMessage(cryptoErrorInfo, CryptoAsync::kActionDecrypt);
cryptoErrorInfo->setInt32("err", err);
@@ -6268,10 +6354,17 @@
}
}
if (mCryptoAsync) {
+ // TODO b/316565675 - enable async path for audio
// prepare a message and enqueue
sp<AMessage> cryptoInfo = new AMessage();
buildCryptoInfoAMessage(cryptoInfo, CryptoAsync::kActionDecrypt);
mCryptoAsync->decrypt(cryptoInfo);
+ } else if (msg->findObject("cryptoInfos", &obj)) {
+ buffer->meta()->setObject("cryptoInfos", obj);
+ err = mBufferChannel->queueSecureInputBuffers(
+ buffer,
+ (mFlags & kFlagIsSecure),
+ errorDetailMsg);
} else {
err = mBufferChannel->queueSecureInputBuffer(
buffer,
@@ -6645,7 +6738,7 @@
if (accessUnitInfoObj) {
outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE;
msg->setObject("accessUnitInfo", accessUnitInfoObj);
- sp<BufferInfosWrapper> auInfo(
+ sp<BufferInfosWrapper> auInfo(
(decltype(auInfo.get()))accessUnitInfoObj.get());
auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM;
}
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 8741daa..bffb294 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -71,6 +71,26 @@
~AccessUnitInfo() {}
};
+struct CodecCryptoInfo {
+ size_t mNumSubSamples{0};
+ CryptoPlugin::SubSample *mSubSamples{nullptr};
+ uint8_t *mIv{nullptr};
+ uint8_t *mKey{nullptr};
+ enum CryptoPlugin::Mode mMode;
+ CryptoPlugin::Pattern mPattern;
+
+ virtual ~CodecCryptoInfo() {}
+protected:
+ CodecCryptoInfo():
+ mNumSubSamples(0),
+ mSubSamples(nullptr),
+ mIv(nullptr),
+ mKey(nullptr),
+ mMode{CryptoPlugin::kMode_Unencrypted},
+ mPattern{0, 0} {
+ }
+};
+
struct CodecParameterDescriptor {
std::string name;
AMessage::Type type;
@@ -372,6 +392,30 @@
const CryptoPlugin::SubSample *subSamples,
size_t numSubSamples,
AString *errorDetailMsg) = 0;
+
+ /**
+ * Queue a secure input buffer with multiple access units into the buffer channel.
+ *
+ * @param buffer The buffer to queue. The access unit delimiters and crypto
+ * subsample information is included in the buffer metadata.
+ * @param secure Whether the buffer is secure.
+ * @param errorDetailMsg The error message to be set in case of error.
+ * @return OK if successful;
+ * -ENOENT of the buffer is not known
+ * -ENOSYS if mCrypto is not set so that decryption is not
+ * possible;
+ * other errors if decryption failed.
+ */
+ virtual status_t queueSecureInputBuffers(
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ AString *errorDetailMsg) {
+ (void)buffer;
+ (void)secure;
+ (void)errorDetailMsg;
+ return -ENOSYS;
+ }
+
/**
* Attach a Codec 2.0 buffer to MediaCodecBuffer.
*
@@ -418,6 +462,34 @@
(void)errorDetailMsg;
return -ENOSYS;
}
+
+ /**
+ * Attach an encrypted HidlMemory buffer containing multiple access units to an index
+ *
+ * @param memory The memory to attach.
+ * @param offset index???
+ * @param buffer The MediaCodecBuffer to attach the memory to. The access
+ * unit delimiters and crypto subsample information is included
+ * in the buffer metadata.
+ * @param secure Whether the buffer is secure.
+ * @param errorDetailMsg The error message to be set if an error occurs.
+ * @return OK if successful;
+ * -ENOENT if index is not recognized
+ * -ENOSYS if attaching buffer is not possible or not supported
+ */
+ virtual status_t attachEncryptedBuffers(
+ const sp<hardware::HidlMemory> &memory,
+ size_t offset,
+ const sp<MediaCodecBuffer> &buffer,
+ bool secure,
+ AString* errorDetailMsg) {
+ (void)memory;
+ (void)offset;
+ (void)buffer;
+ (void)secure;
+ (void)errorDetailMsg;
+ return -ENOSYS;
+ }
/**
* Request buffer rendering at specified time.
*
diff --git a/media/libstagefright/include/media/stagefright/CryptoAsync.h b/media/libstagefright/include/media/stagefright/CryptoAsync.h
index b675518..acb3dae 100644
--- a/media/libstagefright/include/media/stagefright/CryptoAsync.h
+++ b/media/libstagefright/include/media/stagefright/CryptoAsync.h
@@ -85,6 +85,18 @@
kActionDecrypt = (1 << 0),
kActionAttachEncryptedBuffer = (1 << 1)
};
+
+ // This struct is meant to copy the mapped contents from the original info.
+ struct CryptoAsyncInfo : public CodecCryptoInfo {
+ public:
+ explicit CryptoAsyncInfo(const std::unique_ptr<CodecCryptoInfo> &info);
+ virtual ~CryptoAsyncInfo() = default;
+ protected:
+ // all backup buffers for the base object.
+ sp<ABuffer> mKeyBuffer;
+ sp<ABuffer> mIvBuffer;
+ sp<ABuffer> mSubSamplesBuffer;
+ };
protected:
// Message types for the looper
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index b0b1427..9ecb12e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -59,6 +59,7 @@
class BufferChannelBase;
struct AccessUnitInfo;
struct CodecBase;
+struct CodecCryptoInfo;
struct CodecParameterDescriptor;
class IBatteryStats;
struct ICrypto;
@@ -81,6 +82,7 @@
using aidl::android::media::ClientConfigParcel;
typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+typedef WrapperObject<std::vector<std::unique_ptr<CodecCryptoInfo>>> CryptoInfosWrapper;
struct MediaCodec : public AHandler {
enum Domain {
@@ -210,6 +212,14 @@
uint32_t flags,
AString *errorDetailMsg = NULL);
+ status_t queueSecureInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<BufferInfosWrapper> &accessUnitInfo,
+ const sp<CryptoInfosWrapper> &cryptoInfos,
+ AString *errorDetailMsg = NULL);
+
status_t queueBuffer(
size_t index,
const std::shared_ptr<C2Buffer> &buffer,
@@ -221,13 +231,9 @@
size_t index,
const sp<hardware::HidlMemory> &memory,
size_t offset,
- const CryptoPlugin::SubSample *subSamples,
- size_t numSubSamples,
- const uint8_t key[16],
- const uint8_t iv[16],
- CryptoPlugin::Mode mode,
- const CryptoPlugin::Pattern &pattern,
+ size_t size,
const sp<BufferInfosWrapper> &bufferInfos,
+ const sp<CryptoInfosWrapper> &cryptoInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg = NULL);
diff --git a/media/utils/fuzzers/Android.bp b/media/utils/fuzzers/Android.bp
index bd9a462..0a047c1 100644
--- a/media/utils/fuzzers/Android.bp
+++ b/media/utils/fuzzers/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 3fdc6eb..a68569a 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/services/Android.mk b/services/Android.mk
deleted file mode 100644
index c86a226..0000000
--- a/services/Android.mk
+++ /dev/null
@@ -1 +0,0 @@
-$(eval $(call declare-1p-copy-files,frameworks/av/services/audiopolicy,))
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index fcf02d9..99fd533 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1957,7 +1957,7 @@
mHardwareStatus = AUDIO_HW_IDLE;
// Change parameters of the configuration each iteration until we find a
- // configuration that the device will support.
+ // configuration that the device will support, or HAL suggests what it supports.
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
for (auto testChannelMask : channelMasks) {
config.channel_mask = testChannelMask;
@@ -1967,11 +1967,16 @@
config.sample_rate = testSampleRate;
size_t bytes = 0;
+ audio_config_t loopConfig = config;
status_t result = dev->getInputBufferSize(&config, &bytes);
+ if (result == BAD_VALUE) {
+ // Retry with the config suggested by the HAL.
+ result = dev->getInputBufferSize(&config, &bytes);
+ }
if (result != OK || bytes == 0) {
+ config = loopConfig;
continue;
}
-
if (config.sample_rate != sampleRate || config.channel_mask != channelMask ||
config.format != format) {
uint32_t dstChannelCount = audio_channel_count_from_in_mask(channelMask);
@@ -2171,30 +2176,24 @@
sp<IAfThreadBase> thread;
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- if (mPlaybackThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) {
- ALOG_ASSERT(thread == 0);
- thread = mPlaybackThreads.valueAt(i);
+ thread = mPlaybackThreads.valueAt(i);
+ if (thread->getEffect(sessionId, effectId) != 0) {
+ return thread;
}
}
- if (thread != nullptr) {
- return thread;
- }
for (size_t i = 0; i < mRecordThreads.size(); i++) {
- if (mRecordThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) {
- ALOG_ASSERT(thread == 0);
- thread = mRecordThreads.valueAt(i);
+ thread = mRecordThreads.valueAt(i);
+ if (thread->getEffect(sessionId, effectId) != 0) {
+ return thread;
}
}
- if (thread != nullptr) {
- return thread;
- }
for (size_t i = 0; i < mMmapThreads.size(); i++) {
- if (mMmapThreads.valueAt(i)->getEffect(sessionId, effectId) != 0) {
- ALOG_ASSERT(thread == 0);
- thread = mMmapThreads.valueAt(i);
+ thread = mMmapThreads.valueAt(i);
+ if (thread->getEffect(sessionId, effectId) != 0) {
+ return thread;
}
}
- return thread;
+ return nullptr;
}
// ----------------------------------------------------------------------------
@@ -4387,11 +4386,12 @@
sp<IAfThreadBase> thread = getEffectThread_l(sessionId, effectId);
if (thread == nullptr) {
- return;
+ return;
}
audio_utils::lock_guard _sl(thread->mutex());
- sp<IAfEffectModule> effect = thread->getEffect_l(sessionId, effectId);
- thread->setEffectSuspended_l(&effect->desc().type, suspended, sessionId);
+ if (const auto& effect = thread->getEffect_l(sessionId, effectId)) {
+ thread->setEffectSuspended_l(&effect->desc().type, suspended, sessionId);
+ }
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 767c502..9018dcc 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3057,7 +3057,7 @@
}
void PlaybackThread::onCodecFormatChanged(
- const std::basic_string<uint8_t>& metadataBs)
+ const std::vector<uint8_t>& metadataBs)
{
const auto weakPointerThis = wp<PlaybackThread>::fromExisting(this);
std::thread([this, metadataBs, weakPointerThis]() {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a6888c0..86e1894 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -964,7 +964,7 @@
protected:
// StreamHalInterfaceCodecFormatCallback implementation
void onCodecFormatChanged(
- const std::basic_string<uint8_t>& metadataBs) final;
+ const std::vector<uint8_t>& metadataBs) final;
// ThreadBase virtuals
void preExit() final EXCLUDES_ThreadBase_Mutex;
diff --git a/services/audioflinger/sounddose/SoundDoseManager.cpp b/services/audioflinger/sounddose/SoundDoseManager.cpp
index 8d40b63..3b764d1 100644
--- a/services/audioflinger/sounddose/SoundDoseManager.cpp
+++ b/services/audioflinger/sounddose/SoundDoseManager.cpp
@@ -231,10 +231,12 @@
ALOGI("%s: could not find port id for device %s", __func__, adt.toString().c_str());
return AUDIO_PORT_HANDLE_NONE;
}
- const auto btDeviceIt = mBluetoothDevicesWithCsd.find(std::make_pair(address, type));
- if (btDeviceIt != mBluetoothDevicesWithCsd.end()) {
- if (!btDeviceIt->second) {
- ALOGI("%s: bt device %s does not support sound dose", __func__, adt.toString().c_str());
+
+ if (audio_is_ble_out_device(type) || audio_is_a2dp_device(type)) {
+ const auto btDeviceIt = mBluetoothDevicesWithCsd.find(std::make_pair(address, type));
+ if (btDeviceIt == mBluetoothDevicesWithCsd.end() || !btDeviceIt->second) {
+ ALOGI("%s: bt device %s does not support sound dose", __func__,
+ adt.toString().c_str());
return AUDIO_PORT_HANDLE_NONE;
}
}
diff --git a/services/audioflinger/timing/tests/Android.bp b/services/audioflinger/timing/tests/Android.bp
index d1e5563..040a914 100644
--- a/services/audioflinger/timing/tests/Android.bp
+++ b/services/audioflinger/timing/tests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
@@ -13,7 +14,7 @@
host_supported: true,
srcs: [
- "mediasyncevent_tests.cpp"
+ "mediasyncevent_tests.cpp",
],
header_libs: [
@@ -38,7 +39,7 @@
host_supported: true,
srcs: [
- "monotonicframecounter_tests.cpp"
+ "monotonicframecounter_tests.cpp",
],
static_libs: [
@@ -54,26 +55,26 @@
}
cc_test {
- name: "synchronizedrecordstate_tests",
+ name: "synchronizedrecordstate_tests",
- host_supported: true,
+ host_supported: true,
- srcs: [
- "synchronizedrecordstate_tests.cpp"
- ],
+ srcs: [
+ "synchronizedrecordstate_tests.cpp",
+ ],
- header_libs: [
- "libaudioclient_headers",
- ],
+ header_libs: [
+ "libaudioclient_headers",
+ ],
- static_libs: [
- "liblog",
- "libutils", // RefBase
- ],
+ static_libs: [
+ "liblog",
+ "libutils", // RefBase
+ ],
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
- }
\ No newline at end of file
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+}
diff --git a/services/audioparameterparser/Android.bp b/services/audioparameterparser/Android.bp
index 18205bd..b3da333 100644
--- a/services/audioparameterparser/Android.bp
+++ b/services/audioparameterparser/Android.bp
@@ -57,7 +57,6 @@
relative_install_path: "hw",
init_rc: ["android.hardware.audio.parameter_parser.example_service.rc"],
- vintf_fragments: ["android.hardware.audio.parameter_parser.example_service.xml"],
defaults: [
"android.hardware.audio.parameter_parser.example_defaults",
diff --git a/services/audioparameterparser/android.hardware.audio.parameter_parser.example_service.xml b/services/audioparameterparser/android.hardware.audio.parameter_parser.example_service.xml
deleted file mode 100644
index 91addaa..0000000
--- a/services/audioparameterparser/android.hardware.audio.parameter_parser.example_service.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<manifest version="1.0" type="framework">
- <hal format="aidl">
- <name>android.media.audio</name>
- <version>1</version>
- <fqname>IHalAdapterVendorExtension/default</fqname>
- </hal>
-</manifest>
diff --git a/services/audiopolicy/Android.bp b/services/audiopolicy/Android.bp
index b33bba3..66ba7e2 100644
--- a/services/audiopolicy/Android.bp
+++ b/services/audiopolicy/Android.bp
@@ -5,7 +5,7 @@
// all of the 'license_kinds' from "frameworks_av_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_av_license"],
+ default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_library_headers {
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 101b90c..bd56366 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -542,6 +542,10 @@
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
+ binder::Status onSensorPrivacyStateChanged(int, int, int) {
+ return binder::Status::ok();
+ }
+
private:
wp<AudioPolicyService> mService;
std::atomic_bool mSensorPrivacyEnabled = false;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 76f65c0..2d55f39 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -135,6 +135,8 @@
static const std::string sSystemCameraPermission("android.permission.SYSTEM_CAMERA");
static const std::string sCameraHeadlessSystemUserPermission(
"android.permission.CAMERA_HEADLESS_SYSTEM_USER");
+static const std::string sCameraPrivacyAllowlistPermission(
+ "android.permission.CAMERA_PRIVACY_ALLOWLIST");
static const std::string
sCameraSendSystemEventsPermission("android.permission.CAMERA_SEND_SYSTEM_EVENTS");
static const std::string sCameraOpenCloseListenerPermission(
@@ -820,6 +822,14 @@
std::string(), AppOpsManager::OP_NONE);
}
+bool CameraService::hasPermissionsForCameraPrivacyAllowlist(int callingPid, int callingUid) const{
+ AttributionSourceState attributionSource{};
+ attributionSource.pid = callingPid;
+ attributionSource.uid = callingUid;
+ return checkPermission(std::string(), sCameraPrivacyAllowlistPermission, attributionSource,
+ std::string(), AppOpsManager::OP_NONE);
+}
+
Status CameraService::getNumberOfCameras(int32_t type, int32_t* numCameras) {
ATRACE_CALL();
Mutex::Autolock l(mServiceLock);
@@ -906,13 +916,6 @@
"request for system only device %s: ", cameraId.c_str());
}
- // Check for camera permissions
- if (!hasCameraPermissions()) {
- return STATUS_ERROR(ERROR_PERMISSION_DENIED,
- "android.permission.CAMERA needed to call"
- "createDefaultRequest");
- }
-
CameraMetadata metadata;
status_t err = mCameraProviderManager->createDefaultRequest(cameraId, tempId, &metadata);
if (err == OK) {
@@ -961,13 +964,6 @@
cameraId.c_str());
}
- // Check for camera permissions
- if (!hasCameraPermissions()) {
- return STATUS_ERROR(ERROR_PERMISSION_DENIED,
- "android.permission.CAMERA needed to call"
- "isSessionConfigurationWithParametersSupported");
- }
-
*supported = false;
status_t ret = mCameraProviderManager->isSessionConfigurationSupported(cameraId.c_str(),
sessionConfiguration, /*mOverrideForPerfClass*/false, /*checkSessionParams*/true,
@@ -998,6 +994,61 @@
return res;
}
+Status CameraService::getSessionCharacteristics(const std::string& unresolvedCameraId,
+ int targetSdkVersion, bool overrideToPortrait,
+ const SessionConfiguration& sessionConfiguration,
+ /*out*/ CameraMetadata* outMetadata) {
+ ATRACE_CALL();
+
+ if (!mInitialized) {
+ ALOGE("%s: Camera HAL couldn't be initialized", __FUNCTION__);
+ logServiceError("Camera subsystem is not available", ERROR_DISCONNECTED);
+ return STATUS_ERROR(ERROR_DISCONNECTED, "Camera subsystem is not available");
+ }
+
+ const std::string cameraId =
+ resolveCameraId(unresolvedCameraId, CameraThreadState::getCallingUid());
+
+ if (outMetadata == nullptr) {
+ std::string msg =
+ fmt::sprintf("Camera %s: Invalid 'outMetadata' input!", unresolvedCameraId.c_str());
+ ALOGE("%s: %s", __FUNCTION__, msg.c_str());
+ return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
+ }
+
+ bool overrideForPerfClass = SessionConfigurationUtils::targetPerfClassPrimaryCamera(
+ mPerfClassPrimaryCameraIds, cameraId, targetSdkVersion);
+
+ status_t ret = mCameraProviderManager->getSessionCharacteristics(
+ cameraId, sessionConfiguration, overrideForPerfClass, overrideToPortrait, outMetadata);
+
+ // TODO(b/303645857): Remove fingerprintable metadata if the caller process does not have
+ // camera access permission.
+
+ Status res = Status::ok();
+ switch (ret) {
+ case OK:
+ // Expected, no handling needed.
+ break;
+ case INVALID_OPERATION: {
+ std::string msg = fmt::sprintf(
+ "Camera %s: Session characteristics query not supported!",
+ cameraId.c_str());
+ ALOGD("%s: %s", __FUNCTION__, msg.c_str());
+ res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.c_str());
+ }
+ break;
+ default: {
+ std::string msg = fmt::sprintf("Camera %s: Error: %s (%d)", cameraId.c_str(),
+ strerror(-ret), ret);
+ ALOGE("%s: %s", __FUNCTION__, msg.c_str());
+ res = STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
+ }
+ }
+
+ return res;
+}
+
Status CameraService::parseCameraIdRemapping(
const hardware::CameraIdRemapping& cameraIdRemapping,
/* out */ TCameraIdRemapping* cameraIdRemappingMap) {
@@ -2345,6 +2396,39 @@
return ret;
}
+bool CameraService::isCameraPrivacyEnabled(const String16& packageName, const std::string& cam_id,
+ int callingPid, int callingUid) {
+ if (!isAutomotiveDevice()) {
+ return mSensorPrivacyPolicy->isCameraPrivacyEnabled();
+ }
+
+ // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for
+ // safety-critical use cases cannot be disabled and are exempt from camera privacy policy.
+ if ((isAutomotivePrivilegedClient(callingUid) && isAutomotiveExteriorSystemCamera(cam_id))) {
+ ALOGI("Camera privacy cannot be enabled for automotive privileged client %d "
+ "using camera %s", callingUid, cam_id.c_str());
+ return false;
+ }
+
+ if (mSensorPrivacyPolicy->isCameraPrivacyEnabled(packageName)) {
+ return true;
+ } else if (mSensorPrivacyPolicy->getCameraPrivacyState() == SensorPrivacyManager::DISABLED) {
+ return false;
+ } else if ((mSensorPrivacyPolicy->getCameraPrivacyState()
+ == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS) ||
+ (mSensorPrivacyPolicy->getCameraPrivacyState()
+ == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS) ||
+ (mSensorPrivacyPolicy->getCameraPrivacyState() ==
+ SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_APPS)) {
+ if (hasPermissionsForCameraPrivacyAllowlist(callingPid, callingUid)) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
+
std::string CameraService::getPackageNameFromUid(int clientUid) {
std::string packageName("");
@@ -2617,38 +2701,39 @@
}
}
- // Automotive privileged client AID_AUTOMOTIVE_EVS using exterior system camera for use
- // cases such as rear view and surround view cannot be disabled and are exempt from camera
- // privacy policy.
- if ((!isAutomotivePrivilegedClient(packageUid) ||
- !isAutomotiveExteriorSystemCamera(cameraId))) {
+ bool isCameraPrivacyEnabled;
+ if (flags::camera_privacy_allowlist()) {
// Set camera muting behavior.
- bool isCameraPrivacyEnabled =
+ isCameraPrivacyEnabled = this->isCameraPrivacyEnabled(
+ toString16(client->getPackageName()), cameraId, packagePid, packageUid);
+ } else {
+ isCameraPrivacyEnabled =
mSensorPrivacyPolicy->isCameraPrivacyEnabled();
- if (client->supportsCameraMute()) {
- client->setCameraMute(
- mOverrideCameraMuteMode || isCameraPrivacyEnabled);
- } else if (isCameraPrivacyEnabled) {
- // no camera mute supported, but privacy is on! => disconnect
- ALOGI("Camera mute not supported for package: %s, camera id: %s",
- client->getPackageName().c_str(), cameraId.c_str());
- // Do not hold mServiceLock while disconnecting clients, but
- // retain the condition blocking other clients from connecting
- // in mServiceLockWrapper if held.
- mServiceLock.unlock();
- // Clear caller identity temporarily so client disconnect PID
- // checks work correctly
- int64_t token = CameraThreadState::clearCallingIdentity();
- // Note AppOp to trigger the "Unblock" dialog
- client->noteAppOp();
- client->disconnect();
- CameraThreadState::restoreCallingIdentity(token);
- // Reacquire mServiceLock
- mServiceLock.lock();
+ }
- return STATUS_ERROR_FMT(ERROR_DISABLED,
- "Camera \"%s\" disabled due to camera mute", cameraId.c_str());
- }
+ if (client->supportsCameraMute()) {
+ client->setCameraMute(
+ mOverrideCameraMuteMode || isCameraPrivacyEnabled);
+ } else if (isCameraPrivacyEnabled) {
+ // no camera mute supported, but privacy is on! => disconnect
+ ALOGI("Camera mute not supported for package: %s, camera id: %s",
+ client->getPackageName().c_str(), cameraId.c_str());
+ // Do not hold mServiceLock while disconnecting clients, but
+ // retain the condition blocking other clients from connecting
+ // in mServiceLockWrapper if held.
+ mServiceLock.unlock();
+ // Clear caller identity temporarily so client disconnect PID
+ // checks work correctly
+ int64_t token = CameraThreadState::clearCallingIdentity();
+ // Note AppOp to trigger the "Unblock" dialog
+ client->noteAppOp();
+ client->disconnect();
+ CameraThreadState::restoreCallingIdentity(token);
+ // Reacquire mServiceLock
+ mServiceLock.lock();
+
+ return STATUS_ERROR_FMT(ERROR_DISABLED,
+ "Camera \"%s\" disabled due to camera mute", cameraId.c_str());
}
if (shimUpdateOnly) {
@@ -4169,8 +4254,15 @@
// return MODE_IGNORED. Do not treat such case as error.
bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid,
mClientPackageName);
- bool isCameraPrivacyEnabled =
+
+ bool isCameraPrivacyEnabled;
+ if (flags::camera_privacy_allowlist()) {
+ isCameraPrivacyEnabled = sCameraService->isCameraPrivacyEnabled(
+ toString16(mClientPackageName), std::string(), mClientPid, mClientUid);
+ } else {
+ isCameraPrivacyEnabled =
sCameraService->mSensorPrivacyPolicy->isCameraPrivacyEnabled();
+ }
// We don't want to return EACCESS if the CameraPrivacy is enabled.
// We prefer to successfully open the camera and perform camera muting
// or blocking in connectHelper as handleAppOpMode can be called before the
@@ -4196,8 +4288,15 @@
if (mAppOpsManager != nullptr) {
// Notify app ops that the camera is not available
mOpsCallback = new OpsCallback(this);
- mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
+
+ if (flags::watch_foreground_changes()) {
+ mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
+ toString16(mClientPackageName),
+ AppOpsManager::WATCH_FOREGROUND_CHANGES, mOpsCallback);
+ } else {
+ mAppOpsManager->startWatchingMode(AppOpsManager::OP_CAMERA,
toString16(mClientPackageName), mOpsCallback);
+ }
// Just check for camera acccess here on open - delay startOp until
// camera frames start streaming in startCameraStreamingOps
@@ -4357,20 +4456,42 @@
block();
} else if (res == AppOpsManager::MODE_IGNORED) {
bool isUidActive = sCameraService->mUidPolicy->isUidActive(mClientUid, mClientPackageName);
- bool isCameraPrivacyEnabled =
+
+ // Uid may be active, but not visible to the user (e.g. PROCESS_STATE_FOREGROUND_SERVICE).
+ // If not visible, but still active, then we want to block instead of muting the camera.
+ int32_t procState = sCameraService->mUidPolicy->getProcState(mClientUid);
+ bool isUidVisible = (procState <= ActivityManager::PROCESS_STATE_BOUND_TOP);
+
+ bool isCameraPrivacyEnabled;
+ if (flags::camera_privacy_allowlist()) {
+ isCameraPrivacyEnabled = sCameraService->isCameraPrivacyEnabled(
+ toString16(mClientPackageName),std::string(),mClientPid,mClientUid);
+ } else {
+ isCameraPrivacyEnabled =
sCameraService->mSensorPrivacyPolicy->isCameraPrivacyEnabled();
- ALOGI("Camera %s: Access for \"%s\" has been restricted, isUidTrusted %d, isUidActive %d",
- mCameraIdStr.c_str(), mClientPackageName.c_str(),
- mUidIsTrusted, isUidActive);
- // If the calling Uid is trusted (a native service), or the client Uid is active (WAR for
- // b/175320666), the AppOpsManager could return MODE_IGNORED. Do not treat such cases as
- // error.
+ }
+
+ ALOGI("Camera %s: Access for \"%s\" has been restricted, isUidTrusted %d, isUidActive %d"
+ " isUidVisible %d, isCameraPrivacyEnabled %d", mCameraIdStr.c_str(),
+ mClientPackageName.c_str(), mUidIsTrusted, isUidActive, isUidVisible,
+ isCameraPrivacyEnabled);
+ // If the calling Uid is trusted (a native service), or the client Uid is active / visible
+ // (WAR for b/175320666)the AppOpsManager could return MODE_IGNORED. Do not treat such
+ // cases as error.
if (!mUidIsTrusted) {
- if (isUidActive && isCameraPrivacyEnabled && supportsCameraMute()) {
- setCameraMute(true);
- } else if (!isUidActive
- || (isCameraPrivacyEnabled && !supportsCameraMute())) {
- block();
+ if (flags::watch_foreground_changes()) {
+ if (isUidVisible && isCameraPrivacyEnabled && supportsCameraMute()) {
+ setCameraMute(true);
+ } else {
+ block();
+ }
+ } else {
+ if (isUidActive && isCameraPrivacyEnabled && supportsCameraMute()) {
+ setCameraMute(true);
+ } else if (!isUidActive
+ || (isCameraPrivacyEnabled && !supportsCameraMute())) {
+ block();
+ }
}
}
} else if (res == AppOpsManager::MODE_ALLOWED) {
@@ -4741,7 +4862,15 @@
}
hasCameraPrivacyFeature(); // Called so the result is cached
mSpm.addSensorPrivacyListener(this);
+ if (isAutomotiveDevice()) {
+ mSpm.addToggleSensorPrivacyListener(this);
+ }
mSensorPrivacyEnabled = mSpm.isSensorPrivacyEnabled();
+ if (flags::camera_privacy_allowlist()) {
+ mCameraPrivacyState = mSpm.getToggleSensorPrivacyState(
+ SensorPrivacyManager::TOGGLE_TYPE_SOFTWARE,
+ SensorPrivacyManager::TOGGLE_SENSOR_CAMERA);
+ }
status_t res = mSpm.linkToDeath(this);
if (res == OK) {
mRegistered = true;
@@ -4773,6 +4902,9 @@
void CameraService::SensorPrivacyPolicy::unregisterSelf() {
Mutex::Autolock _l(mSensorPrivacyLock);
mSpm.removeSensorPrivacyListener(this);
+ if (isAutomotiveDevice()) {
+ mSpm.removeToggleSensorPrivacyListener(this);
+ }
mSpm.unlinkToDeath(this);
mRegistered = false;
ALOGV("SensorPrivacyPolicy: Unregistered with SensorPrivacyManager");
@@ -4787,6 +4919,15 @@
return mSensorPrivacyEnabled;
}
+int CameraService::SensorPrivacyPolicy::getCameraPrivacyState() {
+ if (!mRegistered) {
+ registerWithSensorPrivacyManager();
+ }
+
+ Mutex::Autolock _l(mSensorPrivacyLock);
+ return mCameraPrivacyState;
+}
+
bool CameraService::SensorPrivacyPolicy::isCameraPrivacyEnabled() {
if (!hasCameraPrivacyFeature()) {
return false;
@@ -4794,18 +4935,53 @@
return mSpm.isToggleSensorPrivacyEnabled(SensorPrivacyManager::TOGGLE_SENSOR_CAMERA);
}
+bool CameraService::SensorPrivacyPolicy::isCameraPrivacyEnabled(const String16& packageName) {
+ if (!hasCameraPrivacyFeature()) {
+ return SensorPrivacyManager::DISABLED;
+ }
+ return mSpm.isCameraPrivacyEnabled(packageName);
+}
+
binder::Status CameraService::SensorPrivacyPolicy::onSensorPrivacyChanged(
- int toggleType __unused, int sensor __unused, bool enabled) {
+ int toggleType, int sensor, bool enabled) {
+ if ((toggleType == SensorPrivacyManager::TOGGLE_TYPE_UNKNOWN)
+ && (sensor == SensorPrivacyManager::TOGGLE_SENSOR_UNKNOWN)) {
+ {
+ Mutex::Autolock _l(mSensorPrivacyLock);
+ mSensorPrivacyEnabled = enabled;
+ }
+ // if sensor privacy is enabled then block all clients from accessing the camera
+ if (enabled) {
+ sp<CameraService> service = mService.promote();
+ if (service != nullptr) {
+ service->blockAllClients();
+ }
+ }
+ }
+ return binder::Status::ok();
+}
+
+binder::Status CameraService::SensorPrivacyPolicy::onSensorPrivacyStateChanged(
+ int, int sensor, int state) {
+ if (!flags::camera_privacy_allowlist()
+ || (sensor != SensorPrivacyManager::TOGGLE_SENSOR_CAMERA)) {
+ return binder::Status::ok();
+ }
{
Mutex::Autolock _l(mSensorPrivacyLock);
- mSensorPrivacyEnabled = enabled;
+ mCameraPrivacyState = state;
+ }
+ sp<CameraService> service = mService.promote();
+ if (!service) {
+ return binder::Status::ok();
}
// if sensor privacy is enabled then block all clients from accessing the camera
- if (enabled) {
- sp<CameraService> service = mService.promote();
- if (service != nullptr) {
- service->blockAllClients();
- }
+ if (state == SensorPrivacyManager::ENABLED) {
+ service->blockAllClients();
+ } else if ((state == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_APPS)
+ || (state == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS)
+ || (state == SensorPrivacyManager::AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS)) {
+ service->blockPrivacyEnabledClients();
}
return binder::Status::ok();
}
@@ -5483,8 +5659,7 @@
for (auto& listener : mListenerList) {
bool isVendorListener = listener->isVendorListener();
if (shouldSkipStatusUpdates(deviceKind, isVendorListener,
- listener->getListenerPid(), listener->getListenerUid()) ||
- isVendorListener) {
+ listener->getListenerPid(), listener->getListenerUid())) {
ALOGV("Skipping discovery callback for system-only camera device %s",
cameraId.c_str());
continue;
@@ -5677,6 +5852,23 @@
}
}
+void CameraService::blockPrivacyEnabledClients() {
+ const auto clients = mActiveClientManager.getAll();
+ for (auto& current : clients) {
+ if (current != nullptr) {
+ const auto basicClient = current->getValue();
+ if (basicClient.get() != nullptr) {
+ std::string pkgName = basicClient->getPackageName();
+ bool cameraPrivacyEnabled =
+ mSensorPrivacyPolicy->isCameraPrivacyEnabled(toString16(pkgName));
+ if (cameraPrivacyEnabled) {
+ basicClient->block();
+ }
+ }
+ }
+ }
+}
+
// NOTE: This is a remote API - make sure all args are validated
status_t CameraService::shellCommand(int in, int out, int err, const Vector<String16>& args) {
if (!checkCallingPermission(toString16(sManageCameraPermission), nullptr, nullptr)) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 1487013..8822cd3 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -248,6 +248,11 @@
/*out*/
bool* supported);
+ virtual binder::Status getSessionCharacteristics(
+ const std::string& cameraId, int targetSdkVersion, bool overrideToPortrait,
+ const SessionConfiguration& sessionConfiguration,
+ /*out*/ CameraMetadata* outMetadata);
+
// Extra permissions checks
virtual status_t onTransact(uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags);
@@ -681,6 +686,9 @@
int callingUid) const;
bool hasCameraPermissions() const;
+
+ bool hasPermissionsForCameraPrivacyAllowlist(int callingPid, int callingUid) const;
+
/**
* Typesafe version of device status, containing both the HAL-layer and the service interface-
* layer values.
@@ -868,16 +876,20 @@
public virtual IServiceManager::LocalRegistrationCallback {
public:
explicit SensorPrivacyPolicy(wp<CameraService> service)
- : mService(service), mSensorPrivacyEnabled(false), mRegistered(false) {}
+ : mService(service), mSensorPrivacyEnabled(false),
+ mCameraPrivacyState(SensorPrivacyManager::DISABLED), mRegistered(false) {}
void registerSelf();
void unregisterSelf();
bool isSensorPrivacyEnabled();
bool isCameraPrivacyEnabled();
+ int getCameraPrivacyState();
+ bool isCameraPrivacyEnabled(const String16& packageName);
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
+ binder::Status onSensorPrivacyStateChanged(int toggleType, int sensor, int state);
// Implementation of IServiceManager::LocalRegistrationCallback
virtual void onServiceRegistration(const String16& name,
@@ -890,6 +902,7 @@
wp<CameraService> mService;
Mutex mSensorPrivacyLock;
bool mSensorPrivacyEnabled;
+ int mCameraPrivacyState;
bool mRegistered;
bool hasCameraPrivacyFeature();
@@ -926,6 +939,9 @@
const std::string& clientName, /*inout*/int& clientUid, /*inout*/int& clientPid,
/*out*/int& originalClientPid) const;
+ bool isCameraPrivacyEnabled(const String16& packageName,const std::string& cameraId,
+ int clientPid, int ClientUid);
+
// Handle active client evictions, and update service state.
// Only call with with mServiceLock held.
status_t handleEvictionsLocked(const std::string& cameraId, int clientPid,
@@ -1385,6 +1401,9 @@
// Blocks all active clients.
void blockAllClients();
+ // Blocks clients whose privacy is enabled.
+ void blockPrivacyEnabledClients();
+
// Overrides the UID state as if it is idle
status_t handleSetUidState(const Vector<String16>& args, int err);
diff --git a/services/camera/libcameraservice/aidl/AidlUtils.cpp b/services/camera/libcameraservice/aidl/AidlUtils.cpp
index f2d1414..14e5fad 100644
--- a/services/camera/libcameraservice/aidl/AidlUtils.cpp
+++ b/services/camera/libcameraservice/aidl/AidlUtils.cpp
@@ -17,12 +17,13 @@
#define LOG_TAG "AidlUtils"
#include <aidl/AidlUtils.h>
+#include <aidl/ExtensionMetadataTags.h>
#include <aidl/VndkVersionMetadataTags.h>
#include <aidlcommonsupport/NativeHandle.h>
+#include <camera/StringUtils.h>
#include <device3/Camera3StreamInterface.h>
#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h>
#include <mediautils/AImageReaderUtils.h>
-#include <camera/StringUtils.h>
namespace android::hardware::cameraservice::utils::conversion::aidl {
@@ -333,4 +334,47 @@
return OK;
}
+bool areExtensionKeysSupported(const CameraMetadata& metadata) {
+ auto requestKeys = metadata.find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
+ if (requestKeys.count == 0) {
+ ALOGE("%s: No ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS entries!", __FUNCTION__);
+ return false;
+ }
+
+ auto resultKeys = metadata.find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
+ if (resultKeys.count == 0) {
+ ALOGE("%s: No ANDROID_REQUEST_AVAILABLE_RESULT_KEYS entries!", __FUNCTION__);
+ return false;
+ }
+
+ for (const auto& extensionKey : extension_metadata_keys) {
+ if (std::find(requestKeys.data.i32, requestKeys.data.i32 + requestKeys.count, extensionKey)
+ != requestKeys.data.i32 + requestKeys.count) {
+ return true;
+ }
+
+ if (std::find(resultKeys.data.i32, resultKeys.data.i32 + resultKeys.count, extensionKey)
+ != resultKeys.data.i32 + resultKeys.count) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+status_t filterExtensionKeys(CameraMetadata* metadata /*out*/) {
+ if (metadata == nullptr) {
+ return BAD_VALUE;
+ }
+
+ for (const auto& key : extension_metadata_keys) {
+ status_t res = metadata->erase(key);
+ if (res != OK) {
+ ALOGE("%s metadata key %d could not be erased", __FUNCTION__, key);
+ return res;
+ }
+ }
+ return OK;
+}
+
} // namespace android::hardware::cameraservice::utils::conversion::aidl
diff --git a/services/camera/libcameraservice/aidl/AidlUtils.h b/services/camera/libcameraservice/aidl/AidlUtils.h
index c89d7ff..562aa70 100644
--- a/services/camera/libcameraservice/aidl/AidlUtils.h
+++ b/services/camera/libcameraservice/aidl/AidlUtils.h
@@ -122,6 +122,9 @@
status_t filterVndkKeys(int vndkVersion, CameraMetadata &metadata, bool isStatic = true);
+bool areExtensionKeysSupported(const CameraMetadata& metadata);
+
+status_t filterExtensionKeys(CameraMetadata* metadata /*out*/);
} // namespace android::hardware::cameraservice::utils::conversion::aidl
#endif // FRAMEWORKS_AV_SERVICES_CAMERA_LIBCAMERASERVICE_AIDL_AIDLUTILS_H_
diff --git a/services/camera/libcameraservice/aidl/ExtensionMetadataTags.h b/services/camera/libcameraservice/aidl/ExtensionMetadataTags.h
new file mode 100644
index 0000000..86af36c
--- /dev/null
+++ b/services/camera/libcameraservice/aidl/ExtensionMetadataTags.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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 <vector>
+#pragma once
+/**
+ * ! Do not edit this file directly !
+ *
+ * Generated automatically from extensions_camera_metadata_tags.mako. To be included in libcameraservice
+ * only by aidl/AidlUtils.cpp.
+ */
+
+/**
+ * API level to dynamic keys mapping. To be used for filtering out keys depending on vndk version
+ * used by vendor clients.
+ */
+std::vector<camera_metadata_tag> extension_metadata_keys{
+ ANDROID_EXTENSION_STRENGTH,
+ ANDROID_EXTENSION_CURRENT_TYPE,
+ ANDROID_EFV_PADDING_ZOOM_FACTOR,
+ ANDROID_EFV_AUTO_ZOOM,
+ ANDROID_EFV_MAX_PADDING_ZOOM_FACTOR,
+ ANDROID_EFV_STABILIZATION_MODE,
+ ANDROID_EFV_TRANSLATE_VIEWPORT,
+ ANDROID_EFV_ROTATE_VIEWPORT,
+ ANDROID_EFV_PADDING_REGION,
+ ANDROID_EFV_AUTO_ZOOM_PADDING_REGION,
+ ANDROID_EFV_TARGET_COORDINATES,
+};
diff --git a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
index e403b97..7965474 100644
--- a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
@@ -78,6 +78,7 @@
ANDROID_CONTROL_AUTOFRAMING_AVAILABLE,
ANDROID_CONTROL_AVAILABLE_SETTINGS_OVERRIDES,
ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE,
+ ANDROID_EFV_PADDING_ZOOM_FACTOR_RANGE,
ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
@@ -112,6 +113,15 @@
ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE,
ANDROID_CONTROL_SETTINGS_OVERRIDE,
ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
+ ANDROID_EFV_AUTO_ZOOM,
+ ANDROID_EFV_AUTO_ZOOM_PADDING_REGION,
+ ANDROID_EFV_MAX_PADDING_ZOOM_FACTOR,
+ ANDROID_EFV_PADDING_REGION,
+ ANDROID_EFV_PADDING_ZOOM_FACTOR,
+ ANDROID_EFV_ROTATE_VIEWPORT,
+ ANDROID_EFV_STABILIZATION_MODE,
+ ANDROID_EFV_TARGET_COORDINATES,
+ ANDROID_EFV_TRANSLATE_VIEWPORT,
ANDROID_EXTENSION_CURRENT_TYPE,
ANDROID_EXTENSION_STRENGTH,
ANDROID_FLASH_STRENGTH_LEVEL,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 3488629..508d487 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -777,60 +777,6 @@
return res;
}
-binder::Status CameraDeviceClient::getSessionCharacteristics(
- const SessionConfiguration& sessionConfiguration,
- /*out*/
- hardware::camera2::impl::CameraMetadataNative* sessionCharacteristics) {
- ATRACE_CALL();
- binder::Status res;
- status_t ret = OK;
- if (!(res = checkPidStatus(__FUNCTION__)).isOk()) return res;
-
- Mutex::Autolock icl(mBinderSerializationLock);
-
- if (!mDevice.get()) {
- return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
- }
-
- auto operatingMode = sessionConfiguration.getOperatingMode();
- res = SessionConfigurationUtils::checkOperatingMode(operatingMode, mDevice->info(),
- mCameraIdStr);
- if (!res.isOk()) {
- return res;
- }
-
- camera3::metadataGetter getMetadata = [this](const std::string &id,
- bool /*overrideForPerfClass*/) {
- return mDevice->infoPhysical(id);};
- ret = mProviderManager->getSessionCharacteristics(mCameraIdStr.c_str(),
- sessionConfiguration, mOverrideForPerfClass, getMetadata,
- sessionCharacteristics);
-
- switch (ret) {
- case OK:
- // Expected, do nothing.
- break;
- case INVALID_OPERATION: {
- std::string msg = fmt::sprintf(
- "Camera %s: Session characteristics query not supported!",
- mCameraIdStr.c_str());
- ALOGD("%s: %s", __FUNCTION__, msg.c_str());
- res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION, msg.c_str());
- }
-
- break;
- default: {
- std::string msg = fmt::sprintf( "Camera %s: Error: %s (%d)", mCameraIdStr.c_str(),
- strerror(-ret), ret);
- ALOGE("%s: %s", __FUNCTION__, msg.c_str());
- res = STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT,
- msg.c_str());
- }
- }
-
- return res;
-}
-
binder::Status CameraDeviceClient::deleteStream(int streamId) {
ATRACE_CALL();
ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index c2f7f56..b2c9626 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -109,11 +109,6 @@
/*out*/
bool* streamStatus) override;
- virtual binder::Status getSessionCharacteristics(
- const SessionConfiguration& sessionConfiguration,
- /*out*/
- hardware::camera2::impl::CameraMetadataNative* sessionCharacteristics) override;
-
// Returns -EBUSY if device is not idle or in error state
virtual binder::Status deleteStream(int streamId) override;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 2e471be..15e2755 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -459,19 +459,31 @@
return OK;
}
-status_t CameraProviderManager::getSessionCharacteristics(const std::string& id,
- const SessionConfiguration &configuration, bool overrideForPerfClass,
- metadataGetter getMetadata,
- CameraMetadata* sessionCharacteristics /*out*/) const {
+status_t CameraProviderManager::getSessionCharacteristics(
+ const std::string& id, const SessionConfiguration& configuration, bool overrideForPerfClass,
+ bool overrideToPortrait, CameraMetadata* sessionCharacteristics /*out*/) const {
if (!flags::feature_combination_query()) {
return INVALID_OPERATION;
}
+
std::lock_guard<std::mutex> lock(mInterfaceMutex);
auto deviceInfo = findDeviceInfoLocked(id);
if (deviceInfo == nullptr) {
return NAME_NOT_FOUND;
}
+ metadataGetter getMetadata = [this, overrideToPortrait](const std::string& id,
+ bool overrideForPerfClass) {
+ CameraMetadata metadata;
+ status_t ret = this->getCameraCharacteristicsLocked(id, overrideForPerfClass, &metadata,
+ overrideToPortrait);
+ if (ret != OK) {
+ ALOGE("%s: Could not get CameraCharacteristics for device %s", __FUNCTION__,
+ id.c_str());
+ }
+ return metadata;
+ };
+
return deviceInfo->getSessionCharacteristics(configuration,
overrideForPerfClass, getMetadata, sessionCharacteristics);
}
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index fc0172c..5ff3fcd 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -330,7 +330,8 @@
*/
status_t getSessionCharacteristics(const std::string& id,
const SessionConfiguration &configuration,
- bool overrideForPerfClass, camera3::metadataGetter getMetadata,
+ bool overrideForPerfClass,
+ bool overrideToPortrait,
CameraMetadata* sessionCharacteristics /*out*/) const;
/**
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ab968e3..9792089 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -78,6 +78,7 @@
using namespace android::camera3;
using namespace android::camera3::SessionConfigurationUtils;
using namespace android::hardware::camera;
+using namespace android::hardware::cameraservice::utils::conversion::aidl;
namespace flags = com::android::internal::camera::flags;
namespace android {
@@ -254,6 +255,8 @@
return res;
}
+ mSupportsExtensionKeys = areExtensionKeysSupported(mDeviceInfo);
+
return OK;
}
@@ -3892,13 +3895,22 @@
for (it = captureRequest->mSettingsList.begin();
it != captureRequest->mSettingsList.end(); it++) {
- res = hardware::cameraservice::utils::conversion::aidl::filterVndkKeys(
- mVndkVersion, it->metadata, false /*isStatic*/);
+ res = filterVndkKeys(mVndkVersion, it->metadata, false /*isStatic*/);
if (res != OK) {
SET_ERR("RequestThread: Failed during VNDK filter of capture requests "
"%d: %s (%d)", halRequest->frame_number, strerror(-res), res);
return INVALID_OPERATION;
}
+
+ if (!parent->mSupportsExtensionKeys) {
+ res = filterExtensionKeys(&it->metadata);
+ if (res != OK) {
+ SET_ERR("RequestThread: Failed during extension filter of capture "
+ "requests %d: %s (%d)", halRequest->frame_number,
+ strerror(-res), res);
+ return INVALID_OPERATION;
+ }
+ }
}
}
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 498ef55..ac4b039 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -1523,6 +1523,9 @@
// AE_TARGET_FPS_RANGE
bool mIsFixedFps = false;
+ // Flag to indicate that we shouldn't forward extension related metadata
+ bool mSupportsExtensionKeys = false;
+
// Injection camera related methods.
class Camera3DeviceInjectionMethods : public virtual RefBase {
public:
diff --git a/services/camera/virtualcamera/Android.bp b/services/camera/virtualcamera/Android.bp
index 94007cd..90530f6 100644
--- a/services/camera/virtualcamera/Android.bp
+++ b/services/camera/virtualcamera/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_xr_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
@@ -11,6 +12,7 @@
"libbinder",
"libbinder_ndk",
"libcamera_metadata",
+ "libexif",
"liblog",
"libfmq",
"libgui",
@@ -46,7 +48,7 @@
name: "libvirtualcamera_utils",
srcs: [
"util/JpegUtil.cc",
- "util/MetadataBuilder.cc",
+ "util/MetadataUtil.cc",
"util/Util.cc",
"util/TestPatternHelper.cc",
"util/EglDisplayContext.cc",
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.cc b/services/camera/virtualcamera/VirtualCameraDevice.cc
index fccbbc9..a70f6cf 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.cc
+++ b/services/camera/virtualcamera/VirtualCameraDevice.cc
@@ -38,7 +38,7 @@
#include "android/binder_status.h"
#include "log/log.h"
#include "system/camera_metadata.h"
-#include "util/MetadataBuilder.h"
+#include "util/MetadataUtil.h"
#include "util/Util.h"
namespace android {
@@ -72,12 +72,54 @@
constexpr int32_t kMaxJpegSize = 3 * 1024 * 1024 /*3MiB*/;
+constexpr int32_t kMinFps = 15;
+
+constexpr std::chrono::nanoseconds kMaxFrameDuration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1e9ns / kMinFps);
+
+constexpr uint8_t kPipelineMaxDepth = 2;
+
constexpr MetadataBuilder::ControlRegion kDefaultEmptyControlRegion{};
+constexpr float kAspectRatioEpsilon = 0.05;
+
+const std::array<Resolution, 5> kStandardJpegThumbnailSizes{
+ Resolution(176, 144), Resolution(240, 144), Resolution(256, 144),
+ Resolution(240, 160), Resolution(240, 180)};
+
const std::array<PixelFormat, 3> kOutputFormats{
PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
PixelFormat::BLOB};
+bool isApproximatellySameAspectRatio(const Resolution r1, const Resolution r2) {
+ float aspectRatio1 =
+ static_cast<float>(r1.width) / static_cast<float>(r1.height);
+ float aspectRatio2 =
+ static_cast<float>(r2.width) / static_cast<float>(r2.height);
+
+ return abs(aspectRatio1 - aspectRatio2) < kAspectRatioEpsilon;
+}
+
+std::vector<Resolution> getSupportedJpegThumbnailSizes(
+ const std::vector<SupportedStreamConfiguration>& configs) {
+ auto isSupportedByAnyInputConfig =
+ [&configs](const Resolution thumbnailResolution) {
+ return std::any_of(
+ configs.begin(), configs.end(),
+ [thumbnailResolution](const SupportedStreamConfiguration& config) {
+ return isApproximatellySameAspectRatio(
+ thumbnailResolution, Resolution(config.width, config.height));
+ });
+ };
+
+ std::vector<Resolution> supportedThumbnailSizes({Resolution(0, 0)});
+ std::copy_if(kStandardJpegThumbnailSizes.begin(),
+ kStandardJpegThumbnailSizes.end(),
+ std::back_insert_iterator(supportedThumbnailSizes),
+ isSupportedByAnyInputConfig);
+ return supportedThumbnailSizes;
+}
+
bool isSupportedOutputFormat(const PixelFormat pixelFormat) {
return std::find(kOutputFormats.begin(), kOutputFormats.end(), pixelFormat) !=
kOutputFormats.end();
@@ -86,10 +128,20 @@
std::vector<MetadataBuilder::FpsRange> fpsRangesForInputConfig(
const std::vector<SupportedStreamConfiguration>& configs) {
std::set<MetadataBuilder::FpsRange> availableRanges;
+
for (const SupportedStreamConfiguration& config : configs) {
+ availableRanges.insert({.minFps = kMinFps, .maxFps = config.maxFps});
availableRanges.insert({.minFps = config.maxFps, .maxFps = config.maxFps});
}
+ if (std::any_of(configs.begin(), configs.end(),
+ [](const SupportedStreamConfiguration& config) {
+ return config.maxFps >= 30;
+ })) {
+ availableRanges.insert({.minFps = kMinFps, .maxFps = 30});
+ availableRanges.insert({.minFps = 30, .maxFps = 30});
+ }
+
return std::vector<MetadataBuilder::FpsRange>(availableRanges.begin(),
availableRanges.end());
}
@@ -159,11 +211,14 @@
.setSensorTimestampSource(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN)
.setSensorPhysicalSize(36.0, 24.0)
.setAvailableFaceDetectModes({ANDROID_STATISTICS_FACE_DETECT_MODE_OFF})
+ .setAvailableTestPatternModes({ANDROID_SENSOR_TEST_PATTERN_MODE_OFF})
.setAvailableMaxDigitalZoom(1.0)
.setControlAvailableModes({ANDROID_CONTROL_MODE_AUTO})
.setControlAfAvailableModes({ANDROID_CONTROL_AF_MODE_OFF})
.setControlAvailableSceneModes({ANDROID_CONTROL_SCENE_MODE_DISABLED})
.setControlAvailableEffects({ANDROID_CONTROL_EFFECT_MODE_OFF})
+ .setControlAvailableVideoStabilizationModes(
+ {ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF})
.setControlAeAvailableModes({ANDROID_CONTROL_AE_MODE_ON})
.setControlAeAvailableAntibandingModes(
{ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO})
@@ -179,26 +234,43 @@
.setControlAeLockAvailable(false)
.setControlAvailableAwbModes({ANDROID_CONTROL_AWB_MODE_AUTO})
.setControlZoomRatioRange(/*min=*/1.0, /*max=*/1.0)
- // TODO(b/301023410) Add JPEG Exif + thumbnail support.
- .setJpegAvailableThumbnailSizes({Resolution(0, 0)})
+ .setJpegAvailableThumbnailSizes(
+ getSupportedJpegThumbnailSizes(supportedInputConfig))
.setMaxJpegSize(kMaxJpegSize)
+ .setMaxFrameDuration(kMaxFrameDuration)
.setMaxNumberOutputStreams(
VirtualCameraDevice::kMaxNumberOfRawStreams,
VirtualCameraDevice::kMaxNumberOfProcessedStreams,
VirtualCameraDevice::kMaxNumberOfStallStreams)
+ .setPipelineMaxDepth(kPipelineMaxDepth)
.setSyncMaxLatency(ANDROID_SYNC_MAX_LATENCY_UNKNOWN)
- .setAvailableRequestKeys(
- {ANDROID_CONTROL_CAPTURE_INTENT, ANDROID_CONTROL_AE_MODE,
- ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
- ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
- ANDROID_CONTROL_AE_ANTIBANDING_MODE,
- ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, ANDROID_CONTROL_AF_TRIGGER,
- ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AWB_MODE,
- ANDROID_STATISTICS_FACE_DETECT_MODE, ANDROID_FLASH_MODE})
+ .setAvailableRequestKeys({ANDROID_CONTROL_CAPTURE_INTENT,
+ ANDROID_CONTROL_AE_MODE,
+ ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+ ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
+ ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+ ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
+ ANDROID_CONTROL_AF_TRIGGER,
+ ANDROID_CONTROL_AF_MODE,
+ ANDROID_CONTROL_AWB_MODE,
+ ANDROID_SCALER_CROP_REGION,
+ ANDROID_CONTROL_EFFECT_MODE,
+ ANDROID_CONTROL_MODE,
+ ANDROID_CONTROL_SCENE_MODE,
+ ANDROID_CONTROL_VIDEO_STABILIZATION_MODE,
+ ANDROID_CONTROL_ZOOM_RATIO,
+ ANDROID_STATISTICS_FACE_DETECT_MODE,
+ ANDROID_FLASH_MODE,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES,
+ ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY})
.setAvailableResultKeys(
{ANDROID_CONTROL_AE_MODE, ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
ANDROID_CONTROL_AF_MODE, ANDROID_CONTROL_AWB_MODE,
- ANDROID_FLASH_STATE, ANDROID_SENSOR_TIMESTAMP,
+ ANDROID_CONTROL_EFFECT_MODE, ANDROID_CONTROL_MODE,
+ ANDROID_FLASH_MODE, ANDROID_FLASH_STATE,
+ ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, ANDROID_JPEG_QUALITY,
+ ANDROID_JPEG_THUMBNAIL_QUALITY, ANDROID_SENSOR_TIMESTAMP,
ANDROID_LENS_FOCAL_LENGTH})
.setAvailableCapabilities(
{ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE});
@@ -426,6 +498,19 @@
return mSupportedInputConfigurations;
}
+Resolution VirtualCameraDevice::getMaxInputResolution() const {
+ std::optional<Resolution> maxResolution =
+ getMaxResolution(mSupportedInputConfigurations);
+ if (!maxResolution.has_value()) {
+ ALOGE(
+ "%s: Cannot determine sensor size for virtual camera - input "
+ "configurations empty?",
+ __func__);
+ return Resolution(0, 0);
+ }
+ return maxResolution.value();
+}
+
std::shared_ptr<VirtualCameraDevice> VirtualCameraDevice::sharedFromThis() {
// SharedRefBase which BnCameraDevice inherits from breaks
// std::enable_shared_from_this. This is recommended replacement for
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.h b/services/camera/virtualcamera/VirtualCameraDevice.h
index fbd9095..c274dc9 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.h
+++ b/services/camera/virtualcamera/VirtualCameraDevice.h
@@ -24,6 +24,7 @@
#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
#include "aidl/android/hardware/camera/device/BnCameraDevice.h"
+#include "util/Util.h"
namespace android {
namespace companion {
@@ -98,21 +99,27 @@
aidl::android::companion::virtualcamera::SupportedStreamConfiguration>&
getInputConfigs() const;
+ // Returns largest supported input resolution.
+ Resolution getMaxInputResolution() const;
+
// Maximal number of RAW streams - virtual camera doesn't support RAW streams.
- static const int32_t kMaxNumberOfRawStreams = 0;
+ static constexpr int32_t kMaxNumberOfRawStreams = 0;
// Maximal number of non-jpeg streams configured concurrently in single
// session. This should be at least 3 and can be increased at the potential
// cost of more CPU/GPU load if there are many concurrent streams.
- static const int32_t kMaxNumberOfProcessedStreams = 3;
+ static constexpr int32_t kMaxNumberOfProcessedStreams = 3;
// Maximal number of stalling (in case of virtual camera only jpeg for now)
// streams. Can be increaed at the cost of potential cost of more GPU/CPU
// load.
- static const int32_t kMaxNumberOfStallStreams = 1;
+ static constexpr int32_t kMaxNumberOfStallStreams = 1;
// Focal length for full frame sensor.
- constexpr static const float kFocalLength = 43.0;
+ static constexpr float kFocalLength = 43.0;
+
+ // Default JPEG compression quality.
+ static constexpr uint8_t kDefaultJpegQuality = 80;
private:
std::shared_ptr<VirtualCameraDevice> sharedFromThis();
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index cd36c6d..615e449 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -18,18 +18,22 @@
#include "VirtualCameraRenderThread.h"
#include <chrono>
-#include <cstddef>
#include <cstdint>
+#include <cstring>
#include <future>
#include <memory>
#include <mutex>
#include <thread>
+#include <vector>
+#include "Exif.h"
#include "GLES/gl.h"
#include "VirtualCameraDevice.h"
#include "VirtualCameraSessionContext.h"
#include "aidl/android/hardware/camera/common/Status.h"
#include "aidl/android/hardware/camera/device/BufferStatus.h"
+#include "aidl/android/hardware/camera/device/CameraBlob.h"
+#include "aidl/android/hardware/camera/device/CameraBlobId.h"
#include "aidl/android/hardware/camera/device/CameraMetadata.h"
#include "aidl/android/hardware/camera/device/CaptureResult.h"
#include "aidl/android/hardware/camera/device/ErrorCode.h"
@@ -43,7 +47,7 @@
#include "ui/GraphicBuffer.h"
#include "util/EglFramebuffer.h"
#include "util/JpegUtil.h"
-#include "util/MetadataBuilder.h"
+#include "util/MetadataUtil.h"
#include "util/TestPatternHelper.h"
#include "util/Util.h"
#include "utils/Errors.h"
@@ -54,6 +58,8 @@
using ::aidl::android::hardware::camera::common::Status;
using ::aidl::android::hardware::camera::device::BufferStatus;
+using ::aidl::android::hardware::camera::device::CameraBlob;
+using ::aidl::android::hardware::camera::device::CameraBlobId;
using ::aidl::android::hardware::camera::device::CameraMetadata;
using ::aidl::android::hardware::camera::device::CaptureResult;
using ::aidl::android::hardware::camera::device::ErrorCode;
@@ -66,23 +72,44 @@
using ::aidl::android::hardware::graphics::common::PixelFormat;
using ::android::base::ScopedLockAssertion;
+using ::android::hardware::camera::common::helper::ExifUtils;
+
namespace {
using namespace std::chrono_literals;
static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
+// See REQUEST_PIPELINE_DEPTH in CaptureResult.java.
+// This roughly corresponds to frame latency, we set to
+// documented minimum of 2.
+static constexpr uint8_t kPipelineDepth = 2;
+
+static constexpr size_t kJpegThumbnailBufferSize = 32 * 1024; // 32 KiB
+
CameraMetadata createCaptureResultMetadata(
- const std::chrono::nanoseconds timestamp) {
+ const std::chrono::nanoseconds timestamp,
+ const RequestSettings& requestSettings,
+ const Resolution reportedSensorSize) {
std::unique_ptr<CameraMetadata> metadata =
MetadataBuilder()
.setControlAeMode(ANDROID_CONTROL_AE_MODE_ON)
.setControlAePrecaptureTrigger(
ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE)
- .setControlAfMode(ANDROID_CONTROL_AF_MODE_AUTO)
+ .setControlAfMode(ANDROID_CONTROL_AF_MODE_OFF)
.setControlAwbMode(ANDROID_CONTROL_AWB_MODE_AUTO)
+ .setControlEffectMode(ANDROID_CONTROL_EFFECT_MODE_OFF)
+ .setControlMode(ANDROID_CONTROL_MODE_AUTO)
+ .setCropRegion(0, 0, reportedSensorSize.width,
+ reportedSensorSize.height)
+ .setFaceDetectMode(ANDROID_STATISTICS_FACE_DETECT_MODE_OFF)
.setFlashState(ANDROID_FLASH_STATE_UNAVAILABLE)
.setFocalLength(VirtualCameraDevice::kFocalLength)
+ .setJpegQuality(requestSettings.jpegQuality)
+ .setJpegThumbnailSize(requestSettings.thumbnailResolution.width,
+ requestSettings.thumbnailResolution.height)
+ .setJpegThumbnailQuality(requestSettings.thumbnailJpegQuality)
+ .setPipelineDepth(kPipelineDepth)
.setSensorTimestamp(timestamp)
.build();
if (metadata == nullptr) {
@@ -160,6 +187,34 @@
}
}
+std::vector<uint8_t> createExif(
+ Resolution imageSize, const std::vector<uint8_t>& compressedThumbnail = {}) {
+ std::unique_ptr<ExifUtils> exifUtils(ExifUtils::create());
+ exifUtils->initialize();
+ exifUtils->setImageWidth(imageSize.width);
+ exifUtils->setImageHeight(imageSize.height);
+ // TODO(b/324383963) Set Make/Model and orientation.
+
+ std::vector<uint8_t> app1Data;
+
+ size_t thumbnailDataSize = compressedThumbnail.size();
+ const void* thumbnailData =
+ thumbnailDataSize > 0
+ ? reinterpret_cast<const void*>(compressedThumbnail.data())
+ : nullptr;
+
+ if (!exifUtils->generateApp1(thumbnailData, thumbnailDataSize)) {
+ ALOGE("%s: Failed to generate APP1 segment for EXIF metadata", __func__);
+ return app1Data;
+ }
+
+ const uint8_t* data = exifUtils->getApp1Buffer();
+ const size_t size = exifUtils->getApp1Length();
+
+ app1Data.insert(app1Data.end(), data, data + size);
+ return app1Data;
+}
+
} // namespace
CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
@@ -180,12 +235,12 @@
}
VirtualCameraRenderThread::VirtualCameraRenderThread(
- VirtualCameraSessionContext& sessionContext, const int mWidth,
- const int mHeight,
+ VirtualCameraSessionContext& sessionContext,
+ const Resolution inputSurfaceSize, const Resolution reportedSensorSize,
std::shared_ptr<ICameraDeviceCallback> cameraDeviceCallback, bool testMode)
: mCameraDeviceCallback(cameraDeviceCallback),
- mInputSurfaceWidth(mWidth),
- mInputSurfaceHeight(mHeight),
+ mInputSurfaceSize(inputSurfaceSize),
+ mReportedSensorSize(reportedSensorSize),
mTestMode(testMode),
mSessionContext(sessionContext) {
}
@@ -198,8 +253,11 @@
}
ProcessCaptureRequestTask::ProcessCaptureRequestTask(
- int frameNumber, const std::vector<CaptureRequestBuffer>& requestBuffers)
- : mFrameNumber(frameNumber), mBuffers(requestBuffers) {
+ int frameNumber, const std::vector<CaptureRequestBuffer>& requestBuffers,
+ const RequestSettings& requestSettings)
+ : mFrameNumber(frameNumber),
+ mBuffers(requestBuffers),
+ mRequestSettings(requestSettings) {
}
int ProcessCaptureRequestTask::getFrameNumber() const {
@@ -211,6 +269,10 @@
return mBuffers;
}
+const RequestSettings& ProcessCaptureRequestTask::getRequestSettings() const {
+ return mRequestSettings;
+}
+
void VirtualCameraRenderThread::enqueueTask(
std::unique_ptr<ProcessCaptureRequestTask> task) {
std::lock_guard<std::mutex> lock(mLock);
@@ -273,8 +335,8 @@
std::make_unique<EglTextureProgram>(EglTextureProgram::TextureFormat::YUV);
mEglTextureRgbProgram = std::make_unique<EglTextureProgram>(
EglTextureProgram::TextureFormat::RGBA);
- mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(mInputSurfaceWidth,
- mInputSurfaceHeight);
+ mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(
+ mInputSurfaceSize.width, mInputSurfaceSize.height);
mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());
while (std::unique_ptr<ProcessCaptureRequestTask> task = dequeueTask()) {
@@ -297,7 +359,8 @@
captureResult.partialResult = 1;
captureResult.inputBuffer.streamId = -1;
captureResult.physicalCameraMetadata.resize(0);
- captureResult.result = createCaptureResultMetadata(timestamp);
+ captureResult.result = createCaptureResultMetadata(
+ timestamp, request.getRequestSettings(), mReportedSensorSize);
const std::vector<CaptureRequestBuffer>& buffers = request.getBuffers();
captureResult.outputBuffers.resize(buffers.size());
@@ -326,9 +389,9 @@
}
auto status = streamConfig->format == PixelFormat::BLOB
- ? renderIntoBlobStreamBuffer(reqBuffer.getStreamId(),
- reqBuffer.getBufferId(),
- reqBuffer.getFence())
+ ? renderIntoBlobStreamBuffer(
+ reqBuffer.getStreamId(), reqBuffer.getBufferId(),
+ request.getRequestSettings(), reqBuffer.getFence())
: renderIntoImageStreamBuffer(reqBuffer.getStreamId(),
reqBuffer.getBufferId(),
reqBuffer.getFence());
@@ -408,9 +471,70 @@
}
}
+std::vector<uint8_t> VirtualCameraRenderThread::createThumbnail(
+ const Resolution resolution, const int quality) {
+ if (resolution.width == 0 || resolution.height == 0) {
+ ALOGV("%s: Skipping thumbnail creation, zero size requested", __func__);
+ return {};
+ }
+
+ ALOGV("%s: Creating thumbnail with size %d x %d, quality %d", __func__,
+ resolution.width, resolution.height, quality);
+ std::shared_ptr<EglFrameBuffer> framebuffer = allocateTemporaryFramebuffer(
+ mEglDisplayContext->getEglDisplay(), resolution.width, resolution.height);
+ if (framebuffer == nullptr) {
+ ALOGE(
+ "Failed to allocate temporary framebuffer for JPEG thumbnail "
+ "compression");
+ return {};
+ }
+
+ // TODO(b/324383963) Add support for letterboxing if the thumbnail size
+ // doesn't correspond
+ // to input texture aspect ratio.
+ if (!renderIntoEglFramebuffer(*framebuffer).isOk()) {
+ ALOGE(
+ "Failed to render input texture into temporary framebuffer for JPEG "
+ "thumbnail");
+ return {};
+ }
+
+ std::shared_ptr<AHardwareBuffer> inHwBuffer = framebuffer->getHardwareBuffer();
+ GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(inHwBuffer.get());
+
+ if (gBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_YCbCr_420_888) {
+ // This should never happen since we're allocating the temporary buffer
+ // with YUV420 layout above.
+ ALOGE("%s: Cannot compress non-YUV buffer (pixelFormat %d)", __func__,
+ gBuffer->getPixelFormat());
+ return {};
+ }
+
+ YCbCrLockGuard yCbCrLock(inHwBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+ if (yCbCrLock.getStatus() != NO_ERROR) {
+ ALOGE("%s: Failed to lock graphic buffer while generating thumbnail: %d",
+ __func__, yCbCrLock.getStatus());
+ return {};
+ }
+
+ std::vector<uint8_t> compressedThumbnail;
+ compressedThumbnail.resize(kJpegThumbnailBufferSize);
+ ALOGE("%s: Compressing thumbnail %d x %d", __func__, gBuffer->getWidth(),
+ gBuffer->getHeight());
+ std::optional<size_t> compressedSize = compressJpeg(
+ gBuffer->getWidth(), gBuffer->getHeight(), quality, *yCbCrLock, {},
+ compressedThumbnail.size(), compressedThumbnail.data());
+ if (!compressedSize.has_value()) {
+ ALOGE("%s: Failed to compress jpeg thumbnail", __func__);
+ return {};
+ }
+ compressedThumbnail.resize(compressedSize.value());
+ return compressedThumbnail;
+}
+
ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoBlobStreamBuffer(
- const int streamId, const int bufferId, sp<Fence> fence) {
- ALOGV("%s", __func__);
+ const int streamId, const int bufferId,
+ const RequestSettings& requestSettings, sp<Fence> fence) {
std::shared_ptr<AHardwareBuffer> hwBuffer =
mSessionContext.fetchHardwareBuffer(streamId, bufferId);
if (hwBuffer == nullptr) {
@@ -425,6 +549,9 @@
return cameraStatus(Status::INTERNAL_ERROR);
}
+ ALOGV("%s: Rendering JPEG with size %d x %d, quality %d", __func__,
+ stream->width, stream->height, requestSettings.jpegQuality);
+
// Let's create YUV framebuffer and render the surface into this.
// This will take care about rescaling as well as potential format conversion.
std::shared_ptr<EglFrameBuffer> framebuffer = allocateTemporaryFramebuffer(
@@ -441,58 +568,63 @@
return status;
}
- AHardwareBuffer_Planes planes_info;
-
- int32_t rawFence = fence != nullptr ? fence->get() : -1;
- int result = AHardwareBuffer_lockPlanes(hwBuffer.get(),
- AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
- rawFence, nullptr, &planes_info);
- if (result != OK) {
- ALOGE("%s: Failed to lock planes for BLOB buffer: %d", __func__, result);
+ PlanesLockGuard planesLock(hwBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+ fence);
+ if (planesLock.getStatus() != OK) {
return cameraStatus(Status::INTERNAL_ERROR);
}
std::shared_ptr<AHardwareBuffer> inHwBuffer = framebuffer->getHardwareBuffer();
GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(inHwBuffer.get());
- bool compressionSuccess = true;
+ std::optional<size_t> compressedSize;
if (gBuffer != nullptr) {
- android_ycbcr ycbcr;
if (gBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_YCbCr_420_888) {
// This should never happen since we're allocating the temporary buffer
// with YUV420 layout above.
ALOGE("%s: Cannot compress non-YUV buffer (pixelFormat %d)", __func__,
gBuffer->getPixelFormat());
- AHardwareBuffer_unlock(hwBuffer.get(), nullptr);
return cameraStatus(Status::INTERNAL_ERROR);
}
- status_t status =
- gBuffer->lockYCbCr(AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, &ycbcr);
- ALOGV("Locked buffers");
- if (status != NO_ERROR) {
- AHardwareBuffer_unlock(hwBuffer.get(), nullptr);
- ALOGE("%s: Failed to lock graphic buffer: %d", __func__, status);
+ YCbCrLockGuard yCbCrLock(inHwBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN);
+ if (yCbCrLock.getStatus() != OK) {
return cameraStatus(Status::INTERNAL_ERROR);
}
- compressionSuccess =
- compressJpeg(gBuffer->getWidth(), gBuffer->getHeight(), ycbcr,
- stream->bufferSize, planes_info.planes[0].data);
-
- status_t res = gBuffer->unlock();
- if (res != NO_ERROR) {
- ALOGE("Failed to unlock graphic buffer: %d", res);
- }
+ std::vector<uint8_t> app1ExifData =
+ createExif(Resolution(stream->width, stream->height),
+ createThumbnail(requestSettings.thumbnailResolution,
+ requestSettings.thumbnailJpegQuality));
+ compressedSize = compressJpeg(
+ gBuffer->getWidth(), gBuffer->getHeight(), requestSettings.jpegQuality,
+ *yCbCrLock, app1ExifData, stream->bufferSize - sizeof(CameraBlob),
+ (*planesLock).planes[0].data);
} else {
- compressionSuccess =
- compressBlackJpeg(stream->width, stream->height, stream->bufferSize,
- planes_info.planes[0].data);
+ std::vector<uint8_t> app1ExifData =
+ createExif(Resolution(stream->width, stream->height));
+ compressedSize = compressBlackJpeg(
+ stream->width, stream->height, requestSettings.jpegQuality, app1ExifData,
+ stream->bufferSize - sizeof(CameraBlob), (*planesLock).planes[0].data);
}
- AHardwareBuffer_unlock(hwBuffer.get(), nullptr);
- ALOGV("Unlocked buffers");
- return compressionSuccess ? ndk::ScopedAStatus::ok()
- : cameraStatus(Status::INTERNAL_ERROR);
+
+ if (!compressedSize.has_value()) {
+ ALOGE("%s: Failed to compress JPEG image", __func__);
+ return cameraStatus(Status::INTERNAL_ERROR);
+ }
+
+ CameraBlob cameraBlob{
+ .blobId = CameraBlobId::JPEG,
+ .blobSizeBytes = static_cast<int32_t>(compressedSize.value())};
+
+ memcpy(reinterpret_cast<uint8_t*>((*planesLock).planes[0].data) +
+ (stream->bufferSize - sizeof(cameraBlob)),
+ &cameraBlob, sizeof(cameraBlob));
+
+ ALOGV("%s: Successfully compressed JPEG image, resulting size %zu B",
+ __func__, compressedSize.value());
+
+ return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoImageStreamBuffer(
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index b3aaed8..86dad0b 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -17,18 +17,23 @@
#ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERARENDERTHREAD_H
#define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERARENDERTHREAD_H
+#include <cstdint>
#include <deque>
#include <future>
#include <memory>
#include <thread>
+#include <vector>
+#include "VirtualCameraDevice.h"
#include "VirtualCameraSessionContext.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
#include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h"
#include "android/binder_auto_utils.h"
#include "util/EglDisplayContext.h"
#include "util/EglFramebuffer.h"
#include "util/EglProgram.h"
#include "util/EglSurfaceTexture.h"
+#include "util/Util.h"
namespace android {
namespace companion {
@@ -49,11 +54,18 @@
const sp<Fence> mFence;
};
+struct RequestSettings {
+ int jpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
+ Resolution thumbnailResolution = Resolution(0, 0);
+ int thumbnailJpegQuality = VirtualCameraDevice::kDefaultJpegQuality;
+};
+
// Represents single capture request to fill set of buffers.
class ProcessCaptureRequestTask {
public:
ProcessCaptureRequestTask(
- int frameNumber, const std::vector<CaptureRequestBuffer>& requestBuffers);
+ int frameNumber, const std::vector<CaptureRequestBuffer>& requestBuffers,
+ const RequestSettings& RequestSettings = {});
// Returns frame number corresponding to the request.
int getFrameNumber() const;
@@ -65,9 +77,12 @@
// so it cannot be access outside of its lifetime.
const std::vector<CaptureRequestBuffer>& getBuffers() const;
+ const RequestSettings& getRequestSettings() const;
+
private:
const int mFrameNumber;
const std::vector<CaptureRequestBuffer> mBuffers;
+ const RequestSettings mRequestSettings;
};
// Wraps dedicated rendering thread and rendering business with corresponding
@@ -77,14 +92,14 @@
// Create VirtualCameraRenderThread instance:
// * sessionContext - VirtualCameraSessionContext reference for shared access
// to mapped buffers.
- // * inputWidth - requested width of input surface ("virtual camera sensor")
- // * inputHeight - requested height of input surface ("virtual camera sensor")
+ // * inputSurfaceSize - requested size of input surface.
+ // * reportedSensorSize - reported static sensor size of virtual camera.
// * cameraDeviceCallback - callback for corresponding camera instance
// * testMode - when set to true, test pattern is rendered to input surface
// before each capture request is processed to simulate client input.
VirtualCameraRenderThread(
- VirtualCameraSessionContext& sessionContext, int inputWidth,
- int inputHeight,
+ VirtualCameraSessionContext& sessionContext, Resolution inputSurfaceSize,
+ Resolution reportedSensorSize,
std::shared_ptr<
::aidl::android::hardware::camera::device::ICameraDeviceCallback>
cameraDeviceCallback,
@@ -122,13 +137,19 @@
// TODO(b/301023410) - Refactor the actual rendering logic off this class for
// easier testability.
+ // Create thumbnail with specified size for current image.
+ // The compressed image size is limited by 32KiB.
+ // Returns vector with compressed thumbnail if successful,
+ // empty vector otherwise.
+ std::vector<uint8_t> createThumbnail(Resolution resolution, int quality);
+
// Render current image to the BLOB buffer.
// If fence is specified, this function will block until the fence is cleared
// before writing to the buffer.
// Always called on render thread.
- ndk::ScopedAStatus renderIntoBlobStreamBuffer(const int streamId,
- const int bufferId,
- sp<Fence> fence = nullptr);
+ ndk::ScopedAStatus renderIntoBlobStreamBuffer(
+ const int streamId, const int bufferId,
+ const RequestSettings& requestSettings, sp<Fence> fence = nullptr);
// Render current image to the YCbCr buffer.
// If fence is specified, this function will block until the fence is cleared
@@ -149,8 +170,8 @@
::aidl::android::hardware::camera::device::ICameraDeviceCallback>
mCameraDeviceCallback;
- const int mInputSurfaceWidth;
- const int mInputSurfaceHeight;
+ const Resolution mInputSurfaceSize;
+ const Resolution mReportedSensorSize;
const int mTestMode;
VirtualCameraSessionContext& mSessionContext;
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index 8449f41..dfa71f3 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -42,6 +42,7 @@
#include "aidl/android/hardware/camera/common/Status.h"
#include "aidl/android/hardware/camera/device/BufferCache.h"
#include "aidl/android/hardware/camera/device/BufferStatus.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
#include "aidl/android/hardware/camera/device/CaptureRequest.h"
#include "aidl/android/hardware/camera/device/HalStream.h"
#include "aidl/android/hardware/camera/device/NotifyMsg.h"
@@ -61,7 +62,7 @@
#include "util/EglFramebuffer.h"
#include "util/EglProgram.h"
#include "util/JpegUtil.h"
-#include "util/MetadataBuilder.h"
+#include "util/MetadataUtil.h"
#include "util/TestPatternHelper.h"
#include "util/Util.h"
@@ -100,10 +101,16 @@
// Size of request/result metadata fast message queue.
// Setting to 0 to always disables FMQ.
-static constexpr size_t kMetadataMsgQueueSize = 0;
+constexpr size_t kMetadataMsgQueueSize = 0;
// Maximum number of buffers to use per single stream.
-static constexpr size_t kMaxStreamBuffers = 2;
+constexpr size_t kMaxStreamBuffers = 2;
+
+constexpr int32_t kDefaultJpegQuality = 80;
+constexpr int32_t kDefaultJpegThumbnailQuality = 70;
+
+// Thumbnail size (0,0) correspods to disabling thumbnail.
+const Resolution kDefaultJpegThumbnailSize(0, 0);
camera_metadata_enum_android_control_capture_intent_t requestTemplateToIntent(
const RequestTemplate type) {
@@ -144,10 +151,15 @@
.setControlAePrecaptureTrigger(
ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE)
.setControlAfTrigger(ANDROID_CONTROL_AF_TRIGGER_IDLE)
- .setControlAfMode(ANDROID_CONTROL_AF_MODE_AUTO)
+ .setControlAfMode(ANDROID_CONTROL_AF_MODE_OFF)
.setControlAwbMode(ANDROID_CONTROL_AWB_MODE_AUTO)
+ .setControlEffectMode(ANDROID_CONTROL_EFFECT_MODE_OFF)
.setFaceDetectMode(ANDROID_STATISTICS_FACE_DETECT_MODE_OFF)
.setFlashMode(ANDROID_FLASH_MODE_OFF)
+ .setFlashState(ANDROID_FLASH_STATE_UNAVAILABLE)
+ .setJpegQuality(VirtualCameraDevice::kDefaultJpegQuality)
+ .setJpegThumbnailQuality(VirtualCameraDevice::kDefaultJpegQuality)
+ .setJpegThumbnailSize(0, 0)
.build();
if (metadata == nullptr) {
ALOGE("%s: Failed to construct metadata for default request type %s",
@@ -189,6 +201,16 @@
}));
}
+RequestSettings createSettingsFromMetadata(const CameraMetadata& metadata) {
+ return RequestSettings{
+ .jpegQuality = getJpegQuality(metadata).value_or(
+ VirtualCameraDevice::kDefaultJpegQuality),
+ .thumbnailResolution =
+ getJpegThumbnailSize(metadata).value_or(Resolution(0, 0)),
+ .thumbnailJpegQuality = getJpegThumbnailQuality(metadata).value_or(
+ VirtualCameraDevice::kDefaultJpegQuality)};
+}
+
} // namespace
VirtualCameraSession::VirtualCameraSession(
@@ -279,7 +301,8 @@
// If there's no client callback, start camera in test mode.
const bool testMode = mVirtualCameraClientCallback == nullptr;
mRenderThread = std::make_unique<VirtualCameraRenderThread>(
- mSessionContext, inputWidth, inputHeight, mCameraDeviceCallback,
+ mSessionContext, Resolution(inputWidth, inputHeight),
+ virtualCamera->getMaxInputResolution(), mCameraDeviceCallback,
testMode);
mRenderThread->start();
inputSurface = mRenderThread->getInputSurface();
@@ -295,7 +318,6 @@
inputWidth, inputHeight, Format::YUV_420_888);
}
- mFirstRequest.store(true);
return ndk::ScopedAStatus::ok();
}
@@ -437,13 +459,25 @@
const CaptureRequest& request) {
ALOGD("%s: request: %s", __func__, request.toString().c_str());
- if (mFirstRequest.exchange(false) && request.settings.metadata.empty()) {
- return cameraStatus(Status::ILLEGAL_ARGUMENT);
- }
-
std::shared_ptr<ICameraDeviceCallback> cameraCallback = nullptr;
+ RequestSettings requestSettings;
{
std::lock_guard<std::mutex> lock(mLock);
+
+ // If metadata it empty, last received metadata applies, if it's non-empty
+ // update it.
+ if (!request.settings.metadata.empty()) {
+ mCurrentRequestMetadata = request.settings;
+ }
+
+ // We don't have any metadata for this request - this means we received none
+ // in first request, this is an error state.
+ if (mCurrentRequestMetadata.metadata.empty()) {
+ return cameraStatus(Status::ILLEGAL_ARGUMENT);
+ }
+
+ requestSettings = createSettingsFromMetadata(mCurrentRequestMetadata);
+
cameraCallback = mCameraDeviceCallback;
}
@@ -477,7 +511,7 @@
return cameraStatus(Status::INTERNAL_ERROR);
}
mRenderThread->enqueueTask(std::make_unique<ProcessCaptureRequestTask>(
- request.frameNumber, taskBuffers));
+ request.frameNumber, taskBuffers, requestSettings));
}
if (mVirtualCameraClientCallback != nullptr) {
diff --git a/services/camera/virtualcamera/VirtualCameraSession.h b/services/camera/virtualcamera/VirtualCameraSession.h
index 82a7a34..556314f 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.h
+++ b/services/camera/virtualcamera/VirtualCameraSession.h
@@ -25,6 +25,7 @@
#include "VirtualCameraSessionContext.h"
#include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h"
#include "aidl/android/hardware/camera/device/BnCameraDeviceSession.h"
+#include "aidl/android/hardware/camera/device/CameraMetadata.h"
#include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h"
#include "utils/Mutex.h"
@@ -138,7 +139,8 @@
int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>;
std::shared_ptr<ResultMetadataQueue> mResultMetadataQueue;
- std::atomic_bool mFirstRequest{true};
+ aidl::android::hardware::camera::device::CameraMetadata mCurrentRequestMetadata
+ GUARDED_BY(mLock);
std::unique_ptr<VirtualCameraRenderThread> mRenderThread GUARDED_BY(mLock);
};
diff --git a/services/camera/virtualcamera/aidl/Android.bp b/services/camera/virtualcamera/aidl/Android.bp
index a02d390..b3fe3ad 100644
--- a/services/camera/virtualcamera/aidl/Android.bp
+++ b/services/camera/virtualcamera/aidl/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_xr_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/camera/virtualcamera/flags/Android.bp b/services/camera/virtualcamera/flags/Android.bp
index 3ff67ea..5fa8852 100644
--- a/services/camera/virtualcamera/flags/Android.bp
+++ b/services/camera/virtualcamera/flags/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_xr_framework",
+}
+
soong_config_module_type {
name: "virtual_device_build_flags_cc_defaults",
module_type: "cc_defaults",
diff --git a/services/camera/virtualcamera/fuzzer/Android.bp b/services/camera/virtualcamera/fuzzer/Android.bp
index afa1e44..6a72167 100644
--- a/services/camera/virtualcamera/fuzzer/Android.bp
+++ b/services/camera/virtualcamera/fuzzer/Android.bp
@@ -16,6 +16,7 @@
*
*****************************************************************************/
package {
+ default_team: "trendy_team_xr_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/camera/virtualcamera/tests/Android.bp b/services/camera/virtualcamera/tests/Android.bp
index 13104c1..c51b4a3 100644
--- a/services/camera/virtualcamera/tests/Android.bp
+++ b/services/camera/virtualcamera/tests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_xr_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
index 35bf752..9146d8a 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraDeviceTest.cc
@@ -30,6 +30,8 @@
#include "gtest/gtest.h"
#include "log/log_main.h"
#include "system/camera_metadata.h"
+#include "util/MetadataUtil.h"
+#include "util/Util.h"
#include "utils/Errors.h"
namespace android {
@@ -47,6 +49,7 @@
using ::aidl::android::hardware::camera::device::StreamConfiguration;
using ::aidl::android::hardware::camera::device::StreamType;
using ::aidl::android::hardware::graphics::common::PixelFormat;
+using ::testing::ElementsAre;
using ::testing::UnorderedElementsAreArray;
using metadata_stream_t =
camera_metadata_enum_android_scaler_available_stream_configurations_t;
@@ -306,6 +309,16 @@
EXPECT_FALSE(aidl_ret);
}
+TEST_F(VirtualCameraDeviceTest, thumbnailSizeWithCompatibleAspectRatio) {
+ CameraMetadata metadata;
+ ASSERT_TRUE(mCamera->getCameraCharacteristics(&metadata).isOk());
+
+ // Camera is configured with VGA input, we expect 240 x 180 thumbnail size in
+ // characteristics, since it has same aspect ratio.
+ EXPECT_THAT(getJpegAvailableThumbnailSizes(metadata),
+ ElementsAre(Resolution(0, 0), Resolution(240, 180)));
+}
+
} // namespace
} // namespace virtualcamera
} // namespace companion
diff --git a/services/camera/virtualcamera/tests/VirtualCameraRenderThreadTest.cc b/services/camera/virtualcamera/tests/VirtualCameraRenderThreadTest.cc
index 5f899b8..ddcb789 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraRenderThreadTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraRenderThreadTest.cc
@@ -33,6 +33,7 @@
#include "android/binder_auto_utils.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "util/Util.h"
namespace android {
namespace companion {
@@ -62,6 +63,7 @@
constexpr int kInputWidth = 640;
constexpr int kInputHeight = 480;
+const Resolution kInputResolution(kInputWidth, kInputHeight);
Matcher<StreamBuffer> IsStreamBufferWithStatus(const int streamId,
const int bufferId,
@@ -102,7 +104,8 @@
mMockCameraDeviceCallback =
ndk::SharedRefBase::make<MockCameraDeviceCallback>();
mRenderThread = std::make_unique<VirtualCameraRenderThread>(
- *mSessionContext, kInputWidth, kInputHeight, mMockCameraDeviceCallback);
+ *mSessionContext, kInputResolution,
+ /*reportedSensorSize*/ kInputResolution, mMockCameraDeviceCallback);
}
protected:
diff --git a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
index 446c679..1af8b80 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
@@ -30,7 +30,7 @@
#include "android/binder_interface_utils.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "util/MetadataBuilder.h"
+#include "util/MetadataUtil.h"
namespace android {
namespace companion {
diff --git a/services/camera/virtualcamera/util/JpegUtil.cc b/services/camera/virtualcamera/util/JpegUtil.cc
index 2b19c13..98f2448 100644
--- a/services/camera/virtualcamera/util/JpegUtil.cc
+++ b/services/camera/virtualcamera/util/JpegUtil.cc
@@ -19,7 +19,7 @@
#include <cstddef>
#include <cstdint>
-#include <memory>
+#include <optional>
#include <vector>
#include "android/hardware_buffer.h"
@@ -34,11 +34,9 @@
namespace virtualcamera {
namespace {
-constexpr int kJpegQuality = 80;
-
class LibJpegContext {
public:
- LibJpegContext(int width, int height, const size_t outBufferSize,
+ LibJpegContext(int width, int height, int quality, const size_t outBufferSize,
void* outBuffer)
: mWidth(width),
mHeight(height),
@@ -76,7 +74,7 @@
jpeg_set_defaults(&mCompressStruct);
// Set quality and colorspace.
- jpeg_set_quality(&mCompressStruct, kJpegQuality, 1);
+ jpeg_set_quality(&mCompressStruct, quality, 1);
jpeg_set_colorspace(&mCompressStruct, JCS_YCbCr);
// Configure RAW input mode - this let's libjpeg know we're providing raw,
@@ -94,11 +92,31 @@
mCompressStruct.comp_info[2].v_samp_factor = 1;
}
- bool compress(const android_ycbcr& ycbr) {
+ LibJpegContext& setApp1Data(const uint8_t* app1Data, const size_t size) {
+ mApp1Data = app1Data;
+ mApp1DataSize = size;
+ return *this;
+ }
+
+ std::optional<size_t> compress(const android_ycbcr& ycbr) {
+ // TODO(b/301023410) - Add support for compressing image sizes not aligned
+ // with DCT size.
+ if (mWidth % (2 * DCTSIZE) || (mHeight % (2 * DCTSIZE))) {
+ ALOGE(
+ "%s: Compressing YUV420 image with size %dx%d not aligned with 2 * "
+ "DCTSIZE (%d) is not currently supported.",
+ __func__, mWidth, mHeight, 2 * DCTSIZE);
+ return std::nullopt;
+ }
+
+ // Chroma planes have 1/2 resolution of the original image.
+ const int cHeight = mHeight / 2;
+ const int cWidth = mWidth / 2;
+
// Prepare arrays of pointers to scanlines of each plane.
std::vector<JSAMPROW> yLines(mHeight);
- std::vector<JSAMPROW> cbLines(mHeight / 2);
- std::vector<JSAMPROW> crLines(mHeight / 2);
+ std::vector<JSAMPROW> cbLines(cHeight);
+ std::vector<JSAMPROW> crLines(cHeight);
uint8_t* y = static_cast<uint8_t*>(ycbr.y);
uint8_t* cb = static_cast<uint8_t*>(ycbr.cb);
@@ -107,23 +125,27 @@
// Since UV samples might be interleaved (semiplanar) we need to copy
// them to separate planes, since libjpeg doesn't directly
// support processing semiplanar YUV.
- const int c_samples = (mWidth / 2) * (mHeight / 2);
- std::vector<uint8_t> cb_plane(c_samples);
- std::vector<uint8_t> cr_plane(c_samples);
+ const int cSamples = cWidth * cHeight;
+ std::vector<uint8_t> cb_plane(cSamples);
+ std::vector<uint8_t> cr_plane(cSamples);
// TODO(b/301023410) - Use libyuv or ARM SIMD for "unzipping" the data.
- for (int i = 0; i < c_samples; ++i) {
- cb_plane[i] = *cb;
- cr_plane[i] = *cr;
- cb += ycbr.chroma_step;
- cr += ycbr.chroma_step;
+ int out_idx = 0;
+ for (int i = 0; i < cHeight; ++i) {
+ for (int j = 0; j < cWidth; ++j) {
+ cb_plane[out_idx] = cb[j * ycbr.chroma_step];
+ cr_plane[out_idx] = cr[j * ycbr.chroma_step];
+ out_idx++;
+ }
+ cb += ycbr.cstride;
+ cr += ycbr.cstride;
}
// Collect pointers to individual scanline of each plane.
for (int i = 0; i < mHeight; ++i) {
yLines[i] = y + i * ycbr.ystride;
}
- for (int i = 0; i < (mHeight / 2); ++i) {
+ for (int i = 0; i < cHeight; ++i) {
cbLines[i] = cb_plane.data() + i * (mWidth / 2);
crLines[i] = cr_plane.data() + i * (mWidth / 2);
}
@@ -131,7 +153,7 @@
return compress(yLines, cbLines, crLines);
}
- bool compressBlackImage() {
+ std::optional<size_t> compressBlackImage() {
// We only really need to prepare one scanline for Y and one shared scanline
// for Cb & Cr.
std::vector<uint8_t> yLine(mWidth, 0);
@@ -165,11 +187,18 @@
// Takes vector of pointers to Y / Cb / Cr scanlines as an input. Length of
// each vector needs to correspond to height of corresponding plane.
//
- // Returns true if compression is successful, false otherwise.
- bool compress(std::vector<JSAMPROW>& yLines, std::vector<JSAMPROW>& cbLines,
- std::vector<JSAMPROW>& crLines) {
+ // Returns size of compressed image in bytes on success, empty optional otherwise.
+ std::optional<size_t> compress(std::vector<JSAMPROW>& yLines,
+ std::vector<JSAMPROW>& cbLines,
+ std::vector<JSAMPROW>& crLines) {
jpeg_start_compress(&mCompressStruct, TRUE);
+ if (mApp1Data != nullptr && mApp1DataSize > 0) {
+ ALOGV("%s: Writing exif, size %zu B", __func__, mApp1DataSize);
+ jpeg_write_marker(&mCompressStruct, JPEG_APP0 + 1,
+ static_cast<const JOCTET*>(mApp1Data), mApp1DataSize);
+ }
+
while (mCompressStruct.next_scanline < mCompressStruct.image_height) {
const uint32_t batchSize = DCTSIZE * 2;
const uint32_t nl = mCompressStruct.next_scanline;
@@ -181,11 +210,11 @@
ALOGE("%s: compressed %u lines, expected %u (total %u/%u)",
__FUNCTION__, done, batchSize, mCompressStruct.next_scanline,
mCompressStruct.image_height);
- return false;
+ return std::nullopt;
}
}
jpeg_finish_compress(&mCompressStruct);
- return mSuccess;
+ return mEncodedSize;
}
// === libjpeg callbacks below ===
@@ -217,6 +246,10 @@
jpeg_error_mgr mErrorMgr;
jpeg_destination_mgr mDestinationMgr;
+ // APP1 data.
+ const uint8_t* mApp1Data = nullptr;
+ size_t mApp1DataSize = 0;
+
// Dimensions of the input image.
int mWidth;
int mHeight;
@@ -235,15 +268,26 @@
} // namespace
-bool compressJpeg(int width, int height, const android_ycbcr& ycbcr,
- size_t outBufferSize, void* outBuffer) {
- return LibJpegContext(width, height, outBufferSize, outBuffer).compress(ycbcr);
+std::optional<size_t> compressJpeg(const int width, const int height,
+ const int quality, const android_ycbcr& ycbcr,
+ const std::vector<uint8_t>& app1ExifData,
+ size_t outBufferSize, void* outBuffer) {
+ LibJpegContext context(width, height, quality, outBufferSize, outBuffer);
+ if (!app1ExifData.empty()) {
+ context.setApp1Data(app1ExifData.data(), app1ExifData.size());
+ }
+ return context.compress(ycbcr);
}
-bool compressBlackJpeg(int width, int height, size_t outBufferSize,
- void* outBuffer) {
- return LibJpegContext(width, height, outBufferSize, outBuffer)
- .compressBlackImage();
+std::optional<size_t> compressBlackJpeg(const int width, const int height,
+ const int quality,
+ const std::vector<uint8_t>& app1ExifData,
+ size_t outBufferSize, void* outBuffer) {
+ LibJpegContext context(width, height, quality, outBufferSize, outBuffer);
+ if (!app1ExifData.empty()) {
+ context.setApp1Data(app1ExifData.data(), app1ExifData.size());
+ }
+ return context.compressBlackImage();
}
} // namespace virtualcamera
diff --git a/services/camera/virtualcamera/util/JpegUtil.h b/services/camera/virtualcamera/util/JpegUtil.h
index c44d0a8..e64fb4f 100644
--- a/services/camera/virtualcamera/util/JpegUtil.h
+++ b/services/camera/virtualcamera/util/JpegUtil.h
@@ -18,6 +18,7 @@
#define ANDROID_COMPANION_VIRTUALCAMERA_JPEGUTIL_H
#include <memory>
+#include <optional>
#include "android/hardware_buffer.h"
#include "system/graphics.h"
@@ -27,14 +28,34 @@
namespace virtualcamera {
// Jpeg-compress image into the output buffer.
-// Returns true if the compression was successful, false otherwise.
-bool compressJpeg(int width, int height, const android_ycbcr& ycbcr,
- size_t outBufferSize, void* outBuffer);
+// * width - width of the image
+// * heigh - height of the image
+// * quality - 0-100, higher number corresponds to higher quality.
+// * ycbr - android_ycbr structure describing layout of input YUV420 image.
+// * app1ExifData - vector containing data to be included in APP1
+// segment. Can be empty.
+// * outBufferSize - capacity of the output buffer.
+// * outBuffer - output buffer to write compressed data into.
+// Returns size of compressed data if the compression was successful,
+// empty optional otherwise.
+std::optional<size_t> compressJpeg(int width, int height, int quality,
+ const android_ycbcr& ycbcr,
+ const std::vector<uint8_t>& app1ExifData,
+ size_t outBufferSize, void* outBuffer);
// Jpeg-compress all-black image into the output buffer.
-// Returns true if the compression was successful, false otherwise.
-bool compressBlackJpeg(int width, int height, size_t outBufferSize,
- void* outBuffer);
+// * width - width of the image
+// * heigh - height of the image
+// * quality - 0-100, higher number corresponds to higher quality.
+// * app1ExifData - vector containing data to be included in APP1
+// segment. Can be empty.
+// * outBufferSize - capacity of the output buffer.
+// * outBuffer - output buffer to write compressed data into.
+// Returns size of compressed data if the compression was successful,
+// empty optional otherwise.
+std::optional<size_t> compressBlackJpeg(int width, int height, int quality,
+ const std::vector<uint8_t>& app1ExifData,
+ size_t outBufferSize, void* outBuffer);
} // namespace virtualcamera
} // namespace companion
diff --git a/services/camera/virtualcamera/util/MetadataBuilder.cc b/services/camera/virtualcamera/util/MetadataUtil.cc
similarity index 80%
rename from services/camera/virtualcamera/util/MetadataBuilder.cc
rename to services/camera/virtualcamera/util/MetadataUtil.cc
index db5a5dd..d223f17 100644
--- a/services/camera/virtualcamera/util/MetadataBuilder.cc
+++ b/services/camera/virtualcamera/util/MetadataUtil.cc
@@ -15,9 +15,9 @@
*/
// #define LOG_NDEBUG 0
-#define LOG_TAG "MetadataBuilder"
+#define LOG_TAG "MetadataUtil"
-#include "MetadataBuilder.h"
+#include "MetadataUtil.h"
#include <algorithm>
#include <cstdint>
@@ -137,6 +137,14 @@
return *this;
}
+MetadataBuilder& MetadataBuilder::setAvailableTestPatternModes(
+ const std::vector<camera_metadata_enum_android_sensor_test_pattern_mode>&
+ testPatternModes) {
+ mEntryMap[ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES] =
+ convertTo<int32_t>(testPatternModes);
+ return *this;
+}
+
MetadataBuilder& MetadataBuilder::setFaceDetectMode(
const camera_metadata_enum_android_statistics_face_detect_mode_t
faceDetectMode) {
@@ -175,6 +183,21 @@
return *this;
}
+MetadataBuilder& MetadataBuilder::setControlEffectMode(
+ const camera_metadata_enum_android_control_effect_mode_t effectMode) {
+ mEntryMap[ANDROID_CONTROL_EFFECT_MODE] = asVectorOf<uint8_t>(effectMode);
+ return *this;
+}
+
+MetadataBuilder& MetadataBuilder::setControlAvailableVideoStabilizationModes(
+ const std::vector<
+ camera_metadata_enum_android_control_video_stabilization_mode_t>&
+ videoStabilizationModes) {
+ mEntryMap[ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES] =
+ convertTo<uint8_t>(videoStabilizationModes);
+ return *this;
+}
+
MetadataBuilder& MetadataBuilder::setControlAfAvailableModes(
const std::vector<camera_metadata_enum_android_control_af_mode_t>&
availableModes) {
@@ -199,7 +222,7 @@
MetadataBuilder& MetadataBuilder::setControlAeAvailableFpsRanges(
const std::vector<FpsRange>& fpsRanges) {
std::vector<int32_t> ranges;
- ranges.resize(2 * fpsRanges.size());
+ ranges.reserve(2 * fpsRanges.size());
for (const FpsRange fpsRange : fpsRanges) {
ranges.push_back(fpsRange.minFps);
ranges.push_back(fpsRange.maxFps);
@@ -341,11 +364,26 @@
return *this;
}
+MetadataBuilder& MetadataBuilder::setCropRegion(const int32_t x, const int32_t y,
+ const int32_t width,
+ const int32_t height) {
+ mEntryMap[ANDROID_SCALER_CROP_REGION] =
+ std::vector<int32_t>({x, y, width, height});
+ return *this;
+}
+
MetadataBuilder& MetadataBuilder::setMaxJpegSize(const int32_t size) {
mEntryMap[ANDROID_JPEG_MAX_SIZE] = asVectorOf<int32_t>(size);
return *this;
}
+MetadataBuilder& MetadataBuilder::setMaxFrameDuration(
+ const std::chrono::nanoseconds duration) {
+ mEntryMap[ANDROID_SENSOR_INFO_MAX_FRAME_DURATION] =
+ asVectorOf<int64_t>(duration.count());
+ return *this;
+}
+
MetadataBuilder& MetadataBuilder::setJpegAvailableThumbnailSizes(
const std::vector<Resolution>& thumbnailSizes) {
std::vector<int32_t> sizes;
@@ -358,6 +396,22 @@
return *this;
}
+MetadataBuilder& MetadataBuilder::setJpegQuality(const uint8_t quality) {
+ mEntryMap[ANDROID_JPEG_QUALITY] = asVectorOf<uint8_t>(quality);
+ return *this;
+}
+
+MetadataBuilder& MetadataBuilder::setJpegThumbnailSize(const int width,
+ const int height) {
+ mEntryMap[ANDROID_JPEG_THUMBNAIL_SIZE] = std::vector<int32_t>({width, height});
+ return *this;
+}
+
+MetadataBuilder& MetadataBuilder::setJpegThumbnailQuality(const uint8_t quality) {
+ mEntryMap[ANDROID_JPEG_THUMBNAIL_QUALITY] = asVectorOf<uint8_t>(quality);
+ return *this;
+}
+
MetadataBuilder& MetadataBuilder::setMaxNumberOutputStreams(
const int32_t maxRawStreams, const int32_t maxProcessedStreams,
const int32_t maxStallStreams) {
@@ -372,6 +426,16 @@
return *this;
}
+MetadataBuilder& MetadataBuilder::setPipelineMaxDepth(const uint8_t maxDepth) {
+ mEntryMap[ANDROID_REQUEST_PIPELINE_MAX_DEPTH] = asVectorOf<uint8_t>(maxDepth);
+ return *this;
+}
+
+MetadataBuilder& MetadataBuilder::setPipelineDepth(const uint8_t depth) {
+ mEntryMap[ANDROID_REQUEST_PIPELINE_DEPTH] = asVectorOf<uint8_t>(depth);
+ return *this;
+}
+
MetadataBuilder& MetadataBuilder::setAvailableRequestCapabilities(
const std::vector<camera_metadata_enum_android_request_available_capabilities_t>&
requestCapabilities) {
@@ -549,6 +613,67 @@
return aidlMetadata;
}
+std::optional<int32_t> getJpegQuality(
+ const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
+ auto metadata =
+ reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());
+
+ camera_metadata_ro_entry_t entry;
+ if (find_camera_metadata_ro_entry(metadata, ANDROID_JPEG_QUALITY, &entry) !=
+ OK) {
+ return std::nullopt;
+ }
+
+ return *entry.data.i32;
+}
+
+std::optional<Resolution> getJpegThumbnailSize(
+ const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
+ auto metadata =
+ reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());
+
+ camera_metadata_ro_entry_t entry;
+ if (find_camera_metadata_ro_entry(metadata, ANDROID_JPEG_THUMBNAIL_SIZE,
+ &entry) != OK) {
+ return std::nullopt;
+ }
+
+ return Resolution(entry.data.i32[0], entry.data.i32[1]);
+}
+
+std::optional<int32_t> getJpegThumbnailQuality(
+ const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
+ auto metadata =
+ reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());
+
+ camera_metadata_ro_entry_t entry;
+ if (find_camera_metadata_ro_entry(metadata, ANDROID_JPEG_THUMBNAIL_QUALITY,
+ &entry) != OK) {
+ return std::nullopt;
+ }
+
+ return *entry.data.i32;
+}
+
+std::vector<Resolution> getJpegAvailableThumbnailSizes(
+ const aidl::android::hardware::camera::device::CameraMetadata& cameraMetadata) {
+ auto metadata =
+ reinterpret_cast<const camera_metadata_t*>(cameraMetadata.metadata.data());
+
+ camera_metadata_ro_entry_t entry;
+ if (find_camera_metadata_ro_entry(
+ metadata, ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, &entry) != OK) {
+ return {};
+ }
+
+ std::vector<Resolution> thumbnailSizes;
+ thumbnailSizes.reserve(entry.count / 2);
+ for (int i = 0; i < entry.count; i += 2) {
+ thumbnailSizes.emplace_back(entry.data.i32[i], entry.data.i32[i + 1]);
+ }
+ return thumbnailSizes;
+}
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
diff --git a/services/camera/virtualcamera/util/MetadataBuilder.h b/services/camera/virtualcamera/util/MetadataUtil.h
similarity index 82%
rename from services/camera/virtualcamera/util/MetadataBuilder.h
rename to services/camera/virtualcamera/util/MetadataUtil.h
index fdc35fd..9ddfd81 100644
--- a/services/camera/virtualcamera/util/MetadataBuilder.h
+++ b/services/camera/virtualcamera/util/MetadataUtil.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef ANDROID_COMPANION_VIRTUALCAMERA_METADATABUILDER_H
-#define ANDROID_COMPANION_VIRTUALCAMERA_METADATABUILDER_H
+#ifndef ANDROID_COMPANION_VIRTUALCAMERA_METADATAUTIL_H
+#define ANDROID_COMPANION_VIRTUALCAMERA_METADATAUTIL_H
#include <chrono>
#include <cstdint>
@@ -64,7 +64,8 @@
int32_t maxFps;
bool operator<(const FpsRange& other) const {
- return std::tuple(minFps, maxFps) < std::tuple(other.minFps, other.maxFps);
+ return maxFps == other.maxFps ? minFps < other.minFps
+ : maxFps < other.maxFps;
}
};
@@ -97,7 +98,8 @@
sensorReadoutTimestamp);
// See ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS in CameraMetadataTag.aidl.
- MetadataBuilder& setAvailableFocalLengths(const std::vector<float>& focalLengths);
+ MetadataBuilder& setAvailableFocalLengths(
+ const std::vector<float>& focalLengths);
// See ANDROID_LENS_FOCAL_LENGTH in CameraMetadataTag.aidl.
MetadataBuilder& setFocalLength(float focalLength);
@@ -129,6 +131,11 @@
const std::vector<camera_metadata_enum_android_statistics_face_detect_mode_t>&
faceDetectMode);
+ // See SENSOR_AVAILABLE_TEST_PATTERN_MODES in CameraCharacteristics.java.
+ MetadataBuilder& setAvailableTestPatternModes(
+ const std::vector<camera_metadata_enum_android_sensor_test_pattern_mode>&
+ testPatternModes);
+
// See ANDROID_STATISTICS_FACE_DETECT_MODE in CaptureRequest.java.
MetadataBuilder& setFaceDetectMode(
camera_metadata_enum_android_statistics_face_detect_mode_t faceDetectMode);
@@ -161,6 +168,16 @@
const std::vector<camera_metadata_enum_android_control_effect_mode>&
availableEffects);
+ // See CONTROL_EFFECT_MODE in CaptureRequest.java.
+ MetadataBuilder& setControlEffectMode(
+ camera_metadata_enum_android_control_effect_mode_t effectMode);
+
+ // See ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES
+ MetadataBuilder& setControlAvailableVideoStabilizationModes(
+ const std::vector<
+ camera_metadata_enum_android_control_video_stabilization_mode_t>&
+ videoStabilizationModes);
+
// See CONTROL_AE_AVAILABLE_ANTIBANDING_MODES in CameraCharacteristics.java.
MetadataBuilder& setControlAeAvailableAntibandingModes(
const std::vector<camera_metadata_enum_android_control_ae_antibanding_mode_t>&
@@ -242,6 +259,10 @@
MetadataBuilder& setControlAwbRegions(
const std::vector<ControlRegion>& awbRegions);
+ // See ANDROID_SCALER_CROP_REGION in CaptureRequest.java.
+ MetadataBuilder& setCropRegion(int32_t x, int32_t y, int32_t width,
+ int32_t height);
+
// See ANDROID_CONTROL_AF_REGIONS in CameraMetadataTag.aidl.
MetadataBuilder& setControlAfRegions(
const std::vector<ControlRegion>& afRegions);
@@ -251,10 +272,22 @@
// See ANDROID_JPEG_SIZE in CameraMetadataTag.aidl.
MetadataBuilder& setMaxJpegSize(int32_t size);
+ // See SENSOR_INFO_MAX_FRAME_DURATION in CameraCharacteristic.java.
+ MetadataBuilder& setMaxFrameDuration(std::chrono::nanoseconds duration);
+
// See JPEG_AVAILABLE_THUMBNAIL_SIZES in CameraCharacteristic.java.
MetadataBuilder& setJpegAvailableThumbnailSizes(
const std::vector<Resolution>& thumbnailSizes);
+ // See JPEG_QUALITY in CaptureRequest.java.
+ MetadataBuilder& setJpegQuality(uint8_t quality);
+
+ // See JPEG_THUMBNAIL_SIZE in CaptureRequest.java.
+ MetadataBuilder& setJpegThumbnailSize(int width, int height);
+
+ // See JPEG_THUMBNAIL_QUALITY in CaptureRequest.java.
+ MetadataBuilder& setJpegThumbnailQuality(uint8_t quality);
+
// The maximum numbers of different types of output streams
// that can be configured and used simultaneously by a camera device.
//
@@ -267,6 +300,12 @@
MetadataBuilder& setSyncMaxLatency(
camera_metadata_enum_android_sync_max_latency setSyncMaxLatency);
+ // See REQUEST_PIPELINE_MAX_DEPTH in CameraCharacteristic.java.
+ MetadataBuilder& setPipelineMaxDepth(uint8_t maxDepth);
+
+ // See REQUEST_PIPELINE_DEPTH in CaptureResult.java.
+ MetadataBuilder& setPipelineDepth(uint8_t depth);
+
// See ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM in CameraMetadataTag.aidl.
MetadataBuilder& setAvailableMaxDigitalZoom(const float maxZoom);
@@ -324,8 +363,25 @@
bool mExtendWithAvailableCharacteristicsKeys = false;
};
+// Returns JPEG_QUALITY from metadata, or nullopt if the key is not present.
+std::optional<int32_t> getJpegQuality(
+ const aidl::android::hardware::camera::device::CameraMetadata& metadata);
+
+// Returns JPEG_THUMBNAIL_SIZE from metadata, or nullopt if the key is not present.
+std::optional<Resolution> getJpegThumbnailSize(
+ const aidl::android::hardware::camera::device::CameraMetadata& metadata);
+
+// Returns JPEG_THUMBNAIL_QUALITY from metadata, or nullopt if the key is not present.
+std::optional<int32_t> getJpegThumbnailQuality(
+ const aidl::android::hardware::camera::device::CameraMetadata& metadata);
+
+// Returns JPEG_AVAILABLE_THUMBNAIL_SIZES from metadata, or nullopt if the key
+// is not present.
+std::vector<Resolution> getJpegAvailableThumbnailSizes(
+ const aidl::android::hardware::camera::device::CameraMetadata& metadata);
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
-#endif // ANDROID_COMPANION_VIRTUALCAMERA_METADATABUILDER_H
+#endif // ANDROID_COMPANION_VIRTUALCAMERA_METADATAUTIL_H
diff --git a/services/camera/virtualcamera/util/Util.cc b/services/camera/virtualcamera/util/Util.cc
index 2d0545d..b2048bc 100644
--- a/services/camera/virtualcamera/util/Util.cc
+++ b/services/camera/virtualcamera/util/Util.cc
@@ -20,8 +20,13 @@
#include <algorithm>
#include <array>
+#include <cstdint>
+#include <memory>
+#include "android/hardware_buffer.h"
#include "jpeglib.h"
+#include "ui/GraphicBuffer.h"
+#include "utils/Errors.h"
namespace android {
namespace companion {
@@ -40,6 +45,82 @@
constexpr std::array<Format, 2> kSupportedFormats{Format::YUV_420_888,
Format::RGBA_8888};
+YCbCrLockGuard::YCbCrLockGuard(std::shared_ptr<AHardwareBuffer> hwBuffer,
+ const uint32_t usageFlags)
+ : mHwBuffer(hwBuffer) {
+ GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(mHwBuffer.get());
+ if (gBuffer == nullptr) {
+ ALOGE("%s: Attempting to lock nullptr buffer.", __func__);
+ return;
+ }
+ mLockStatus = gBuffer->lockYCbCr(usageFlags, &mYCbCr);
+ if (mLockStatus != OK) {
+ ALOGE("%s: Failed to lock graphic buffer: %s", __func__,
+ statusToString(mLockStatus).c_str());
+ }
+}
+
+YCbCrLockGuard::~YCbCrLockGuard() {
+ if (getStatus() != OK) {
+ return;
+ }
+
+ GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(mHwBuffer.get());
+ if (gBuffer == nullptr) {
+ return;
+ }
+ gBuffer->unlock();
+ status_t status = gBuffer->unlock();
+ if (status != NO_ERROR) {
+ ALOGE("Failed to unlock graphic buffer: %s", statusToString(status).c_str());
+ }
+}
+
+status_t YCbCrLockGuard::getStatus() const {
+ return mLockStatus;
+}
+
+const android_ycbcr& YCbCrLockGuard::operator*() const {
+ LOG_ALWAYS_FATAL_IF(getStatus() != OK,
+ "Dereferencing unlocked YCbCrLockGuard, status is %s",
+ statusToString(mLockStatus).c_str());
+ return mYCbCr;
+}
+
+PlanesLockGuard::PlanesLockGuard(std::shared_ptr<AHardwareBuffer> hwBuffer,
+ const uint64_t usageFlags, sp<Fence> fence) {
+ if (hwBuffer == nullptr) {
+ ALOGE("%s: Attempting to lock nullptr buffer.", __func__);
+ return;
+ }
+
+ const int32_t rawFence = fence != nullptr ? fence->get() : -1;
+ mLockStatus = static_cast<status_t>(AHardwareBuffer_lockPlanes(
+ hwBuffer.get(), usageFlags, rawFence, nullptr, &mPlanes));
+ if (mLockStatus != OK) {
+ ALOGE("%s: Failed to lock graphic buffer: %s", __func__,
+ statusToString(mLockStatus).c_str());
+ }
+}
+
+PlanesLockGuard::~PlanesLockGuard() {
+ if (getStatus() != OK || mHwBuffer == nullptr) {
+ return;
+ }
+ AHardwareBuffer_unlock(mHwBuffer.get(), /*fence=*/nullptr);
+}
+
+int PlanesLockGuard::getStatus() const {
+ return mLockStatus;
+}
+
+const AHardwareBuffer_Planes& PlanesLockGuard::operator*() const {
+ LOG_ALWAYS_FATAL_IF(getStatus() != OK,
+ "Dereferencing unlocked PlanesLockGuard, status is %s",
+ statusToString(mLockStatus).c_str());
+ return mPlanes;
+}
+
sp<Fence> importFence(const NativeHandle& aidlHandle) {
if (aidlHandle.fds.size() != 1) {
return sp<Fence>::make();
@@ -79,6 +160,10 @@
return true;
}
+std::ostream& operator<<(std::ostream& os, const Resolution& resolution) {
+ return os << resolution.width << "x" << resolution.height;
+}
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
diff --git a/services/camera/virtualcamera/util/Util.h b/services/camera/virtualcamera/util/Util.h
index 9f81bb1..d5b0b1f 100644
--- a/services/camera/virtualcamera/util/Util.h
+++ b/services/camera/virtualcamera/util/Util.h
@@ -18,17 +18,80 @@
#define ANDROID_COMPANION_VIRTUALCAMERA_UTIL_H
#include <cstdint>
+#include <memory>
#include "aidl/android/companion/virtualcamera/Format.h"
#include "aidl/android/hardware/camera/common/Status.h"
#include "aidl/android/hardware/camera/device/StreamBuffer.h"
#include "android/binder_auto_utils.h"
+#include "android/hardware_buffer.h"
+#include "system/graphics.h"
#include "ui/Fence.h"
namespace android {
namespace companion {
namespace virtualcamera {
+// RAII utility class to safely lock AHardwareBuffer and obtain android_ycbcr
+// structure describing YUV plane layout.
+//
+// Access to the buffer is locked immediatelly afer construction.
+class YCbCrLockGuard {
+ public:
+ YCbCrLockGuard(std::shared_ptr<AHardwareBuffer> hwBuffer, uint32_t usageFlags);
+ YCbCrLockGuard(YCbCrLockGuard&& other) = default;
+ ~YCbCrLockGuard();
+
+ // Returns OK if the buffer is successfully locked.
+ status_t getStatus() const;
+
+ // Dereferencing instance of this guard returns android_ycbcr structure
+ // describing the layout.
+ // Caller needs to check whether the buffer was successfully locked
+ // before dereferencing.
+ const android_ycbcr& operator*() const;
+
+ // Disable copy.
+ YCbCrLockGuard(const YCbCrLockGuard&) = delete;
+ YCbCrLockGuard& operator=(const YCbCrLockGuard&) = delete;
+
+ private:
+ std::shared_ptr<AHardwareBuffer> mHwBuffer;
+ android_ycbcr mYCbCr = {};
+ status_t mLockStatus = DEAD_OBJECT;
+};
+
+// RAII utility class to safely lock AHardwareBuffer and obtain
+// AHardwareBuffer_Planes (Suitable for interacting with RGBA / BLOB buffers.
+//
+// Access to the buffer is locked immediatelly afer construction.
+class PlanesLockGuard {
+ public:
+ PlanesLockGuard(std::shared_ptr<AHardwareBuffer> hwBuffer,
+ uint64_t usageFlags, sp<Fence> fence = nullptr);
+ PlanesLockGuard(PlanesLockGuard&& other) = default;
+ ~PlanesLockGuard();
+
+ // Returns OK if the buffer is successfully locked.
+ status_t getStatus() const;
+
+ // Dereferencing instance of this guard returns AHardwareBuffer_Planes
+ // structure describing the layout.
+ //
+ // Caller needs to check whether the buffer was successfully locked
+ // before dereferencing.
+ const AHardwareBuffer_Planes& operator*() const;
+
+ // Disable copy.
+ PlanesLockGuard(const PlanesLockGuard&) = delete;
+ PlanesLockGuard& operator=(const YCbCrLockGuard&) = delete;
+
+ private:
+ std::shared_ptr<AHardwareBuffer> mHwBuffer;
+ AHardwareBuffer_Planes mPlanes;
+ status_t mLockStatus = DEAD_OBJECT;
+};
+
// Converts camera AIDL status to ndk::ScopedAStatus
inline ndk::ScopedAStatus cameraStatus(
const ::aidl::android::hardware::camera::common::Status status) {
@@ -54,6 +117,7 @@
// Representation of resolution / size.
struct Resolution {
+ Resolution() = default;
Resolution(const int w, const int h) : width(w), height(h) {
}
@@ -69,10 +133,12 @@
return width == other.width && height == other.height;
}
- const int width;
- const int height;
+ int width = 0;
+ int height = 0;
};
+std::ostream& operator<<(std::ostream& os, const Resolution& resolution);
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
diff --git a/services/medialog/fuzzer/Android.bp b/services/medialog/fuzzer/Android.bp
index c96c37b..bf90f43 100644
--- a/services/medialog/fuzzer/Android.bp
+++ b/services/medialog/fuzzer/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"
diff --git a/services/oboeservice/fuzzer/Android.bp b/services/oboeservice/fuzzer/Android.bp
index c130b12..31ed8ac 100644
--- a/services/oboeservice/fuzzer/Android.bp
+++ b/services/oboeservice/fuzzer/Android.bp
@@ -19,6 +19,7 @@
*/
package {
+ default_team: "trendy_team_media_framework_audio",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_av_license"