Merge "Refactored camera_c2OutputConfiguration_fuzzer" into main
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 2e808d1..424923a 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -880,7 +880,7 @@
return OK;
}
-metadata_vendor_id_t CameraMetadata::getVendorId() {
+metadata_vendor_id_t CameraMetadata::getVendorId() const {
return get_camera_metadata_vendor_id(mBuffer);
}
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index fb26f83..c12a1a1 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -466,7 +466,7 @@
int VendorTagDescriptorCache::getTagType(uint32_t tag,
metadata_vendor_id_t id) const {
- int ret = 0;
+ int ret = -1;
auto desc = mVendorMap.find(id);
if (desc != mVendorMap.end()) {
ret = desc->second->getTagType(tag);
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 8078cea..0eeeb7f 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -149,6 +149,18 @@
void remapCameraIds(in CameraIdRemapping cameraIdRemapping);
/**
+ * Inject Session Params into an existing camera session.
+ *
+ * @param cameraId the camera id session to inject session params into. Note that
+ * if there is no active session for the input cameraid, this operation
+ * will be a no-op. In addition, future camera sessions for cameraid will
+ * not be affected.
+ * @param sessionParams the session params to override for the existing session.
+ */
+ void injectSessionParams(@utf8InCpp String cameraId,
+ in CameraMetadataNative sessionParams);
+
+ /**
* Remove listener for changes to camera device and flashlight state.
*/
void removeListener(ICameraServiceListener listener);
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 8e1fcc0..843e0d4 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -99,6 +99,15 @@
*/
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 f7deda1..5d2a263 100644
--- a/camera/camera_platform.aconfig
+++ b/camera/camera_platform.aconfig
@@ -62,3 +62,17 @@
description: "Enable session parameter injection via reconfiguration"
bug: "308984721"
}
+
+flag {
+ namespace: "camera_platform"
+ name: "camera_ae_mode_low_light_boost"
+ description: "An AE mode that enables increased brightening in low light scenes"
+ bug: "312803148"
+}
+
+flag {
+ namespace: "camera_platform"
+ name: "multiresolution_imagereader_usage_config"
+ description: "Enable creating MultiResolutionImageReader with usage flag configuration"
+ bug: "301588215"
+}
diff --git a/camera/include/camera/CameraMetadata.h b/camera/include/camera/CameraMetadata.h
index c56ee6d..2903dfb 100644
--- a/camera/include/camera/CameraMetadata.h
+++ b/camera/include/camera/CameraMetadata.h
@@ -245,7 +245,7 @@
/**
* Return the current vendor tag id associated with this metadata.
*/
- metadata_vendor_id_t getVendorId();
+ metadata_vendor_id_t getVendorId() const;
private:
camera_metadata_t *mBuffer;
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 3a483cc..2c68cef 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -2244,6 +2244,39 @@
*/
ACAMERA_CONTROL_AUTOFRAMING_STATE = // byte (acamera_metadata_enum_android_control_autoframing_state_t)
ACAMERA_CONTROL_START + 54,
+ /**
+ * <p>The operating luminance range of low light boost measured in lux (lx).</p>
+ *
+ * <p>Type: float[2]</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraManager_getCameraCharacteristics</li>
+ * </ul></p>
+ *
+ */
+ ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE = // float[2]
+ ACAMERA_CONTROL_START + 55,
+ /**
+ * <p>Current state of the low light boost AE mode.</p>
+ *
+ * <p>Type: byte (acamera_metadata_enum_android_control_low_light_boost_state_t)</p>
+ *
+ * <p>This tag may appear in:
+ * <ul>
+ * <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
+ * </ul></p>
+ *
+ * <p>When low light boost is enabled by setting the AE mode to
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
+ * boost when the light level threshold is exceeded.</p>
+ * <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
+ * indicate when it is not being applied by returning 'INACTIVE'.</p>
+ * <p>This key will be absent from the CaptureResult if AE mode is not set to
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY.</p>
+ */
+ ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE = // byte (acamera_metadata_enum_android_control_low_light_boost_state_t)
+ ACAMERA_CONTROL_START + 56,
ACAMERA_CONTROL_END,
/**
@@ -4625,8 +4658,8 @@
* <p>The guaranteed stream combinations related to stream use case for a camera device with
* <a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE">CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE</a>
* capability is documented in the camera device
- * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>. The application is strongly recommended to use one of the guaranteed stream
- * combinations.
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>.
+ * The application is strongly recommended to use one of the guaranteed stream combinations.
* If the application creates a session with a stream combination not in the guaranteed
* list, or with mixed DEFAULT and non-DEFAULT use cases within the same session,
* the camera device may ignore some stream use cases due to hardware constraints
@@ -8214,6 +8247,44 @@
*/
ACAMERA_CONTROL_AE_MODE_ON_EXTERNAL_FLASH = 5,
+ /**
+ * <p>Like 'ON' but applies additional brightness boost in low light scenes.</p>
+ * <p>When the scene lighting conditions are within the range defined by
+ * ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE this mode will apply additional
+ * brightness boost.</p>
+ * <p>This mode will automatically adjust the intensity of low light boost applied
+ * according to the scene lighting conditions. A darker scene will receive more boost
+ * while a brighter scene will receive less boost.</p>
+ * <p>This mode can ignore the set target frame rate to allow more light to be captured
+ * which can result in choppier motion. The frame rate can extend to lower than the
+ * ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES but will not go below 10 FPS. This mode
+ * can also increase the sensor sensitivity gain which can result in increased luma
+ * and chroma noise. The sensor sensitivity gain can extend to higher values beyond
+ * ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE. This mode may also apply additional
+ * processing to recover details in dark and bright areas of the image,and noise
+ * reduction at high sensitivity gain settings to manage the trade-off between light
+ * sensitivity and capture noise.</p>
+ * <p>This mode is restricted to two output surfaces. One output surface type can either
+ * be SurfaceView or TextureView. Another output surface type can either be MediaCodec
+ * or MediaRecorder. This mode cannot be used with a target FPS range higher than 30
+ * FPS.</p>
+ * <p>If the session configuration is not supported, the AE mode reported in the
+ * CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
+ * <p>The application can observe the CapturerResult field
+ * ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE to determine when low light boost is 'ACTIVE' or
+ * 'INACTIVE'.</p>
+ * <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
+ * upper bound lux value defined by ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE.
+ * This mode will be 'INACTIVE' once the scene lighting condition is greater than the
+ * upper bound lux value defined by ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE.</p>
+ *
+ * @see ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+ * @see ACAMERA_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE
+ * @see ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE
+ * @see ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE
+ */
+ ACAMERA_CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY = 6,
+
} acamera_metadata_enum_android_control_ae_mode_t;
// ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER
@@ -9215,6 +9286,20 @@
} acamera_metadata_enum_android_control_autoframing_state_t;
+// ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE
+typedef enum acamera_metadata_enum_acamera_control_low_light_boost_state {
+ /**
+ * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled but not applied.</p>
+ */
+ ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE_INACTIVE = 0,
+
+ /**
+ * <p>The AE mode 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY' is enabled and applied.</p>
+ */
+ ACAMERA_CONTROL_LOW_LIGHT_BOOST_STATE_ACTIVE = 1,
+
+} acamera_metadata_enum_android_control_low_light_boost_state_t;
+
// ACAMERA_EDGE_MODE
@@ -10090,8 +10175,8 @@
* </ul>
* <p><a href="https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#SCALER_AVAILABLE_STREAM_USE_CASES">CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES</a>
* lists all of the supported stream use cases.</p>
- * <p>Refer to
- * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice.html#stream-use-case-capability-additional-guaranteed-configurations">CameraDevice#stream-use-case-capability-additional-guaranteed-configurations</a>
+ * <p>Refer to the
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#stream-use-case-capability-additional-guaranteed-configurations">guideline</a>
* for the mandatory stream combinations involving stream use cases, which can also be
* queried via <a href="https://developer.android.com/reference/android/hardware/camera2/params/MandatoryStreamCombination.html">MandatoryStreamCombination</a>.</p>
*/
@@ -10952,9 +11037,9 @@
/**
* <p>This camera device does not have enough capabilities to qualify as a <code>FULL</code> device or
* better.</p>
- * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code> tables in the
- * {@link ACameraDevice_createCaptureSession }
- * documentation are guaranteed to be supported.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code> and <code>LIMITED</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#limited-level-additional-guaranteed-configurations">tables</a>
+ * in the documentation are guaranteed to be supported.</p>
* <p>All <code>LIMITED</code> devices support the <code>BACKWARDS_COMPATIBLE</code> capability, indicating basic
* support for color image capture. The only exception is that the device may
* alternatively support only the <code>DEPTH_OUTPUT</code> capability, if it can only output depth
@@ -10979,9 +11064,9 @@
/**
* <p>This camera device is capable of supporting advanced imaging applications.</p>
- * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code> tables in the
- * {@link ACameraDevice_createCaptureSession }
- * documentation are guaranteed to be supported.</p>
+ * <p>The stream configurations listed in the <code>FULL</code>, <code>LEGACY</code> and <code>LIMITED</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#full-level-additional-guaranteed-configurations">tables</a>
+ * in the documentation are guaranteed to be supported.</p>
* <p>A <code>FULL</code> device will support below capabilities:</p>
* <ul>
* <li><code>BURST_CAPTURE</code> capability (ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains
@@ -11008,9 +11093,9 @@
/**
* <p>This camera device is running in backward compatibility mode.</p>
- * <p>Only the stream configurations listed in the <code>LEGACY</code> table in the
- * {@link ACameraDevice_createCaptureSession }
- * documentation are supported.</p>
+ * <p>Only the stream configurations listed in the <code>LEGACY</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#legacy-level-guaranteed-configurations">table</a>
+ * in the documentation are supported.</p>
* <p>A <code>LEGACY</code> device does not support per-frame control, manual sensor control, manual
* post-processing, arbitrary cropping regions, and has relaxed performance constraints.
* No additional capabilities beyond <code>BACKWARD_COMPATIBLE</code> will ever be listed by a
@@ -11032,9 +11117,9 @@
* <p>This camera device is capable of YUV reprocessing and RAW data capture, in addition to
* FULL-level capabilities.</p>
* <p>The stream configurations listed in the <code>LEVEL_3</code>, <code>RAW</code>, <code>FULL</code>, <code>LEGACY</code> and
- * <code>LIMITED</code> tables in the
- * {@link ACameraDevice_createCaptureSession }
- * documentation are guaranteed to be supported.</p>
+ * <code>LIMITED</code>
+ * <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#level-3-additional-guaranteed-configurations">tables</a>
+ * in the documentation are guaranteed to be supported.</p>
* <p>The following additional capabilities are guaranteed to be supported:</p>
* <ul>
* <li><code>YUV_REPROCESSING</code> capability (ACAMERA_REQUEST_AVAILABLE_CAPABILITIES contains
diff --git a/media/audioaidlconversion/AidlConversionCppNdk.cpp b/media/audioaidlconversion/AidlConversionCppNdk.cpp
index 60b814e..1a6c7f1 100644
--- a/media/audioaidlconversion/AidlConversionCppNdk.cpp
+++ b/media/audioaidlconversion/AidlConversionCppNdk.cpp
@@ -28,7 +28,6 @@
#include "media/AidlConversionCppNdk.h"
-#include <media/ShmemCompat.h>
#include <media/stagefright/foundation/MediaDefs.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/media/audioaidlconversion/Android.bp b/media/audioaidlconversion/Android.bp
index d3a5755..07c59c7 100644
--- a/media/audioaidlconversion/Android.bp
+++ b/media/audioaidlconversion/Android.bp
@@ -56,6 +56,19 @@
}
cc_defaults {
+ name: "audio_aidl_conversion_common_default_cpp",
+ shared_libs: [
+ "libbinder",
+ "libshmemcompat",
+ "shared-file-region-aidl-cpp",
+ "framework-permission-aidl-cpp",
+ ],
+ export_shared_lib_headers: [
+ "shared-file-region-aidl-cpp",
+ ],
+}
+
+cc_defaults {
name: "audio_aidl_conversion_common_default",
export_include_dirs: ["include"],
host_supported: true,
@@ -67,17 +80,12 @@
],
shared_libs: [
"libbase",
- "libbinder",
"liblog",
- "libshmemcompat",
"libstagefright_foundation",
"libutils",
- "shared-file-region-aidl-cpp",
- "framework-permission-aidl-cpp",
],
export_shared_lib_headers: [
"libbase",
- "shared-file-region-aidl-cpp",
],
cflags: [
"-Wall",
@@ -113,6 +121,7 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "audio_aidl_conversion_common_default_cpp",
"latest_android_media_audio_common_types_cpp_export_shared",
],
min_sdk_version: "29",
@@ -223,6 +232,7 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "audio_aidl_conversion_common_default_cpp",
"audio_aidl_conversion_common_util_default",
"latest_android_media_audio_common_types_cpp_shared",
"latest_android_media_audio_common_types_ndk_shared",
diff --git a/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h b/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
index 656d76a..53f529e 100644
--- a/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
+++ b/media/audioaidlconversion/include/media/AidlConversionUtil-impl.h
@@ -30,7 +30,6 @@
#include <utility>
#include <android-base/expected.h>
-#include <binder/Status.h>
#if defined(BACKEND_NDK_IMPL)
#include <android/binder_auto_utils.h>
@@ -40,6 +39,7 @@
namespace aidl {
#else
#include <binder/Enums.h>
+#include <binder/Status.h>
#endif // BACKEND_NDK_IMPL
namespace android {
@@ -374,6 +374,30 @@
* Note: for EX_TRANSACTION_FAILED and EX_SERVICE_SPECIFIC a more detailed error code
* can be found from transactionError() or serviceSpecificErrorCode().
*/
+#if defined(BACKEND_NDK_IMPL)
+static inline ::android::status_t statusTFromExceptionCode(binder_exception_t exception) {
+ switch (exception) {
+ case EX_NONE:
+ return ::android::OK;
+ case EX_SECURITY: // Java SecurityException, rethrows locally in Java
+ return ::android::PERMISSION_DENIED;
+ case EX_BAD_PARCELABLE: // Java BadParcelableException, rethrows in Java
+ case EX_ILLEGAL_ARGUMENT: // Java IllegalArgumentException, rethrows in Java
+ case EX_NULL_POINTER: // Java NullPointerException, rethrows in Java
+ return ::android::BAD_VALUE;
+ case EX_ILLEGAL_STATE: // Java IllegalStateException, rethrows in Java
+ case EX_UNSUPPORTED_OPERATION: // Java UnsupportedOperationException, rethrows
+ return ::android::INVALID_OPERATION;
+ case EX_PARCELABLE: // Java bootclass loader (not standard exception), rethrows
+ case EX_NETWORK_MAIN_THREAD: // Java NetworkOnMainThreadException, rethrows
+ case EX_TRANSACTION_FAILED: // Native - see error code
+ case EX_SERVICE_SPECIFIC: // Java ServiceSpecificException,
+ // rethrows in Java with integer error code
+ return ::android::UNKNOWN_ERROR;
+ }
+ return ::android::UNKNOWN_ERROR;
+}
+#else
static inline ::android::status_t statusTFromExceptionCode(int32_t exceptionCode) {
using namespace ::android::binder;
switch (exceptionCode) {
@@ -398,6 +422,7 @@
}
return ::android::UNKNOWN_ERROR;
}
+#endif // BACKEND_NDK_IMPL
/**
* Return the equivalent Android ::android::status_t from a binder status.
@@ -410,6 +435,7 @@
*
* return_type method(type0 param0, ...)
*/
+#if !defined(BACKEND_NDK_IMPL)
static inline ::android::status_t statusTFromBinderStatus(const ::android::binder::Status &status) {
return status.isOk() ? ::android::OK // check ::android::OK,
: status.serviceSpecificErrorCode() // service-side error, not standard Java exception
@@ -418,6 +444,7 @@
?: statusTFromExceptionCode(status.exceptionCode()); // a service-side error with a
// standard Java exception (fromExceptionCode)
}
+#endif
#if defined(BACKEND_NDK_IMPL)
static inline ::android::status_t statusTFromBinderStatus(const ::ndk::ScopedAStatus &status) {
@@ -443,6 +470,7 @@
* This is used for methods not returning an explicit status_t,
* where Java callers expect an exception, not an integer return value.
*/
+#if !defined(BACKEND_NDK_IMPL)
static inline ::android::binder::Status binderStatusFromStatusT(
::android::status_t status, const char *optionalMessage = nullptr) {
const char * const emptyIfNull = optionalMessage == nullptr ? "" : optionalMessage;
@@ -470,6 +498,7 @@
// throw a ServiceSpecificException.
return Status::fromServiceSpecificError(status, emptyIfNull);
}
+#endif
} // namespace aidl_utils
diff --git a/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp b/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
index 60727b4..f78243e 100644
--- a/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
+++ b/media/audioaidlconversion/tests/audio_aidl_ndk_conversion_tests.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+#define LOG_TAG "AidlConversionNdkTests"
#include <iostream>
#include <type_traits>
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
index ce9fc39..c7c04c5 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/audio/VtsHalMediaC2V1_0TargetAudioDecTest.cpp
@@ -90,8 +90,8 @@
std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(
- mLinearAllocator, mBlockPoolId++, getBufferPoolVer());
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++,
+ getBufferPoolVer());
ASSERT_NE(mLinearPool, nullptr);
std::vector<std::unique_ptr<C2Param>> queried;
@@ -336,7 +336,9 @@
ASSERT_TRUE(false) << "Wait for generating C2Work exceeded timeout";
}
int64_t timestamp = (*Info)[frameID].timestamp;
- flags = ((*Info)[frameID].flags == FLAG_CONFIG_DATA) ? C2FrameData::FLAG_CODEC_CONFIG : 0;
+ flags = ((*Info)[frameID].vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME))
+ ? C2FrameData::FLAG_CODEC_CONFIG
+ : 0;
if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
flags |= C2FrameData::FLAG_END_OF_STREAM;
@@ -531,14 +533,10 @@
// request EOS for thumbnail
// signal EOS flag with last frame
- size_t i = -1;
- uint32_t flags;
- do {
- i++;
- flags = 0;
- if (Info[i].flags) flags = 1u << (Info[i].flags - 1);
-
- } while (!(flags & SYNC_FRAME));
+ size_t i;
+ for (i = 0; i < Info.size(); i++) {
+ if (Info[i].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) break;
+ }
std::ifstream eleStream;
eleStream.open(mInputFile, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
@@ -643,14 +641,11 @@
mFlushedIndices.clear();
int index = numFramesFlushed;
bool keyFrame = false;
- uint32_t flags = 0;
while (index < (int)Info.size()) {
- if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
- if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+ if (Info[index].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
keyFrame = true;
break;
}
- flags = 0;
eleStream.ignore(Info[index].bytesCount);
index++;
}
@@ -684,24 +679,24 @@
int bytesCount = 0;
uint32_t frameId = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
bool codecConfig = false;
// This test introduces empty CSD after every 20th frame
// and empty input frames at an interval of 5 frames.
while (1) {
if (!(frameId % 5)) {
- if (!(frameId % 20))
- flags = 32;
- else
- flags = 0;
+ vtsFlags = !(frameId % 20) ? (1 << VTS_BIT_FLAG_CSD_FRAME) : 0;
bytesCount = 0;
} else {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
eleInfo >> timestamp;
- codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
}
- Info.push_back({bytesCount, flags, timestamp});
+ Info.push_back({bytesCount, vtsFlags, timestamp});
frameId++;
}
eleInfo.close();
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
index f36bc41..a72f7bd 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp
@@ -233,18 +233,24 @@
int32_t numCsds = 0;
int32_t bytesCount = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
while (1) {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ if (vtsFlags == 0xFF) {
+ ALOGE("unrecognized flag entry in info file %s", info.c_str());
+ return -1;
+ }
eleInfo >> timestamp;
- bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ bool codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0 ;
if (codecConfig) numCsds++;
- bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+ bool nonDisplayFrame = (vtsFlags & (1 << VTS_BIT_FLAG_NO_SHOW_FRAME)) != 0;
if (timestampDevTest && !codecConfig && !nonDisplayFrame) {
timestampUslist->push_back(timestamp);
}
- frameInfo->push_back({bytesCount, flags, timestamp});
+ frameInfo->push_back({bytesCount, vtsFlags, timestamp});
}
ALOGV("numCsds : %d", numCsds);
eleInfo.close();
@@ -273,3 +279,11 @@
ASSERT_EQ(flushedIndices.empty(), true);
flushedWork.clear();
}
+
+int mapInfoFlagstoVtsFlags(int infoFlags) {
+ if (infoFlags == 0) return 0;
+ else if (infoFlags == 0x1) return (1 << VTS_BIT_FLAG_SYNC_FRAME);
+ else if (infoFlags == 0x10) return (1 << VTS_BIT_FLAG_NO_SHOW_FRAME);
+ else if (infoFlags == 0x20) return (1 << VTS_BIT_FLAG_CSD_FRAME);
+ return 0xFF;
+}
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
index 48e80a4..eda7b99 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
+++ b/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.h
@@ -29,9 +29,6 @@
#include <chrono>
#include <fstream>
-#define FLAG_NON_DISPLAY_FRAME (1 << 4)
-#define FLAG_CONFIG_DATA (1 << 5)
-
#define MAX_RETRY 20
#define TIME_OUT 400ms
#define MAX_INPUT_BUFFERS 8
@@ -53,9 +50,15 @@
// Component name prefix
extern std::string sComponentNamePrefix;
+enum c2_vts_flags_t {
+ VTS_BIT_FLAG_SYNC_FRAME = 1,
+ VTS_BIT_FLAG_NO_SHOW_FRAME = 2,
+ VTS_BIT_FLAG_CSD_FRAME = 3,
+};
+
struct FrameInfo {
int bytesCount;
- uint32_t flags;
+ uint32_t vtsFlags;
int64_t timestamp;
};
@@ -165,4 +168,7 @@
void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
std::list<std::unique_ptr<C2Work>>& workQueue,
std::list<uint64_t>& flushedIndices, std::mutex& queueLock);
+
+int mapInfoFlagstoVtsFlags(int infoFlags);
+
#endif // MEDIA_C2_HIDL_TEST_COMMON_H
diff --git a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
index 2cf0d6e..fdb28f4 100644
--- a/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
+++ b/media/codec2/hal/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoDecTest.cpp
@@ -120,8 +120,8 @@
std::shared_ptr<C2AllocatorStore> store = android::GetCodec2PlatformAllocatorStore();
CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator), C2_OK);
- mLinearPool = std::make_shared<C2PooledBlockPool>(
- mLinearAllocator, mBlockPoolId++, getBufferPoolVer());
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, mBlockPoolId++,
+ getBufferPoolVer());
ASSERT_NE(mLinearPool, nullptr);
std::vector<std::unique_ptr<C2Param>> queried;
@@ -463,7 +463,9 @@
}
int64_t timestamp = (*Info)[frameID].timestamp;
- flags = ((*Info)[frameID].flags == FLAG_CONFIG_DATA) ? C2FrameData::FLAG_CODEC_CONFIG : 0;
+ flags = ((*Info)[frameID].vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME))
+ ? C2FrameData::FLAG_CODEC_CONFIG
+ : 0;
if (signalEOS && ((frameID == (int)Info->size() - 1) || (frameID == (offset + range - 1))))
flags |= C2FrameData::FLAG_END_OF_STREAM;
@@ -711,17 +713,19 @@
ASSERT_EQ(eleInfo.is_open(), true) << mInputFile << " - file not found";
int bytesCount = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
uint32_t timestampMax = 0;
while (1) {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
eleInfo >> timestamp;
timestamp += timestampOffset;
- Info.push_back({bytesCount, flags, timestamp});
- bool codecConfig =
- flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
- bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
+ Info.push_back({bytesCount, vtsFlags, timestamp});
+ bool codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
+ bool nonDisplayFrame = (vtsFlags & (1 << VTS_BIT_FLAG_NO_SHOW_FRAME)) != 0;
{
ULock l(mQueueLock);
@@ -795,20 +799,15 @@
int32_t numCsds = populateInfoVector(mInfoFile, &Info, mTimestampDevTest, &mTimestampUslist);
ASSERT_GE(numCsds, 0) << "Error in parsing input info file: " << mInfoFile;
- uint32_t flags = 0;
for (size_t i = 0; i < MAX_ITERATIONS; i++) {
ASSERT_EQ(mComponent->start(), C2_OK);
// request EOS for thumbnail
// signal EOS flag with last frame
size_t j = -1;
- do {
- j++;
- flags = 0;
- if (Info[j].flags) flags = 1u << (Info[j].flags - 1);
-
- } while (!(flags & SYNC_FRAME));
-
+ for (j = 0; j < Info.size(); j++) {
+ if (Info[j].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) break;
+ }
std::ifstream eleStream;
eleStream.open(mInputFile, std::ifstream::binary);
ASSERT_EQ(eleStream.is_open(), true);
@@ -908,14 +907,11 @@
// Seek to next key frame and start decoding till the end
int index = numFramesFlushed;
bool keyFrame = false;
- uint32_t flags = 0;
while (index < (int)Info.size()) {
- if (Info[index].flags) flags = 1u << (Info[index].flags - 1);
- if ((flags & SYNC_FRAME) == SYNC_FRAME) {
+ if (Info[index].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
keyFrame = true;
break;
}
- flags = 0;
eleStream.ignore(Info[index].bytesCount);
index++;
}
@@ -949,24 +945,24 @@
int bytesCount = 0;
uint32_t frameId = 0;
uint32_t flags = 0;
+ uint32_t vtsFlags = 0;
uint32_t timestamp = 0;
bool codecConfig = false;
// This test introduces empty CSD after every 20th frame
// and empty input frames at an interval of 5 frames.
while (1) {
if (!(frameId % 5)) {
- if (!(frameId % 20))
- flags = 32;
- else
- flags = 0;
+ vtsFlags = !(frameId % 20) ? (1 << VTS_BIT_FLAG_CSD_FRAME) : 0;
bytesCount = 0;
} else {
if (!(eleInfo >> bytesCount)) break;
eleInfo >> flags;
+ vtsFlags = mapInfoFlagstoVtsFlags(flags);
+ ASSERT_NE(vtsFlags, 0xFF) << "unrecognized flag entry in info file: " << mInfoFile;
eleInfo >> timestamp;
- codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+ codecConfig = (vtsFlags & (1 << VTS_BIT_FLAG_CSD_FRAME)) != 0;
}
- Info.push_back({bytesCount, flags, timestamp});
+ Info.push_back({bytesCount, vtsFlags, timestamp});
frameId++;
}
eleInfo.close();
@@ -1046,12 +1042,9 @@
}
int offset = framesToDecode;
- uint32_t flags = 0;
while (1) {
while (offset < (int)Info.size()) {
- flags = 0;
- if (Info[offset].flags) flags = 1u << (Info[offset].flags - 1);
- if (flags & SYNC_FRAME) {
+ if (Info[offset].vtsFlags & (1 << VTS_BIT_FLAG_SYNC_FRAME)) {
keyFrame = true;
break;
}
diff --git a/media/codec2/vndk/include/C2SurfaceSyncObj.h b/media/codec2/vndk/include/C2SurfaceSyncObj.h
index b193b4a..7c1a405 100644
--- a/media/codec2/vndk/include/C2SurfaceSyncObj.h
+++ b/media/codec2/vndk/include/C2SurfaceSyncObj.h
@@ -117,6 +117,16 @@
*/
void notifyAll();
+ /**
+ * Invalide current sync variables on the death of the other process.
+ */
+ void invalidate();
+
+ /**
+ * If a dead process holds the lock, clear the lock.
+ */
+ void clearLockIfNecessary();
+
C2SyncVariables() {}
private:
@@ -135,6 +145,11 @@
*/
int wait();
+ /**
+ * try lock for the specified duration.
+ */
+ bool tryLockFor(size_t ms);
+
std::atomic<uint32_t> mLock;
std::atomic<uint32_t> mCond;
int32_t mMaxDequeueCount;
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 62b0ab5..48157c8 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -396,6 +396,12 @@
if (c2Fence) {
*c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
}
+ if (mInvalidated) {
+ if (c2Fence) {
+ *c2Fence = C2Fence();
+ }
+ return C2_BAD_STATE;
+ }
return C2_BLOCKING;
}
if (syncVar->getSyncStatusLocked() != C2SyncVariables::STATUS_ACTIVE) {
@@ -404,6 +410,12 @@
if (c2Fence) {
*c2Fence = _C2FenceFactory::CreateSurfaceFence(mSyncMem, waitId);
}
+ if (mInvalidated) {
+ if (c2Fence) {
+ *c2Fence = C2Fence();
+ }
+ return C2_BAD_STATE;
+ }
return C2_BLOCKING;
}
syncVar->notifyDequeuedLocked();
@@ -690,7 +702,6 @@
}
}
int migrated = 0;
- std::shared_ptr<C2SurfaceSyncMemory> oldMem;
// poolDatas dtor should not be called during lock is held.
std::shared_ptr<C2BufferQueueBlockPoolData>
poolDatas[NUM_BUFFER_SLOTS];
@@ -708,8 +719,22 @@
mGeneration = 0;
ALOGD("configuring null producer: igbp_information(%d)", bqInformation);
}
- oldMem = mSyncMem; // preven destruction while locked.
- mSyncMem = c2SyncMem;
+ if (mInvalidated) {
+ return;
+ }
+ {
+ std::unique_lock<std::mutex> memLock(mSyncMemMutex);
+ mOldMem = mSyncMem; // prevent destruction while locked.
+ // The waiters from the old memory will be
+ // woken up by the client after this
+ // configuration from HAL being finished.
+ // But we will keep this in case of the
+ // client being dead in between.
+ // In the case the death listener will wake
+ // up the wiators for the old memory using
+ // mOldMem here.
+ mSyncMem = c2SyncMem;
+ }
C2SyncVariables *syncVar = mSyncMem ? mSyncMem->mem() : nullptr;
if (syncVar) {
syncVar->lock();
@@ -736,6 +761,9 @@
// is no longer valid.
mIgbpValidityToken = std::make_shared<int>(0);
}
+ if (mInvalidated) {
+ mIgbpValidityToken = std::make_shared<int>(0);
+ }
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
mBuffers[i] = buffers[i];
mPoolDatas[i] = poolDatas[i];
@@ -754,8 +782,33 @@
}
void invalidate() {
- mInvalidated = true;
+ std::shared_ptr<C2SurfaceSyncMemory> syncMem;
+ std::shared_ptr<C2SurfaceSyncMemory> oldMem;
+ {
+ std::unique_lock<std::mutex> l(mSyncMemMutex);
+ bool old = mInvalidated.exchange(true);
+ if (old) {
+ return;
+ }
+ syncMem = mSyncMem;
+ oldMem = mOldMem;
+ }
mIgbpValidityToken.reset();
+ C2SyncVariables *syncVar = syncMem ? syncMem->mem(): nullptr;
+ if (syncVar) {
+ syncVar->invalidate();
+ }
+ C2SyncVariables *oldVar = oldMem ? oldMem->mem(): nullptr;
+ if (oldVar) {
+ oldVar->invalidate();
+ }
+ // invalidate pending lock from a dead process if any
+ if (syncVar) {
+ syncVar->clearLockIfNecessary();
+ }
+ if (oldVar) {
+ oldVar->clearLockIfNecessary();
+ }
}
private:
@@ -780,7 +833,9 @@
sp<GraphicBuffer> mBuffers[NUM_BUFFER_SLOTS];
std::weak_ptr<C2BufferQueueBlockPoolData> mPoolDatas[NUM_BUFFER_SLOTS];
+ std::mutex mSyncMemMutex;
std::shared_ptr<C2SurfaceSyncMemory> mSyncMem;
+ std::shared_ptr<C2SurfaceSyncMemory> mOldMem;
// IGBP invalidation notification token.
// The buffers(C2BufferQueueBlockPoolData) has the reference to the IGBP where
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
index d8c2292..41d16b5 100644
--- a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -26,6 +26,33 @@
#include <chrono>
#include <C2SurfaceSyncObj.h>
+namespace {
+static inline void timespec_add_ms(timespec& ts, size_t ms) {
+ constexpr int kNanoSecondsPerSec = 1000000000;
+ ts.tv_sec += ms / 1000;
+ ts.tv_nsec += (ms % 1000) * 1000000;
+ if (ts.tv_nsec >= kNanoSecondsPerSec) {
+ ts.tv_sec++;
+ ts.tv_nsec -= kNanoSecondsPerSec;
+ }
+}
+
+/*
+ * lhs < rhs: return <0
+ * lhs == rhs: return 0
+ * lhs > rhs: return >0
+ */
+static inline int timespec_compare(const timespec& lhs, const timespec& rhs) {
+ if (lhs.tv_sec < rhs.tv_sec) {
+ return -1;
+ }
+ if (lhs.tv_sec > rhs.tv_sec) {
+ return 1;
+ }
+ return lhs.tv_nsec - rhs.tv_nsec;
+}
+}
+
const native_handle_t C2SurfaceSyncMemory::HandleSyncMem::cHeader = {
C2SurfaceSyncMemory::HandleSyncMem::version,
C2SurfaceSyncMemory::HandleSyncMem::numFds,
@@ -284,6 +311,26 @@
this->unlock();
}
+void C2SyncVariables::invalidate() {
+ mCond++;
+ (void) syscall(__NR_futex, &mCond, FUTEX_REQUEUE, INT_MAX, (void *)INT_MAX, &mLock, 0);
+}
+
+void C2SyncVariables::clearLockIfNecessary() {
+ // Note: After waiting for 30ms without acquiring the lock,
+ // we will consider the lock is dangling.
+ // Since the lock duration is very brief to manage the counter,
+ // waiting for 30ms should be more than enough.
+ constexpr size_t kTestLockDurationMs = 30;
+
+ bool locked = tryLockFor(kTestLockDurationMs);
+ unlock();
+
+ if (!locked) {
+ ALOGW("A dead process might be holding the lock");
+ }
+}
+
int C2SyncVariables::signal() {
mCond++;
@@ -308,3 +355,35 @@
}
return 0;
}
+
+bool C2SyncVariables::tryLockFor(size_t ms) {
+ uint32_t old = FUTEX_UNLOCKED;
+
+ if (mLock.compare_exchange_strong(old, FUTEX_LOCKED_UNCONTENDED)) {
+ return true;
+ }
+
+ if (old == FUTEX_LOCKED_UNCONTENDED) {
+ old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+ }
+
+ struct timespec wait{
+ static_cast<time_t>(ms / 1000),
+ static_cast<long>((ms % 1000) * 1000000)};
+ struct timespec end;
+ clock_gettime(CLOCK_REALTIME, &end);
+ timespec_add_ms(end, ms);
+
+ while (old != FUTEX_UNLOCKED) { // case of EINTR being returned;
+ (void)syscall(__NR_futex, &mLock, FUTEX_WAIT, FUTEX_LOCKED_CONTENDED, &wait, NULL, 0);
+ old = mLock.exchange(FUTEX_LOCKED_CONTENDED);
+
+ struct timespec now;
+ clock_gettime(CLOCK_REALTIME, &now);
+ if (timespec_compare(now, end) >= 0) {
+ break;
+ }
+ }
+
+ return old == FUTEX_UNLOCKED;
+}
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 886603d..fcb376c 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -41,10 +41,10 @@
"modernize-use-emplace",
"modernize-use-equals-default",
"modernize-use-equals-delete",
- // "modernize-use-nodiscard", // found in aidl generated files
+ "modernize-use-nodiscard",
"modernize-use-noexcept",
"modernize-use-nullptr",
- // "modernize-use-override", // found in aidl generated files
+ "modernize-use-override",
// "modernize-use-trailing-return-type", // not necessarily more readable
"modernize-use-transparent-functors",
"modernize-use-uncaught-exceptions",
@@ -225,10 +225,12 @@
"flowgraph/SinkI16.cpp",
"flowgraph/SinkI24.cpp",
"flowgraph/SinkI32.cpp",
+ "flowgraph/SinkI8_24.cpp",
"flowgraph/SourceFloat.cpp",
"flowgraph/SourceI16.cpp",
"flowgraph/SourceI24.cpp",
"flowgraph/SourceI32.cpp",
+ "flowgraph/SourceI8_24.cpp",
"flowgraph/resampler/IntegerRatio.cpp",
"flowgraph/resampler/LinearResampler.cpp",
"flowgraph/resampler/MultiChannelResampler.cpp",
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index 722dd14..12cc42e 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -53,7 +53,7 @@
int32_t addFileDescriptor(const android::base::unique_fd& fd, int32_t sizeInBytes);
/**
- * Close current data file descriptor. The duplicated file descriptor will be close.
+ * Close current data file descriptor. The duplicated file descriptor will be closed.
*/
void closeDataFileDescriptor();
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index b7e0ae6..14e2007 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -30,10 +30,12 @@
#include <flowgraph/SinkI16.h>
#include <flowgraph/SinkI24.h>
#include <flowgraph/SinkI32.h>
+#include <flowgraph/SinkI8_24.h>
#include <flowgraph/SourceFloat.h>
#include <flowgraph/SourceI16.h>
#include <flowgraph/SourceI24.h>
#include <flowgraph/SourceI32.h>
+#include <flowgraph/SourceI8_24.h>
using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
@@ -68,6 +70,9 @@
case AUDIO_FORMAT_PCM_32_BIT:
mSource = std::make_unique<SourceI32>(sourceChannelCount);
break;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ mSource = std::make_unique<SourceI8_24>(sourceChannelCount);
+ break;
default:
ALOGE("%s() Unsupported source format = %d", __func__, sourceFormat);
return AAUDIO_ERROR_UNIMPLEMENTED;
@@ -139,6 +144,9 @@
case AUDIO_FORMAT_PCM_32_BIT:
mSink = std::make_unique<SinkI32>(sinkChannelCount);
break;
+ case AUDIO_FORMAT_PCM_8_24_BIT:
+ mSink = std::make_unique<SinkI8_24>(sinkChannelCount);
+ break;
default:
ALOGE("%s() Unsupported sink format = %d", __func__, sinkFormat);
return AAUDIO_ERROR_UNIMPLEMENTED;
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index 2c23e1d..b117572 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -107,7 +107,7 @@
*/
void eraseDataMemory();
- void freeDataQueue();
+ void freeDataQueue() { mDataQueue.reset(); }
void dump() const;
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index f431da8..52925d9 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -399,6 +399,12 @@
uint8_t buffer[getDeviceBufferCapacity() * getBytesPerFrame()];
android::fifo_frames_t fullFramesAvailable = mAudioEndpoint->read(buffer,
getDeviceBufferCapacity());
+ // Before releasing the data queue, update the frames read and written.
+ getFramesRead();
+ getFramesWritten();
+ // Call freeDataQueue() here because the following call to
+ // closeDataFileDescriptor() will invalidate the pointers used by the data queue.
+ mAudioEndpoint->freeDataQueue();
mEndPointParcelable.closeDataFileDescriptor();
aaudio_result_t result = mServiceInterface.exitStandby(
mServiceStreamHandleInfo, endpointParcelable);
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index d9b75da..68c9156 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -273,7 +273,8 @@
int64_t AudioStreamInternalCapture::getFramesRead() {
if (mAudioEndpoint) {
- mLastFramesRead = mAudioEndpoint->getDataReadCounter() + mFramesOffsetFromService;
+ mLastFramesRead = std::max(mLastFramesRead,
+ mAudioEndpoint->getDataReadCounter() + mFramesOffsetFromService);
}
return mLastFramesRead;
}
@@ -295,8 +296,10 @@
if ((result != mCallbackFrames)) {
ALOGE("callbackLoop: read() returned %d", result);
if (result >= 0) {
- // Only read some of the frames requested. Must have timed out.
- result = AAUDIO_ERROR_TIMEOUT;
+ // Only read some of the frames requested. The stream can be disconnected
+ // or timed out.
+ processCommands();
+ result = isDisconnected() ? AAUDIO_ERROR_DISCONNECTED : AAUDIO_ERROR_TIMEOUT;
}
maybeCallErrorCallback(result);
break;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 3badb0b..5bac2ca 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -329,8 +329,9 @@
int64_t AudioStreamInternalPlay::getFramesWritten() {
if (mAudioEndpoint) {
- mLastFramesWritten = mAudioEndpoint->getDataWriteCounter()
- + mFramesOffsetFromService;
+ mLastFramesWritten = std::max(
+ mLastFramesWritten,
+ mAudioEndpoint->getDataWriteCounter() + mFramesOffsetFromService);
}
return mLastFramesWritten;
}
@@ -353,8 +354,10 @@
result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
if ((result != mCallbackFrames)) {
if (result >= 0) {
- // Only wrote some of the frames requested. Must have timed out.
- result = AAUDIO_ERROR_TIMEOUT;
+ // Only wrote some of the frames requested. The stream can be disconnected
+ // or timed out.
+ processCommands();
+ result = isDisconnected() ? AAUDIO_ERROR_DISCONNECTED : AAUDIO_ERROR_TIMEOUT;
}
maybeCallErrorCallback(result);
break;
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 1db62f3..3e51575 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -61,6 +61,7 @@
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
+ case AUDIO_FORMAT_PCM_8_24_BIT:
case AUDIO_FORMAT_IEC61937:
break; // valid
default:
diff --git a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
index 5e90588..e277d6e 100644
--- a/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
+++ b/media/libaaudio/src/flowgraph/FlowgraphUtilities.h
@@ -17,6 +17,7 @@
#ifndef FLOWGRAPH_UTILITIES_H
#define FLOWGRAPH_UTILITIES_H
+#include <math.h>
#include <unistd.h>
using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
@@ -50,6 +51,20 @@
return f > 0 ? f + 0.5 : f - 0.5;
}
+/**
+ * Convert a single-precision floating point value to a Q0.23 integer value, stored in a
+ * 32 bit signed integer (technically stored as Q8.23, but clamped to Q0.23).
+ *
+ * Values outside the range [-1.0, 1.0) are properly clamped to -8388608 and 8388607,
+ * including -Inf and +Inf. NaN values are considered undefined, and behavior may change
+ * depending on hardware and future implementation of this function.
+ */
+static int32_t clamp24FromFloat(float f)
+{
+ static const float scale = 1 << 23;
+ return (int32_t) lroundf(fmaxf(fminf(f * scale, scale - 1.f), -scale));
+}
+
};
#endif // FLOWGRAPH_UTILITIES_H
diff --git a/media/libaaudio/src/flowgraph/SinkI8_24.cpp b/media/libaaudio/src/flowgraph/SinkI8_24.cpp
new file mode 100644
index 0000000..d5e4b80
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI8_24.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FlowGraphNode.h"
+#include "FlowgraphUtilities.h"
+#include "SinkI8_24.h"
+
+#if FLOWGRAPH_ANDROID_INTERNAL
+#include <audio_utils/primitives.h>
+#endif
+
+using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
+
+SinkI8_24::SinkI8_24(int32_t channelCount)
+ : FlowGraphSink(channelCount) {}
+
+int32_t SinkI8_24::read(void *data, int32_t numFrames) {
+ int32_t *intData = (int32_t *) data;
+ const int32_t channelCount = input.getSamplesPerFrame();
+
+ int32_t framesLeft = numFrames;
+ while (framesLeft > 0) {
+ // Run the graph and pull data through the input port.
+ int32_t framesRead = pullData(framesLeft);
+ if (framesRead <= 0) {
+ break;
+ }
+ const float *signal = input.getBuffer();
+ int32_t numSamples = framesRead * channelCount;
+#if FLOWGRAPH_ANDROID_INTERNAL
+ memcpy_to_q8_23_from_float_with_clamp(intData, signal, numSamples);
+ intData += numSamples;
+ signal += numSamples;
+#else
+ for (int i = 0; i < numSamples; i++) {
+ *intData++ = FlowgraphUtilities::clamp24FromFloat(*signal++);
+ }
+#endif
+ framesLeft -= framesRead;
+ }
+ return numFrames - framesLeft;
+}
diff --git a/media/libaaudio/src/flowgraph/SinkI8_24.h b/media/libaaudio/src/flowgraph/SinkI8_24.h
new file mode 100644
index 0000000..aa96918
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SinkI8_24.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_SINK_I8_24_H
+#define FLOWGRAPH_SINK_I8_24_H
+
+#include <stdint.h>
+
+#include "FlowGraphNode.h"
+
+namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph {
+
+class SinkI8_24 : public FlowGraphSink {
+public:
+ explicit SinkI8_24(int32_t channelCount);
+ ~SinkI8_24() override = default;
+
+ int32_t read(void *data, int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SinkI8_24";
+ }
+};
+
+} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */
+
+#endif //FLOWGRAPH_SINK_I8_24_H
diff --git a/media/libaaudio/src/flowgraph/SourceI8_24.cpp b/media/libaaudio/src/flowgraph/SourceI8_24.cpp
new file mode 100644
index 0000000..684446c
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI8_24.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <unistd.h>
+
+#include "FlowGraphNode.h"
+#include "SourceI8_24.h"
+
+#if FLOWGRAPH_ANDROID_INTERNAL
+#include <audio_utils/primitives.h>
+#endif
+
+using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
+
+SourceI8_24::SourceI8_24(int32_t channelCount)
+ : FlowGraphSourceBuffered(channelCount) {
+}
+
+int32_t SourceI8_24::onProcess(int32_t numFrames) {
+ float *floatData = output.getBuffer();
+ const int32_t channelCount = output.getSamplesPerFrame();
+
+ const int32_t framesLeft = mSizeInFrames - mFrameIndex;
+ const int32_t framesToProcess = std::min(numFrames, framesLeft);
+ const int32_t numSamples = framesToProcess * channelCount;
+
+ const int32_t *intBase = static_cast<const int32_t *>(mData);
+ const int32_t *intData = &intBase[mFrameIndex * channelCount];
+
+#if FLOWGRAPH_ANDROID_INTERNAL
+ memcpy_to_float_from_q8_23(floatData, intData, numSamples);
+#else
+ for (int i = 0; i < numSamples; i++) {
+ *floatData++ = *intData++ * kScale;
+ }
+#endif
+
+ mFrameIndex += framesToProcess;
+ return framesToProcess;
+}
diff --git a/media/libaaudio/src/flowgraph/SourceI8_24.h b/media/libaaudio/src/flowgraph/SourceI8_24.h
new file mode 100644
index 0000000..91c756c
--- /dev/null
+++ b/media/libaaudio/src/flowgraph/SourceI8_24.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FLOWGRAPH_SOURCE_I8_24_H
+#define FLOWGRAPH_SOURCE_I8_24_H
+
+#include <stdint.h>
+
+#include "FlowGraphNode.h"
+
+namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph {
+
+class SourceI8_24 : public FlowGraphSourceBuffered {
+public:
+ explicit SourceI8_24(int32_t channelCount);
+ ~SourceI8_24() override = default;
+
+ int32_t onProcess(int32_t numFrames) override;
+
+ const char *getName() override {
+ return "SourceI8_24";
+ }
+private:
+ static constexpr float kScale = 1.0 / (1UL << 23);
+};
+
+} /* namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph */
+
+#endif //FLOWGRAPH_SOURCE_I8_24_H
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 1129ced..d59afef 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -241,3 +241,10 @@
"libaaudio_internal",
],
}
+
+cc_binary {
+ name: "test_idle_disconnected_shared_stream",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_idle_disconnected_shared_stream.cpp"],
+ shared_libs: ["libaaudio"],
+}
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
index 7eb8b0d..f20a4bb 100644
--- a/media/libaaudio/tests/test_flowgraph.cpp
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -31,14 +31,16 @@
#include "flowgraph/Limiter.h"
#include "flowgraph/MonoBlend.h"
#include "flowgraph/MonoToMultiConverter.h"
-#include "flowgraph/SourceFloat.h"
#include "flowgraph/RampLinear.h"
#include "flowgraph/SinkFloat.h"
#include "flowgraph/SinkI16.h"
#include "flowgraph/SinkI24.h"
#include "flowgraph/SinkI32.h"
+#include "flowgraph/SinkI8_24.h"
+#include "flowgraph/SourceFloat.h"
#include "flowgraph/SourceI16.h"
#include "flowgraph/SourceI24.h"
+#include "flowgraph/SourceI8_24.h"
#include "flowgraph/resampler/IntegerRatio.h"
using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
@@ -52,6 +54,9 @@
PARAM_RESAMPLER_QUALITY
};
+constexpr int kInt24Min = 0xff800000;
+constexpr int kInt24Max = 0x007fffff;
+
constexpr int kBytesPerI24Packed = 3;
constexpr int kNumSamples = 8;
@@ -59,15 +64,19 @@
1.0f, 0.5f, -0.25f, -1.0f,
0.0f, 53.9f, -87.2f, -1.02f};
-// Corresponding PCM values as integers.
-constexpr std::array<int16_t, kNumSamples> kExpectedI16 = {
+// Corresponding PCM values as integers.
+constexpr std::array<int16_t, kNumSamples> kExpectedI16 = {
INT16_MAX, 1 << 14, INT16_MIN / 4, INT16_MIN,
0, INT16_MAX, INT16_MIN, INT16_MIN};
-constexpr std::array<int32_t, kNumSamples> kExpectedI32 = {
+constexpr std::array<int32_t, kNumSamples> kExpectedI32 = {
INT32_MAX, 1 << 30, INT32_MIN / 4, INT32_MIN,
0, INT32_MAX, INT32_MIN, INT32_MIN};
+constexpr std::array<int32_t, kNumSamples> kExpectedI8_24 = {
+ kInt24Max, 1 << 22, kInt24Min / 4, kInt24Min,
+ 0, kInt24Max, kInt24Min, kInt24Min};
+
// =================================== FLOAT to I16 ==============
// Simple test that tries to reproduce a Clang compiler bug.
@@ -215,6 +224,8 @@
EXPECT_EQ(input[0], output[1]);
EXPECT_EQ(input[1], output[2]);
EXPECT_EQ(input[1], output[3]);
+ EXPECT_EQ(input[2], output[4]);
+ EXPECT_EQ(input[2], output[5]);
}
TEST(test_flowgraph, module_ramp_linear) {
@@ -434,6 +445,70 @@
}
}
+// =================================== FLOAT to Q8.23 ==============
+__attribute__((noinline))
+static int32_t clamp24FromFloat(float f)
+{
+ static const float scale = 1 << 23;
+ return (int32_t) lroundf(fmaxf(fminf(f * scale, scale - 1.f), -scale));
+}
+
+void local_convert_float_to_i8_24(const float *input,
+ int32_t *output,
+ int count) {
+ for (int i = 0; i < count; i++) {
+ *output++ = clamp24FromFloat(*input++);
+ }
+}
+
+TEST(test_flowgraph, local_convert_float_to_i8_24) {
+ std::array<int32_t, kNumSamples> output;
+ // Convert audio signal using the function.
+ output.fill(777);
+ local_convert_float_to_i8_24(kInputFloat.data(), output.data(), kNumSamples);
+ for (int i = 0; i < kNumSamples; i++) {
+ EXPECT_EQ(kExpectedI8_24.at(i), output.at(i)) << ", i = " << i;
+ }
+}
+
+TEST(test_flowgraph, module_sinkI8_24) {
+ std::array<int32_t, kNumSamples + 10> output; // larger than input
+
+ SourceFloat sourceFloat{2};
+ SinkI8_24 sinkI8_24{2};
+
+ sourceFloat.setData(kInputFloat.data(), kNumSamples);
+ sourceFloat.output.connect(&sinkI8_24.input);
+
+ output.fill(777);
+ int32_t numRead = sinkI8_24.read(output.data(), output.size());
+ ASSERT_EQ(kNumSamples, numRead);
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_EQ(kExpectedI8_24.at(i), output.at(i)) << ", i = " << i;
+ }
+}
+
+TEST(test_flowgraph, module_sourceI8_24) {
+ static const int32_t input[] = {1 << 23, 1 << 22, -(1 << 21), -(1 << 23), 0, 1 << 25,
+ -(1 << 25)};
+ static const float expected[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 4.0f, -4.0f};
+ float output[100];
+
+ SourceI8_24 sourceI8_24{1};
+ SinkFloat sinkFloat{1};
+
+ int numSamples = std::size(input);
+
+ sourceI8_24.setData(input, numSamples);
+ sourceI8_24.output.connect(&sinkFloat.input);
+
+ int32_t numRead = sinkFloat.read(output, numSamples);
+ ASSERT_EQ(numSamples, numRead);
+ for (int i = 0; i < numRead; i++) {
+ EXPECT_EQ(expected[i], output[i]) << ", i = " << i;
+ }
+}
+
void checkSampleRateConversionVariedSizes(int32_t sourceSampleRate,
int32_t sinkSampleRate,
MultiChannelResampler::Quality resamplerQuality) {
diff --git a/media/libaaudio/tests/test_idle_disconnected_shared_stream.cpp b/media/libaaudio/tests/test_idle_disconnected_shared_stream.cpp
new file mode 100644
index 0000000..2e24b6b
--- /dev/null
+++ b/media/libaaudio/tests/test_idle_disconnected_shared_stream.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// When receive disconnect event, ignore it and leave the shared stream at OPEN
+// state. It should be possible to open another shared stream and start it.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+#include <aaudio/AAudioTesting.h>
+
+static constexpr unsigned int ONE_SECOND = 1e6;
+static constexpr unsigned int WAIT_TIME_MS = 10 * ONE_SECOND;
+#define MMAP_POLICY AAUDIO_POLICY_ALWAYS
+
+AAudioStream* openStream() {
+ AAudioStreamBuilder *aaudioBuilder = nullptr;
+ aaudio_result_t result = AAudio_createStreamBuilder(&aaudioBuilder);
+ if (result != AAUDIO_OK) {
+ printf("Failed to create stream builder, result=%d, %s\n",
+ result, AAudio_convertResultToText(result));
+ return nullptr;
+ }
+ AAudioStreamBuilder_setSharingMode(aaudioBuilder, AAUDIO_SHARING_MODE_SHARED);
+ AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStream* aaudioStream;
+ result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
+ if (result != AAUDIO_OK) {
+ printf("ERROR could not open AAudio stream, %d %s\n",
+ result, AAudio_convertResultToText(result));
+ }
+ AAudioStreamBuilder_delete(aaudioBuilder);
+ return aaudioStream;
+}
+
+aaudio_result_t testNoCloseSharedStreamAfterRoutingChanged(bool stopFirstStream) {
+ aaudio_result_t result = AAUDIO_OK;
+ printf("Please connect external device that supports MMAP playback, will wait 10 seconds\n");
+ usleep(WAIT_TIME_MS);
+
+ // Open first shared stream
+ printf("Open first shared stream\n");
+ AAudioStream* firstStream = openStream();
+ if (firstStream == nullptr) {
+ return 1;
+ }
+ result = AAudioStream_requestStart(firstStream);
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
+ if (stopFirstStream) {
+ printf("Stop first shared stream\n");
+ result = AAudioStream_requestStop(firstStream);
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+ printf("Wait to make sure the stream is stopped\n");
+ usleep(ONE_SECOND * 3);
+ }
+
+ printf("Please disconnect and reconnect the external device, will wait 10 second\n");
+ usleep(WAIT_TIME_MS);
+
+ // Open second stream after the first shared stream was reconnected
+ printf("Open second shared stream\n");
+ AAudioStream* secondStream = openStream();
+ if (secondStream == nullptr) {
+ result = 1;
+ goto exit;
+ }
+
+ // Starting second stream should be successful
+ printf("Start second shared stream\n");
+ result = AAudioStream_requestStart(secondStream);
+ if (result != AAUDIO_OK) {
+ printf("ERROR could not start second stream, %d %s\n",
+ result, AAudio_convertResultToText(result));
+ }
+
+exit:
+ // Close all streams
+ AAudioStream_close(firstStream);
+ AAudioStream_close(secondStream);
+ return result;
+}
+
+int main(int argc, char **argv) {
+ (void) argc; // unused
+ (void) argv; // unused
+
+ aaudio_policy_t originalPolicy = AAudio_getMMapPolicy();
+ AAudio_setMMapPolicy(MMAP_POLICY);
+
+ printf("Run first test. The first stream is started when routing changed.\n");
+ aaudio_result_t result = testNoCloseSharedStreamAfterRoutingChanged(false /*stopFirstStream*/);
+
+ if (result != AAUDIO_OK) {
+ goto exit;
+ }
+
+ printf("First test passed\n");
+ printf("----------------------------------------------------------------\n");
+ printf("Run second test. The first stream is stopped when routing changed.\n");
+ if (testNoCloseSharedStreamAfterRoutingChanged(true /*stopFirstStream*/) == AAUDIO_OK) {
+ printf("Second test passed\n");
+ }
+
+exit:
+ AAudio_setMMapPolicy(originalPolicy);
+
+ return result != AAUDIO_OK ? EXIT_FAILURE : EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index 658bf63..51a679b 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -217,6 +217,7 @@
],
defaults: [
"audio_aidl_conversion_common_default",
+ "audio_aidl_conversion_common_default_cpp",
"latest_android_media_audio_common_types_cpp_export_shared",
],
}
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index 6a6557c..86fa63f 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -451,6 +451,7 @@
}
*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.
::aidl::android::hardware::audio::core::IModule::OpenOutputStreamArguments args;
args.portConfigId = mixPortConfig.id;
const bool isOffload = isBitPositionFlagSet(
@@ -523,6 +524,7 @@
}
*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.
::aidl::android::hardware::audio::core::IModule::OpenInputStreamArguments args;
args.portConfigId = mixPortConfig.id;
RecordTrackMetadata aidlTrackMetadata{
@@ -707,8 +709,7 @@
*config, isInput, 0 /*portId*/));
AudioPortConfig portConfig;
std::lock_guard l(mLock);
- return mMapper.findOrCreatePortConfig(
- requestedPortConfig, std::set<int32_t>(), &portConfig);
+ return mMapper.setPortConfig(requestedPortConfig, std::set<int32_t>(), &portConfig);
}
MicrophoneInfoProvider::Info const* DeviceHalAidl::getMicrophoneInfo() {
@@ -780,7 +781,7 @@
Hal2AidlMapper::Cleanups cleanups(mMapperAccessor);
{
std::lock_guard l(mLock);
- RETURN_STATUS_IF_ERROR(mMapper.findOrCreatePortConfig(
+ RETURN_STATUS_IF_ERROR(mMapper.setPortConfig(
requestedPortConfig, {} /*destinationPortIds*/, &devicePortConfig, &cleanups));
}
auto aidlEffect = sp<effect::EffectHalAidl>::cast(effect);
diff --git a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
index a8f9f7e..96a3e60 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalAidl.cpp
@@ -24,7 +24,6 @@
#include <aidl/android/hardware/audio/core/IModule.h>
#include <android/binder_manager.h>
-#include <binder/IServiceManager.h>
#include <media/AidlConversionNdkCpp.h>
#include <media/AidlConversionUtil.h>
#include <utils/Log.h>
@@ -119,23 +118,9 @@
if (pids == nullptr) {
return BAD_VALUE;
}
- // The functionality for retrieving debug infos of services is not exposed via the NDK.
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm == nullptr) {
- return NO_INIT;
- }
- std::set<pid_t> pidsSet;
- const auto moduleServiceName = std::string(IModule::descriptor) + "/";
- auto debugInfos = sm->getServiceDebugInfo();
- for (const auto& info : debugInfos) {
- if (info.pid > 0 &&
- info.name.size() > moduleServiceName.size() && // '>' as there must be instance name
- info.name.substr(0, moduleServiceName.size()) == moduleServiceName) {
- pidsSet.insert(info.pid);
- }
- }
- *pids = {pidsSet.begin(), pidsSet.end()};
- return NO_ERROR;
+ // Retrieval of HAL pids requires "list services" permission which is not granted
+ // to the audio server. This job is performed by AudioService (in Java) instead.
+ return PERMISSION_DENIED;
}
status_t DevicesFactoryHalAidl::setCallbackOnce(sp<DevicesFactoryHalCallback> callback) {
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index aee42a9..182d68a 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -131,6 +131,7 @@
}
ndk::ScopedAStatus EffectProxy::close() {
+ command(CommandId::STOP);
return runWithAllSubEffects([&](std::shared_ptr<IEffect>& effect) {
return effect->close();
});
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.cpp b/media/libaudiohal/impl/Hal2AidlMapper.cpp
index 47fcd27..413a1f8 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.cpp
+++ b/media/libaudiohal/impl/Hal2AidlMapper.cpp
@@ -30,11 +30,13 @@
using aidl::android::aidl_utils::statusTFromBinderStatus;
using aidl::android::media::audio::common::AudioChannelLayout;
using aidl::android::media::audio::common::AudioConfig;
+using aidl::android::media::audio::common::AudioConfigBase;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceAddress;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioDeviceType;
using aidl::android::media::audio::common::AudioFormatDescription;
+using aidl::android::media::audio::common::AudioFormatType;
using aidl::android::media::audio::common::AudioInputFlags;
using aidl::android::media::audio::common::AudioIoFlags;
using aidl::android::media::audio::common::AudioOutputFlags;
@@ -64,10 +66,11 @@
portConfig.format.value() == config.base.format;
}
-void setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
+AudioConfig* setConfigFromPortConfig(AudioConfig* config, const AudioPortConfig& portConfig) {
config->base.sampleRate = portConfig.sampleRate.value().value;
config->base.channelMask = portConfig.channelMask.value();
config->base.format = portConfig.format.value();
+ return config;
}
void setPortConfigFromConfig(AudioPortConfig* portConfig, const AudioConfig& config) {
@@ -142,8 +145,11 @@
std::vector<int32_t>* ids, std::set<int32_t>* portIds) -> status_t {
for (const auto& s : configs) {
AudioPortConfig portConfig;
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
+ RETURN_STATUS_IF_ERROR(setPortConfig(
s, destinationPortIds, &portConfig, cleanups));
+ LOG_ALWAYS_FATAL_IF(portConfig.id == 0,
+ "fillPortConfigs: initial config: %s, port config: %s",
+ s.toString().c_str(), portConfig.toString().c_str());
ids->push_back(portConfig.id);
if (portIds != nullptr) {
portIds->insert(portConfig.portId);
@@ -189,30 +195,23 @@
}
status_t Hal2AidlMapper::createOrUpdatePortConfig(
- const AudioPortConfig& requestedPortConfig, PortConfigs::iterator* result, bool* created) {
- AudioPortConfig appliedPortConfig;
+ const AudioPortConfig& requestedPortConfig, AudioPortConfig* result, bool* created) {
bool applied = false;
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
- requestedPortConfig, &appliedPortConfig, &applied)));
+ requestedPortConfig, result, &applied)));
if (!applied) {
- RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->setAudioPortConfig(
- appliedPortConfig, &appliedPortConfig, &applied)));
- if (!applied) {
- ALOGE("%s: module %s did not apply suggested config %s",
- __func__, mInstance.c_str(), appliedPortConfig.toString().c_str());
- return NO_INIT;
- }
+ result->id = 0;
+ *created = false;
+ return OK;
}
- int32_t id = appliedPortConfig.id;
+ int32_t id = result->id;
if (requestedPortConfig.id != 0 && requestedPortConfig.id != id) {
LOG_ALWAYS_FATAL("%s: requested port config id %d changed to %d", __func__,
requestedPortConfig.id, id);
}
- auto [it, inserted] = mPortConfigs.insert_or_assign(std::move(id),
- std::move(appliedPortConfig));
- *result = it;
+ auto [_, inserted] = mPortConfigs.insert_or_assign(id, *result);
*created = inserted;
return OK;
}
@@ -258,11 +257,10 @@
return OK;
}
-status_t Hal2AidlMapper::findOrCreatePortConfig(
+status_t Hal2AidlMapper::findOrCreateDevicePortConfig(
const AudioDevice& device, const AudioConfig* config, AudioPortConfig* portConfig,
bool* created) {
- auto portConfigIt = findPortConfig(device);
- if (portConfigIt == mPortConfigs.end()) {
+ if (auto portConfigIt = findPortConfig(device); portConfigIt == mPortConfigs.end()) {
auto portsIt = findPort(device);
if (portsIt == mPorts.end()) {
ALOGE("%s: device port for device %s is not found in the module %s",
@@ -274,24 +272,40 @@
if (config != nullptr) {
setPortConfigFromConfig(&requestedPortConfig, *config);
}
- RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
- created));
+ AudioPortConfig suggestedOrAppliedPortConfig;
+ RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig,
+ &suggestedOrAppliedPortConfig, created));
+ if (suggestedOrAppliedPortConfig.id == 0) {
+ // Try again with the suggested config
+ suggestedOrAppliedPortConfig.id = requestedPortConfig.id;
+ AudioPortConfig appliedPortConfig;
+ RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(suggestedOrAppliedPortConfig,
+ &appliedPortConfig, created));
+ if (appliedPortConfig.id == 0) {
+ ALOGE("%s: module %s did not apply suggested config %s", __func__,
+ mInstance.c_str(), suggestedOrAppliedPortConfig.toString().c_str());
+ return NO_INIT;
+ }
+ *portConfig = appliedPortConfig;
+ } else {
+ *portConfig = suggestedOrAppliedPortConfig;
+ }
} else {
+ *portConfig = portConfigIt->second;
*created = false;
}
- *portConfig = portConfigIt->second;
return OK;
}
-status_t Hal2AidlMapper::findOrCreatePortConfig(
+status_t Hal2AidlMapper::findOrCreateMixPortConfig(
const AudioConfig& config, const std::optional<AudioIoFlags>& flags, int32_t ioHandle,
AudioSource source, const std::set<int32_t>& destinationPortIds,
AudioPortConfig* portConfig, bool* created) {
// These flags get removed one by one in this order when retrying port finding.
static const std::vector<AudioInputFlags> kOptionalInputFlags{
AudioInputFlags::FAST, AudioInputFlags::RAW, AudioInputFlags::VOIP_TX };
- auto portConfigIt = findPortConfig(config, flags, ioHandle);
- if (portConfigIt == mPortConfigs.end() && flags.has_value()) {
+ if (auto portConfigIt = findPortConfig(config, flags, ioHandle);
+ portConfigIt == mPortConfigs.end() && flags.has_value()) {
auto optionalInputFlagsIt = kOptionalInputFlags.begin();
AudioIoFlags matchFlags = flags.value();
auto portsIt = findPort(config, matchFlags, destinationPortIds);
@@ -319,14 +333,14 @@
AudioPortConfig requestedPortConfig;
requestedPortConfig.portId = portsIt->first;
setPortConfigFromConfig(&requestedPortConfig, config);
+ requestedPortConfig.flags = portsIt->second.flags;
requestedPortConfig.ext = AudioPortMixExt{ .handle = ioHandle };
if (matchFlags.getTag() == AudioIoFlags::Tag::input
&& source != AudioSource::SYS_RESERVED_INVALID) {
requestedPortConfig.ext.get<AudioPortExt::Tag::mix>().usecase =
AudioPortMixExtUseCase::make<AudioPortMixExtUseCase::Tag::source>(source);
}
- RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
- created));
+ return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
} else if (portConfigIt == mPortConfigs.end() && !flags.has_value()) {
ALOGW("%s: mix port config for %s, handle %d not found in the module %s, "
"and was not created as flags are not specified",
@@ -343,13 +357,12 @@
}
if (requestedPortConfig != portConfigIt->second) {
- RETURN_STATUS_IF_ERROR(createOrUpdatePortConfig(requestedPortConfig, &portConfigIt,
- created));
+ return createOrUpdatePortConfig(requestedPortConfig, portConfig, created);
} else {
+ *portConfig = portConfigIt->second;
*created = false;
}
}
- *portConfig = portConfigIt->second;
return OK;
}
@@ -371,11 +384,11 @@
AudioPortMixExtUseCase::Tag::source ?
requestedPortConfig.ext.get<Tag::mix>().usecase.
get<AudioPortMixExtUseCase::Tag::source>() : AudioSource::SYS_RESERVED_INVALID;
- return findOrCreatePortConfig(config, requestedPortConfig.flags,
+ return findOrCreateMixPortConfig(config, requestedPortConfig.flags,
requestedPortConfig.ext.get<Tag::mix>().handle, source, destinationPortIds,
portConfig, created);
} else if (requestedPortConfig.ext.getTag() == Tag::device) {
- return findOrCreatePortConfig(
+ return findOrCreateDevicePortConfig(
requestedPortConfig.ext.get<Tag::device>().device, nullptr /*config*/,
portConfig, created);
}
@@ -384,18 +397,6 @@
return BAD_VALUE;
}
-status_t Hal2AidlMapper::findOrCreatePortConfig(
- const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
- AudioPortConfig* portConfig, Cleanups* cleanups) {
- bool created = false;
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
- requestedPortConfig, destinationPortIds, portConfig, &created));
- if (created && cleanups != nullptr) {
- cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
- }
- return OK;
-}
-
status_t Hal2AidlMapper::findPortConfig(const AudioDevice& device, AudioPortConfig* portConfig) {
if (auto it = findPortConfig(device); it != mPortConfigs.end()) {
*portConfig = it->second;
@@ -675,21 +676,56 @@
config->toString().c_str(), mixPortConfig->toString().c_str());
resetUnusedPatchesAndPortConfigs();
const bool isInput = flags.getTag() == AudioIoFlags::Tag::input;
+ const AudioConfig initialConfig = *config;
// Find / create AudioPortConfigs for the device port and the mix port,
// then find / create a patch between them, and open a stream on the mix port.
AudioPortConfig devicePortConfig;
bool created = false;
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(device, config,
- &devicePortConfig, &created));
+ RETURN_STATUS_IF_ERROR(findOrCreateDevicePortConfig(device, config,
+ &devicePortConfig, &created));
+ LOG_ALWAYS_FATAL_IF(devicePortConfig.id == 0);
if (created) {
cleanups->add(&Hal2AidlMapper::resetPortConfig, devicePortConfig.id);
}
- RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(*config, flags, ioHandle, source,
+ RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
std::set<int32_t>{devicePortConfig.portId}, mixPortConfig, &created));
if (created) {
cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
}
setConfigFromPortConfig(config, *mixPortConfig);
+ bool retryWithSuggestedConfig = false; // By default, let the framework to retry.
+ if (mixPortConfig->id == 0 && config->base == AudioConfigBase{}) {
+ // The HAL proposes a default config, can retry here.
+ retryWithSuggestedConfig = true;
+ } else if (isInput && config->base != initialConfig.base) {
+ // If the resulting config is different, we must stop and provide the config to the
+ // framework so that it can retry.
+ mixPortConfig->id = 0;
+ } else if (!isInput && mixPortConfig->id == 0 &&
+ (initialConfig.base.format.type == AudioFormatType::PCM ||
+ !isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
+ AudioOutputFlags::DIRECT) ||
+ isBitPositionFlagSet(flags.get<AudioIoFlags::output>(),
+ AudioOutputFlags::COMPRESS_OFFLOAD))) {
+ // The framework does not retry opening non-direct PCM and IEC61937 outputs, need to retry
+ // here (see 'AudioHwDevice::openOutputStream').
+ retryWithSuggestedConfig = true;
+ }
+ if (mixPortConfig->id == 0 && retryWithSuggestedConfig) {
+ ALOGD("%s: retrying to find/create a mix port config using config %s", __func__,
+ config->toString().c_str());
+ RETURN_STATUS_IF_ERROR(findOrCreateMixPortConfig(*config, flags, ioHandle, source,
+ std::set<int32_t>{devicePortConfig.portId}, mixPortConfig, &created));
+ if (created) {
+ cleanups->add(&Hal2AidlMapper::resetPortConfig, mixPortConfig->id);
+ }
+ setConfigFromPortConfig(config, *mixPortConfig);
+ }
+ if (mixPortConfig->id == 0) {
+ ALOGD("%p %s: returning suggested config for the stream: %s", this, __func__,
+ config->toString().c_str());
+ return OK;
+ }
if (isInput) {
RETURN_STATUS_IF_ERROR(findOrCreatePatch(
{devicePortConfig.id}, {mixPortConfig->id}, patch, &created));
@@ -706,6 +742,18 @@
return OK;
}
+status_t Hal2AidlMapper::setPortConfig(
+ const AudioPortConfig& requestedPortConfig, const std::set<int32_t>& destinationPortIds,
+ AudioPortConfig* portConfig, Cleanups* cleanups) {
+ bool created = false;
+ RETURN_STATUS_IF_ERROR(findOrCreatePortConfig(
+ requestedPortConfig, destinationPortIds, portConfig, &created));
+ if (created && cleanups != nullptr) {
+ cleanups->add(&Hal2AidlMapper::resetPortConfig, portConfig->id);
+ }
+ return OK;
+}
+
status_t Hal2AidlMapper::releaseAudioPatch(int32_t patchId) {
return releaseAudioPatches({patchId});
}
diff --git a/media/libaudiohal/impl/Hal2AidlMapper.h b/media/libaudiohal/impl/Hal2AidlMapper.h
index 70a2bd7..ee55b22 100644
--- a/media/libaudiohal/impl/Hal2AidlMapper.h
+++ b/media/libaudiohal/impl/Hal2AidlMapper.h
@@ -36,6 +36,11 @@
// structures directly. Mapper does the job of translating the "legacy" way of identifying ports
// and port configs (by device addresses and I/O handles) into AIDL IDs. Once the framework will
// be updated to provide these IDs directly to libaudiohal, the need for the mapper will cease.
+//
+// Note that unlike DeviceHalInterface, which sometimes allows a method to return an error,
+// but still consider some of the outputs to be valid (for example, in 'open{Input|Output}Stream'),
+// 'Hal2AidlMapper' follows the Binder convention. It means that if a method returns an error,
+// the outputs may not be initialized at all and should not be considered by the caller.
class Hal2AidlMapper {
public:
using Cleanups = Cleanups<Hal2AidlMapper>;
@@ -49,27 +54,6 @@
const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sources,
const std::vector<::aidl::android::media::audio::common::AudioPortConfig>& sinks,
int32_t* patchId, Cleanups* cleanups);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioDevice& device,
- const ::aidl::android::media::audio::common::AudioConfig* config,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
- bool* created);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioConfig& config,
- const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
- int32_t ioHandle,
- ::aidl::android::media::audio::common::AudioSource source,
- const std::set<int32_t>& destinationPortIds,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
- const std::set<int32_t>& destinationPortIds,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
- status_t findOrCreatePortConfig(
- const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
- const std::set<int32_t>& destinationPortIds,
- ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
- Cleanups* cleanups = nullptr);
status_t findPortConfig(
const ::aidl::android::media::audio::common::AudioDevice& device,
::aidl::android::media::audio::common::AudioPortConfig* portConfig);
@@ -88,6 +72,8 @@
return ::aidl::android::convertContainer(mRoutes, routes, converter);
}
status_t initialize();
+ // If the resulting 'mixPortConfig->id' is 0, that means the stream was not created,
+ // and 'config' is a suggested config.
status_t prepareToOpenStream(
int32_t ioHandle,
const ::aidl::android::media::audio::common::AudioDevice& device,
@@ -97,6 +83,11 @@
::aidl::android::media::audio::common::AudioConfig* config,
::aidl::android::media::audio::common::AudioPortConfig* mixPortConfig,
::aidl::android::hardware::audio::core::AudioPatch* patch);
+ status_t setPortConfig(
+ const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+ const std::set<int32_t>& destinationPortIds,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
+ Cleanups* cleanups = nullptr);
status_t releaseAudioPatch(int32_t patchId);
void resetUnusedPatchesAndPortConfigs();
status_t setDevicePortConnectedState(
@@ -128,9 +119,11 @@
const ::aidl::android::media::audio::common::AudioPort& p);
bool audioDeviceMatches(const ::aidl::android::media::audio::common::AudioDevice& device,
const ::aidl::android::media::audio::common::AudioPortConfig& p);
+ // If the 'result->id' is 0, that means, the config was not created/updated,
+ // and the 'result' is a suggestion from the HAL.
status_t createOrUpdatePortConfig(
const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
- PortConfigs::iterator* result, bool *created);
+ ::aidl::android::media::audio::common::AudioPortConfig* result, bool *created);
void eraseConnectedPort(int32_t portId);
status_t findOrCreatePatch(
const std::set<int32_t>& sourcePortConfigIds,
@@ -139,6 +132,24 @@
status_t findOrCreatePatch(
const ::aidl::android::hardware::audio::core::AudioPatch& requestedPatch,
::aidl::android::hardware::audio::core::AudioPatch* patch, bool* created);
+ status_t findOrCreateDevicePortConfig(
+ const ::aidl::android::media::audio::common::AudioDevice& device,
+ const ::aidl::android::media::audio::common::AudioConfig* config,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig,
+ bool* created);
+ // If the resulting 'portConfig->id' is 0, that means the config was not created,
+ // and 'portConfig' is a suggested config.
+ status_t findOrCreateMixPortConfig(
+ const ::aidl::android::media::audio::common::AudioConfig& config,
+ const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
+ int32_t ioHandle,
+ ::aidl::android::media::audio::common::AudioSource source,
+ const std::set<int32_t>& destinationPortIds,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
+ status_t findOrCreatePortConfig(
+ const ::aidl::android::media::audio::common::AudioPortConfig& requestedPortConfig,
+ const std::set<int32_t>& destinationPortIds,
+ ::aidl::android::media::audio::common::AudioPortConfig* portConfig, bool* created);
Patches::iterator findPatch(const std::set<int32_t>& sourcePortConfigIds,
const std::set<int32_t>& sinkPortConfigIds);
Ports::iterator findPort(const ::aidl::android::media::audio::common::AudioDevice& device);
diff --git a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
index adff110..1204a3b 100644
--- a/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
+++ b/media/libaudiohal/tests/CoreAudioHalAidl_test.cpp
@@ -201,6 +201,9 @@
ndk::ScopedAStatus getAAudioHardwareBurstMinUsec(int32_t*) override {
return ndk::ScopedAStatus::ok();
}
+ ndk::ScopedAStatus prepareToDisconnectExternalDevice(int32_t) override {
+ return ndk::ScopedAStatus::ok();
+ }
bool mIsScreenTurnedOn = false;
ScreenRotation mScreenRotation = ScreenRotation::DEG_0;
diff --git a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
index 578f58a..33f6779 100644
--- a/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
+++ b/media/libeffects/loudness/dsp/core/dynamic_range_compression.cpp
@@ -89,8 +89,7 @@
} else {
state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
}
- compressor_gain_ *=
- math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
+ compressor_gain_ *= expf(state_ - prev_state);
x *= compressor_gain_;
if (x > kFixedPointLimit) {
return kFixedPointLimit;
@@ -118,8 +117,7 @@
} else {
state_ = alpha_release_ * state_ + (1.0f - alpha_release_) * cv;
}
- compressor_gain_ *=
- math::ExpApproximationViaTaylorExpansionOrder5(state_ - prev_state);
+ compressor_gain_ *= expf(state_ - prev_state);
*x1 *= compressor_gain_;
if (*x1 > kFixedPointLimit) {
*x1 = kFixedPointLimit;
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index 0db7a73..a163f4b 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -32,6 +32,20 @@
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioDeviceType;
+BundleContext::BundleContext(int statusDepth, const Parameter::Common& common,
+ const lvm::BundleEffectType& type)
+ : EffectContext(statusDepth, common), mType(type) {
+ LOG(DEBUG) << __func__ << type;
+ int channelCount = ::aidl::android::hardware::audio::common::getChannelCount(
+ common.input.base.channelMask);
+ mSamplesPerSecond = common.input.base.sampleRate * channelCount;
+}
+
+BundleContext::~BundleContext() {
+ LOG(DEBUG) << __func__;
+ deInit();
+}
+
RetCode BundleContext::init() {
std::lock_guard lg(mMutex);
// init with pre-defined preset NORMAL
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index 3f31b8b..779d53a 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -29,14 +29,8 @@
class BundleContext final : public EffectContext {
public:
BundleContext(int statusDepth, const Parameter::Common& common,
- const lvm::BundleEffectType& type)
- : EffectContext(statusDepth, common), mType(type) {
- LOG(DEBUG) << __func__ << type;
- }
- ~BundleContext() override {
- LOG(DEBUG) << __func__;
- deInit();
- }
+ const lvm::BundleEffectType& type);
+ ~BundleContext();
RetCode init();
void deInit();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index a625893..bb49b5a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -1977,6 +1977,8 @@
if (rate > 0) {
format->setFloat("operating-rate", rate * mPlaybackSettings.mSpeed);
}
+
+ format->setInt32("android._video-scaling", mVideoScalingMode);
}
Mutex::Autolock autoLock(mDecoderLock);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index ee66622..46a3587 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1197,6 +1197,7 @@
mTunneledInputHeight(0),
mTunneled(false),
mTunnelPeekState(TunnelPeekState::kLegacyMode),
+ mTunnelPeekEnabled(false),
mHaveInputSurface(false),
mHavePendingInputBuffers(false),
mCpuBoostRequested(false),
@@ -1742,6 +1743,7 @@
TunnelPeekState previousState = mTunnelPeekState;
if(tunnelPeek == 0){
+ mTunnelPeekEnabled = false;
switch (mTunnelPeekState) {
case TunnelPeekState::kLegacyMode:
msg->setInt32("android._tunnel-peek-set-legacy", 0);
@@ -1757,6 +1759,7 @@
return;
}
} else {
+ mTunnelPeekEnabled = true;
switch (mTunnelPeekState) {
case TunnelPeekState::kLegacyMode:
msg->setInt32("android._tunnel-peek-set-legacy", 0);
@@ -4943,10 +4946,11 @@
CHECK(msg->senderAwaitsResponse(&replyID));
TunnelPeekState previousState = mTunnelPeekState;
if (previousState != TunnelPeekState::kLegacyMode) {
- mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ mTunnelPeekState = mTunnelPeekEnabled ? TunnelPeekState::kEnabledNoBuffer :
+ TunnelPeekState::kDisabledNoBuffer;
ALOGV("TunnelPeekState: %s -> %s",
asString(previousState),
- asString(TunnelPeekState::kEnabledNoBuffer));
+ asString(mTunnelPeekState));
}
mReplyID = replyID;
@@ -5445,10 +5449,11 @@
returnBuffersToCodec();
TunnelPeekState previousState = mTunnelPeekState;
if (previousState != TunnelPeekState::kLegacyMode) {
- mTunnelPeekState = TunnelPeekState::kEnabledNoBuffer;
+ mTunnelPeekState = mTunnelPeekEnabled ? TunnelPeekState::kEnabledNoBuffer :
+ TunnelPeekState::kDisabledNoBuffer;
ALOGV("TunnelPeekState: %s -> %s",
asString(previousState),
- asString(TunnelPeekState::kEnabledNoBuffer));
+ asString(mTunnelPeekState));
}
break;
}
@@ -5991,7 +5996,7 @@
if (isBufferDecodeOnly) {
buffer->meta()->setInt32("decode-only", true);
}
- if (mTunneled && !isBufferDecodeOnly) {
+ if (mTunneled && !isBufferDecodeOnly && !(flags & BUFFER_FLAG_CODECCONFIG)) {
TunnelPeekState previousState = mTunnelPeekState;
switch(mTunnelPeekState){
case TunnelPeekState::kEnabledNoBuffer:
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index e091cb8..800e2e1 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -314,8 +314,11 @@
outHeader->nFilledLen = frameSize;
List<BufferInfo *>::iterator it = outQueue.begin();
- while ((*it)->mHeader != outHeader) {
- ++it;
+ while (it != outQueue.end() && (*it)->mHeader != outHeader) {
+ ++it;
+ }
+ if (it == outQueue.end()) {
+ return;
}
BufferInfo *outInfo = *it;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index f99a78b..baa5b7e 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -560,6 +560,7 @@
int32_t mTunneledInputHeight;
bool mTunneled;
TunnelPeekState mTunnelPeekState;
+ bool mTunnelPeekEnabled;
sp<IDescrambler> mDescrambler;
diff --git a/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp b/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp
index 5cf1ed3..e8fea73 100644
--- a/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp
+++ b/media/module/codecs/mp3dec/src/pvmp3_framedecoder.cpp
@@ -154,6 +154,184 @@
; FUNCTION CODE
----------------------------------------------------------------------------*/
+/* The below code is borrowed from ./test/mp3reader.cpp */
+static bool parseHeader(
+ uint32_t header, size_t *frame_size,
+ uint32_t *out_sampling_rate = NULL, uint32_t *out_channels = NULL ,
+ uint32_t *out_bitrate = NULL, uint32_t *out_num_samples = NULL) {
+ *frame_size = 0;
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = 0;
+ }
+
+ if (out_channels) {
+ *out_channels = 0;
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = 0;
+ }
+
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
+
+ if ((header & 0xffe00000) != 0xffe00000) {
+ return false;
+ }
+
+ unsigned version = (header >> 19) & 3;
+
+ if (version == 0x01) {
+ return false;
+ }
+
+ unsigned layer = (header >> 17) & 3;
+
+ if (layer == 0x00) {
+ return false;
+ }
+
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+
+ if (bitrate_index == 0 || bitrate_index == 0x0f) {
+ // Disallow "free" bitrate.
+ return false;
+ }
+
+ unsigned sampling_rate_index = (header >> 10) & 3;
+
+ if (sampling_rate_index == 3) {
+ return false;
+ }
+
+ static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) {
+ // layer I
+
+ static const int kBitrateV1[] = {
+ 32, 64, 96, 128, 160, 192, 224, 256,
+ 288, 320, 352, 384, 416, 448
+ };
+
+ static const int kBitrateV2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 144, 160, 176, 192, 224, 256
+ };
+
+ int bitrate =
+ (version == 3 /* V1 */)
+ ? kBitrateV1[bitrate_index - 1]
+ : kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+
+ if (out_num_samples) {
+ *out_num_samples = 384;
+ }
+ } else {
+ // layer II or III
+
+ static const int kBitrateV1L2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 160, 192, 224, 256, 320, 384
+ };
+
+ static const int kBitrateV1L3[] = {
+ 32, 40, 48, 56, 64, 80, 96, 112,
+ 128, 160, 192, 224, 256, 320
+ };
+
+ static const int kBitrateV2[] = {
+ 8, 16, 24, 32, 40, 48, 56, 64,
+ 80, 96, 112, 128, 144, 160
+ };
+
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate = (layer == 2 /* L2 */)
+ ? kBitrateV1L2[bitrate_index - 1]
+ : kBitrateV1L3[bitrate_index - 1];
+
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
+ } else {
+ // V2 (or 2.5)
+
+ bitrate = kBitrateV2[bitrate_index - 1];
+ if (out_num_samples) {
+ *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+ }
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else {
+ // V2 or V2.5
+ size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+ *frame_size = tmp * bitrate / sampling_rate + padding;
+ }
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+// Check if the input is valid by checking if it contains a sync word
+static bool isInputValid(uint8 *buf, uint32 inSize)
+{
+ // Buffer needs to contain at least 4 bytes which is the size of
+ // the header
+ if (inSize < 4) return false;
+
+ size_t totalInSize = 0;
+ size_t frameSize = 0;
+ while (totalInSize <= (inSize - 4)) {
+ if (!parseHeader(U32_AT(buf + totalInSize), &frameSize)) {
+ return false;
+ }
+ // Buffer needs to be large enough to include complete frame
+ if ((frameSize > inSize) || (totalInSize > (inSize - frameSize))) {
+ return false;
+ }
+ totalInSize += frameSize;
+ }
+
+ return true;
+}
+
ERROR_CODE pvmp3_framedecoder(tPVMP3DecoderExternal *pExt,
void *pMem)
{
@@ -170,6 +348,12 @@
mp3Header info_data;
mp3Header *info = &info_data;
+ if (!isInputValid(pExt->pInputBuffer, pExt->inputBufferCurrentLength))
+ {
+ pExt->outputFrameSize = 0;
+ return SYNCH_LOST_ERROR;
+ }
+
pVars->inputStream.pBuffer = pExt->pInputBuffer;
diff --git a/media/module/extractors/mp4/ItemTable.cpp b/media/module/extractors/mp4/ItemTable.cpp
index cf3df62..c6586fc 100644
--- a/media/module/extractors/mp4/ItemTable.cpp
+++ b/media/module/extractors/mp4/ItemTable.cpp
@@ -1501,9 +1501,9 @@
info.isExif(), (long long)offset, (long long)size);
if ((info.isExif() && size > 4) || (info.isXmp() && size > 0)) {
ExternalMetaItem metaItem = {
- .isExif = info.isExif(),
.offset = offset,
.size = size,
+ .isExif = info.isExif(),
};
mItemIdToMetaMap.add(info.itemId, metaItem);
}
diff --git a/media/module/extractors/mp4/MPEG4Extractor.cpp b/media/module/extractors/mp4/MPEG4Extractor.cpp
index 559927a..b3707c8 100644
--- a/media/module/extractors/mp4/MPEG4Extractor.cpp
+++ b/media/module/extractors/mp4/MPEG4Extractor.cpp
@@ -522,7 +522,7 @@
return AMEDIA_ERROR_UNKNOWN;
}
- [=] {
+ [this, &track] {
int64_t duration;
int32_t samplerate;
// Only for audio track.
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 42e9078..a2b1c5e 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -427,6 +427,9 @@
}
void MtpDataPacket::putString(const uint16_t* string) {
+ if (string == NULL) {
+ return;
+ }
int count = 0;
for (int i = 0; i <= MTP_STRING_MAX_CHARACTER_NUMBER; i++) {
if (string[i])
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b87b373..9420bf1 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -31,6 +31,7 @@
#include <afutils/Permission.h>
#include <afutils/PropertyUtils.h>
#include <afutils/TypedLogger.h>
+#include <android-base/errors.h>
#include <android-base/stringprintf.h>
#include <android/media/IAudioPolicyService.h>
#include <audiomanager/IAudioManager.h>
@@ -38,6 +39,7 @@
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
#include <cutils/properties.h>
+#include <com_android_media_audioserver.h>
#include <media/AidlConversion.h>
#include <media/AudioParameter.h>
#include <media/AudioValidator.h>
@@ -210,6 +212,20 @@
return methodStatistics;
}
+namespace base {
+template <typename T>
+struct OkOrFail<std::optional<T>> {
+ using opt_t = std::optional<T>;
+ OkOrFail() = delete;
+ OkOrFail(const opt_t&) = delete;
+
+ static bool IsOk(const opt_t& opt) { return opt.has_value(); }
+ static T Unwrap(opt_t&& opt) { return std::move(opt.value()); }
+ static std::string ErrorMessage(const opt_t&) { return "Empty optional"; }
+ static void Fail(opt_t&&) {}
+};
+}
+
class DevicesFactoryHalCallbackImpl : public DevicesFactoryHalCallback {
public:
void onNewDevicesAvailable() override {
@@ -3597,11 +3613,20 @@
void AudioFlinger::dumpToThreadLog_l(const sp<IAfThreadBase> &thread)
{
constexpr int THREAD_DUMP_TIMEOUT_MS = 2;
- audio_utils::FdToStringOldImpl fdToString("- ", THREAD_DUMP_TIMEOUT_MS);
- const int fd = fdToString.borrowFdUnsafe();
- if (fd >= 0) {
- thread->dump(fd, {} /* args */);
- mThreadLog.logs(-1 /* time */, fdToString.closeAndGetString());
+ constexpr auto PREFIX = "- ";
+ if (com::android::media::audioserver::fdtostring_timeout_fix()) {
+ using ::android::audio_utils::FdToString;
+
+ auto writer = OR_RETURN(FdToString::createWriter(PREFIX));
+ thread->dump(writer.borrowFdUnsafe(), {} /* args */);
+ mThreadLog.logs(-1 /* time */, FdToString::closeWriterAndGetString(std::move(writer)));
+ } else {
+ audio_utils::FdToStringOldImpl fdToString("- ", THREAD_DUMP_TIMEOUT_MS);
+ const int fd = fdToString.borrowFdUnsafe();
+ if (fd >= 0) {
+ thread->dump(fd, {} /* args */);
+ mThreadLog.logs(-1 /* time */, fdToString.closeAndGetString());
+ }
}
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 54880f8..18bb173 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7608,6 +7608,23 @@
}
}
+void DuplicatingThread::threadLoop_exit()
+{
+ // Prevent calling the OutputTrack dtor in the DuplicatingThread dtor
+ // where other mutexes (i.e. AudioPolicyService_Mutex) may be held.
+ // Do so here in the threadLoop_exit().
+
+ SortedVector <sp<IAfOutputTrack>> localTracks;
+ {
+ audio_utils::lock_guard l(mutex());
+ localTracks = std::move(mOutputTracks);
+ mOutputTracks.clear();
+ }
+ localTracks.clear();
+ outputTracks.clear();
+ PlaybackThread::threadLoop_exit();
+}
+
void DuplicatingThread::dumpInternals_l(int fd, const Vector<String16>& args)
{
MixerThread::dumpInternals_l(fd, args);
@@ -7881,6 +7898,15 @@
}
}
+void SpatializerThread::threadLoop_exit()
+{
+ // The Spatializer EffectHandle must be released on the PlaybackThread
+ // threadLoop() to prevent lock inversion in the SpatializerThread dtor.
+ mFinalDownMixer.clear();
+
+ PlaybackThread::threadLoop_exit();
+}
+
// ----------------------------------------------------------------------------
// Record
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a5afdd8..ea994a5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1849,6 +1849,7 @@
void threadLoop_sleepTime() final REQUIRES(ThreadBase_ThreadLoop);
ssize_t threadLoop_write() final REQUIRES(ThreadBase_ThreadLoop);
void threadLoop_standby() final REQUIRES(ThreadBase_ThreadLoop);
+ void threadLoop_exit() final REQUIRES(ThreadBase_ThreadLoop);
void cacheParameters_l() final REQUIRES(mutex(), ThreadBase_ThreadLoop);
private:
@@ -1900,6 +1901,8 @@
REQUIRES(ThreadBase_ThreadLoop) EXCLUDES_ThreadBase_Mutex;
void setHalLatencyMode_l() final REQUIRES(mutex());
+ void threadLoop_exit() final REQUIRES(ThreadBase_ThreadLoop);
+
private:
// Do not request a specific mode by default
audio_latency_mode_t mRequestedLatencyMode = AUDIO_LATENCY_MODE_FREE;
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index b560bc4..f066c09 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -61,7 +61,7 @@
status_t updateMix(const AudioMix& mix, const std::vector<AudioMixMatchCriterion>& newCriteria);
- void closeOutput(sp<SwAudioOutputDescriptor> &desc);
+ void closeOutput(sp<SwAudioOutputDescriptor> &desc, const SwAudioOutputCollection& allOutputs);
/**
* Tries to find the best matching audio policy mix
diff --git a/services/audiopolicy/common/managerdefinitions/include/HwModule.h b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
index cf20260..d206637 100644
--- a/services/audiopolicy/common/managerdefinitions/include/HwModule.h
+++ b/services/audiopolicy/common/managerdefinitions/include/HwModule.h
@@ -89,10 +89,12 @@
status_t addProfile(const sp<IOProfile> &profile);
status_t addOutputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address);
+ audio_devices_t device, const String8& address,
+ audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE);
status_t removeOutputProfile(const std::string& name);
status_t addInputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address);
+ audio_devices_t device, const String8& address,
+ audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE);
status_t removeInputProfile(const std::string& name);
audio_module_handle_t getHandle() const { return mHandle; }
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 7ee75c7..dc0f466 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -272,12 +272,33 @@
return BAD_VALUE;
}
-void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
+void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc,
+ const SwAudioOutputCollection& allOutputs)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioPolicyMix> policyMix = itemAt(i);
- if (policyMix->getOutput() == desc) {
- policyMix->clearOutput();
+ if (policyMix->getOutput() != desc) {
+ continue;
+ }
+ policyMix->clearOutput();
+ if (policyMix->mRouteFlags != MIX_ROUTE_FLAG_RENDER) {
+ continue;
+ }
+ auto device = desc->supportedDevices().getDevice(
+ policyMix->mDeviceType, policyMix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
+ if (device == nullptr) {
+ // This must not happen
+ ALOGE("%s, the rerouted device is not found", __func__);
+ continue;
+ }
+ // Restore the policy mix mix output to the first opened output supporting a route to
+ // the mix device. This is because the current mix output can be changed to a direct output.
+ for (size_t j = 0; j < allOutputs.size(); ++j) {
+ if (allOutputs[i] != desc && !allOutputs[i]->isDuplicated() &&
+ allOutputs[i]->supportedDevices().contains(device)) {
+ policyMix->setOutput(allOutputs[i]);
+ break;
+ }
}
}
}
@@ -335,13 +356,6 @@
"audio policy mix.", __func__);
return INVALID_OPERATION;
}
- if (mixDevice != nullptr) {
- // TODO(b/301619865): Only disallow the device that doesn't support MMAP.
- ALOGD("%s: Rejecting MMAP_NOIRQ request matched to dynamic audio policy "
- "mix pointing to device %s which the mmap support is unknown at this moment",
- __func__, mixDevice->toString(false).c_str());
- return INVALID_OPERATION;
- }
}
if (mixDevice != nullptr && mixDevice->equals(requestedDevice)) {
diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
index cb45f54..6696b45 100644
--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp
@@ -59,12 +59,13 @@
}
status_t HwModule::addOutputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address)
+ audio_devices_t device, const String8& address,
+ audio_output_flags_t flags)
{
sp<IOProfile> profile = new OutputProfile(name);
-
profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
config->sample_rate));
+ profile->setFlags(flags);
sp<DeviceDescriptor> devDesc =
new DeviceDescriptor(device, getTagForDevice(device), address.c_str());
@@ -128,11 +129,13 @@
}
status_t HwModule::addInputProfile(const std::string& name, const audio_config_t *config,
- audio_devices_t device, const String8& address)
+ audio_devices_t device, const String8& address,
+ audio_input_flags_t flags)
{
sp<IOProfile> profile = new InputProfile(name);
profile->addAudioProfile(new AudioProfile(config->format, config->channel_mask,
config->sample_rate));
+ profile->setFlags(flags);
sp<DeviceDescriptor> devDesc =
new DeviceDescriptor(device, getTagForDevice(device), address.c_str());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 69d3b5d..3bffcd8 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1217,13 +1217,14 @@
return BAD_VALUE;
}
if (usePrimaryOutputFromPolicyMixes) {
- sp<DeviceDescriptor> deviceDesc =
+ sp<DeviceDescriptor> policyMixDevice =
mAvailableOutputDevices.getDevice(primaryMix->mDeviceType,
primaryMix->mDeviceAddress,
AUDIO_FORMAT_DEFAULT);
sp<SwAudioOutputDescriptor> policyDesc = primaryMix->getOutput();
bool tryDirectForFlags = policyDesc == nullptr ||
- (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT);
+ (policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) ||
+ (*flags & (AUDIO_OUTPUT_FLAG_HW_AV_SYNC | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ));
// if a direct output can be opened to deliver the track's multi-channel content to the
// output rather than being downmixed by the primary output, then use this direct
// output by by-passing the primary mix if possible, otherwise fall-through to primary
@@ -1231,23 +1232,29 @@
bool tryDirectForChannelMask = policyDesc != nullptr
&& (audio_channel_count_from_out_mask(policyDesc->getConfig().channel_mask) <
audio_channel_count_from_out_mask(config->channel_mask));
- if (deviceDesc != nullptr && (tryDirectForFlags || tryDirectForChannelMask)) {
+ if (policyMixDevice != nullptr && (tryDirectForFlags || tryDirectForChannelMask)) {
audio_io_handle_t newOutput;
status = openDirectOutput(
*stream, session, config,
(audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT),
- DeviceVector(deviceDesc), &newOutput);
+ DeviceVector(policyMixDevice), &newOutput);
if (status == NO_ERROR) {
policyDesc = mOutputs.valueFor(newOutput);
primaryMix->setOutput(policyDesc);
} else if (tryDirectForFlags) {
+ ALOGW("%s, failed open direct, status: %d", __func__, status);
policyDesc = nullptr;
} // otherwise use primary if available.
}
if (policyDesc != nullptr) {
policyDesc->mPolicyMix = primaryMix;
*output = policyDesc->mIoHandle;
- *selectedDeviceId = deviceDesc != 0 ? deviceDesc->getId() : AUDIO_PORT_HANDLE_NONE;
+ *selectedDeviceId = policyMixDevice != nullptr ? policyMixDevice->getId()
+ : AUDIO_PORT_HANDLE_NONE;
+ if ((policyDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != AUDIO_OUTPUT_FLAG_DIRECT) {
+ // Remove direct flag as it is not on a direct output.
+ *flags = (audio_output_flags_t) (*flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
+ }
ALOGV("getOutputForAttr() returns output %d", *output);
if (resultAttr->usage == AUDIO_USAGE_VIRTUAL_SOURCE) {
@@ -1256,6 +1263,13 @@
*outputType = API_OUTPUT_LEGACY;
}
return NO_ERROR;
+ } else {
+ if (policyMixDevice != nullptr) {
+ ALOGE("%s, try to use primary mix but no output found", __func__);
+ return INVALID_OPERATION;
+ }
+ // Fallback to default engine selection as the selected primary mix device is not
+ // available.
}
}
// Virtual sources must always be dynamicaly or explicitly routed
@@ -3659,9 +3673,13 @@
outputConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
inputConfig.channel_mask = AUDIO_CHANNEL_IN_STEREO;
rSubmixModule->addOutputProfile(address.c_str(), &outputConfig,
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address);
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, address,
+ audio_is_linear_pcm(outputConfig.format)
+ ? AUDIO_OUTPUT_FLAG_NONE : AUDIO_OUTPUT_FLAG_DIRECT);
rSubmixModule->addInputProfile(address.c_str(), &inputConfig,
- AUDIO_DEVICE_IN_REMOTE_SUBMIX, address);
+ AUDIO_DEVICE_IN_REMOTE_SUBMIX, address,
+ audio_is_linear_pcm(inputConfig.format)
+ ? AUDIO_INPUT_FLAG_NONE : AUDIO_INPUT_FLAG_DIRECT);
if ((res = setDeviceConnectionStateInt(deviceTypeToMakeAvailable,
AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
@@ -6689,7 +6707,7 @@
return;
}
const bool closingOutputWasActive = closingOutput->isActive();
- mPolicyMixes.closeOutput(closingOutput);
+ mPolicyMixes.closeOutput(closingOutput, mOutputs);
// look for duplicated outputs connected to the output being removed.
for (size_t i = 0; i < mOutputs.size(); i++) {
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 7241597..5d3788d 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -299,7 +299,11 @@
AudioDeviceTypeAddrVector devices;
bool hasSpatializer = mAudioPolicyManager->canBeSpatialized(&attr, nullptr, devices);
if (hasSpatializer) {
+ // Unlock as Spatializer::create() will use the callback and acquire the
+ // AudioPolicyService_Mutex.
+ mLock.unlock();
mSpatializer = Spatializer::create(this, effectsFactoryHal);
+ mLock.lock();
}
if (mSpatializer == nullptr) {
// No spatializer created, signal the reason: NO_INIT a failure, OK means intended.
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 8dbf471..74d3474 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -211,7 +211,7 @@
std::unique_ptr<AudioPolicyManagerTestClient> mClient;
std::unique_ptr<AudioPolicyTestManager> mManager;
- const uint32_t k48000SamplingRate = 48000;
+ constexpr static const uint32_t k48000SamplingRate = 48000;
};
void AudioPolicyManagerTest::SetUp() {
@@ -1274,13 +1274,30 @@
std::string mixAddress, const audio_config_t& audioConfig,
const std::vector<AudioMixMatchCriterion>& matchCriteria);
void clearPolicyMix();
+ void addPolicyMixAndStartInputForLoopback(
+ int mixType, int mixFlag, audio_devices_t deviceType, std::string mixAddress,
+ const audio_config_t& audioConfig,
+ const std::vector<AudioMixMatchCriterion>& matchCriteria,
+ audio_session_t session=AUDIO_SESSION_NONE,
+ audio_config_base_t config=DEFAULT_INPUT_CONFIG,
+ audio_input_flags_t inputFlags=AUDIO_INPUT_FLAG_NONE);
Vector<AudioMix> mAudioMixes;
const std::string mMixAddress = "remote_submix_media";
+
+ audio_port_handle_t mLoopbackInputPortId = AUDIO_PORT_HANDLE_NONE;
+ std::unique_ptr<RecordingActivityTracker> mTracker;
+ struct audio_port_v7 mInjectionPort;
+
+ constexpr static const audio_config_base_t DEFAULT_INPUT_CONFIG = {
+ .sample_rate = k48000SamplingRate,
+ .channel_mask = AUDIO_CHANNEL_IN_STEREO,
+ .format = AUDIO_FORMAT_PCM_16_BIT
+ };
};
void AudioPolicyManagerTestDynamicPolicy::TearDown() {
- mManager->unregisterPolicyMixes(mAudioMixes);
+ clearPolicyMix();
AudioPolicyManagerTestWithConfigurationFile::TearDown();
}
@@ -1302,11 +1319,45 @@
void AudioPolicyManagerTestDynamicPolicy::clearPolicyMix() {
if (mManager != nullptr) {
+ mManager->stopInput(mLoopbackInputPortId);
mManager->unregisterPolicyMixes(mAudioMixes);
}
mAudioMixes.clear();
}
+void AudioPolicyManagerTestDynamicPolicy::addPolicyMixAndStartInputForLoopback(
+ int mixType, int mixFlag, audio_devices_t deviceType, std::string mixAddress,
+ const audio_config_t& audioConfig,
+ const std::vector<AudioMixMatchCriterion>& matchCriteria, audio_session_t session,
+ audio_config_base_t config, audio_input_flags_t inputFlags) {
+ ASSERT_EQ(NO_ERROR,
+ addPolicyMix(mixType, mixFlag, deviceType, mixAddress, audioConfig, matchCriteria));
+ if ((mixFlag & MIX_ROUTE_FLAG_LOOP_BACK) != MIX_ROUTE_FLAG_LOOP_BACK) {
+ return;
+ }
+
+ mTracker.reset(new RecordingActivityTracker());
+ struct audio_port_v7 extractionPort;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+ mixAddress, &extractionPort));
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_source_t source = AUDIO_SOURCE_REMOTE_SUBMIX;
+ audio_attributes_t attr = {
+ AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
+ std::string tags = "addr=" + mMixAddress;
+ audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
+ strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ ASSERT_NO_FATAL_FAILURE(
+ getInputForAttr(attr, &input, session, mTracker->getRiid(),
+ &selectedDeviceId, config.format, config.channel_mask,
+ config.sample_rate, inputFlags, &mLoopbackInputPortId));
+ ASSERT_EQ(NO_ERROR, mManager->startInput(mLoopbackInputPortId));
+ ASSERT_EQ(extractionPort.id, selectedDeviceId);
+
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+ mMixAddress, &mInjectionPort));
+}
+
TEST_F(AudioPolicyManagerTestDynamicPolicy, InitSuccess) {
// SetUp must finish with no assertions
}
@@ -1684,11 +1735,6 @@
public testing::WithParamInterface<DPTestParam> {
protected:
void SetUp() override;
- void TearDown() override;
-
- std::unique_ptr<RecordingActivityTracker> mTracker;
- struct audio_port_v7 mInjectionPort;
- audio_port_handle_t mPortId = AUDIO_PORT_HANDLE_NONE;
};
void AudioPolicyManagerTestDPPlaybackReRouting::SetUp() {
@@ -1702,34 +1748,10 @@
audioConfig.sample_rate = k48000SamplingRate;
DPTestParam param = GetParam();
- status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
- AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, param.mixCriteria);
- ASSERT_EQ(NO_ERROR, ret);
-
- struct audio_port_v7 extractionPort;
- ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SOURCE, AUDIO_DEVICE_IN_REMOTE_SUBMIX,
- mMixAddress, &extractionPort));
-
- audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
- audio_source_t source = AUDIO_SOURCE_REMOTE_SUBMIX;
- audio_attributes_t attr = {
- AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, source, AUDIO_FLAG_NONE, ""};
- std::string tags = "addr=" + mMixAddress;
- audio_io_handle_t input = AUDIO_PORT_HANDLE_NONE;
- strncpy(attr.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
- getInputForAttr(attr, &input, param.session, mTracker->getRiid(),
- &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
- k48000SamplingRate, AUDIO_INPUT_FLAG_NONE, &mPortId);
- ASSERT_EQ(NO_ERROR, mManager->startInput(mPortId));
- ASSERT_EQ(extractionPort.id, selectedDeviceId);
-
- ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
- mMixAddress, &mInjectionPort));
-}
-
-void AudioPolicyManagerTestDPPlaybackReRouting::TearDown() {
- mManager->stopInput(mPortId);
- AudioPolicyManagerTestDynamicPolicy::TearDown();
+ ASSERT_NO_FATAL_FAILURE(
+ addPolicyMixAndStartInputForLoopback(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_LOOP_BACK,
+ AUDIO_DEVICE_OUT_REMOTE_SUBMIX, mMixAddress, audioConfig, param.mixCriteria,
+ param.session));
}
TEST_P(AudioPolicyManagerTestDPPlaybackReRouting, PlaybackReRouting) {
@@ -1924,12 +1946,14 @@
// Add mix matching the test uid.
const int testUid = 12345;
const auto param = GetParam();
- status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, param.mixRouteFlags, param.deviceType,
- param.deviceAddress, audioConfig, {createUidCriterion(testUid)});
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_NO_FATAL_FAILURE(
+ addPolicyMixAndStartInputForLoopback(MIX_TYPE_PLAYERS, param.mixRouteFlags,
+ param.deviceType, param.deviceAddress, audioConfig,
+ {createUidCriterion(testUid)}));
- // Geting output for matching uid and mmap-ed stream should fail.
- audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+ // Getting output for matching uid and mmap-ed stream should fail.
+ audio_output_flags_t outputFlags =
+ (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(INVALID_OPERATION,
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
@@ -1942,11 +1966,12 @@
// Add mix matching the test uid.
const int testUid = 12345;
const auto param = GetParam();
- status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, param.mixRouteFlags, param.deviceType,
- param.deviceAddress, audioConfig, {createUidCriterion(testUid)});
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_NO_FATAL_FAILURE(
+ addPolicyMixAndStartInputForLoopback(MIX_TYPE_PLAYERS, param.mixRouteFlags,
+ param.deviceType,param.deviceAddress, audioConfig,
+ {createUidCriterion(testUid)}));
- // Geting output for matching uid should succeed for non-mmaped stream.
+ // Getting output for matching uid should succeed for non-mmaped stream.
audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_NONE;
ASSERT_EQ(NO_ERROR,
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
@@ -1957,23 +1982,57 @@
TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
MmapPlaybackStreamMatchingRenderDapMixSupportingMmapSucceeds) {
+ const std::string usbAddress = "card=1;device=0";
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_USB_DEVICE, AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+ usbAddress.c_str(), "", AUDIO_FORMAT_DEFAULT));
+ audio_port_v7 usbDevicePort;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_USB_DEVICE,
+ usbAddress, &usbDevicePort));
+
// Add render-only mix matching the test uid.
const int testUid = 12345;
// test_audio_policy_configuration.xml declares mmap-capable mix port
// for AUDIO_DEVICE_OUT_USB_DEVICE.
- status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
- AUDIO_DEVICE_OUT_USB_DEVICE, /*mixAddress=*/"",
- audioConfig, {createUidCriterion(testUid)});
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_EQ(NO_ERROR,
+ addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_USB_DEVICE, /*mixAddress=*/"",
+ audioConfig, {createUidCriterion(testUid)}));
- // Geting output for matching uid should succeed for mmaped stream, because matched mix
+ static const audio_output_flags_t mmapDirectFlags =
+ (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
+ // Getting output for matching uid should succeed for mmaped stream, because matched mix
// redirects to mmap capable device.
- audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+ audio_output_flags_t outputFlags = mmapDirectFlags;
ASSERT_EQ(NO_ERROR,
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
&outputFlags, &mSelectedDeviceId, &mPortId, {},
&mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ ASSERT_EQ(usbDevicePort.id, mSelectedDeviceId);
+ auto outputDesc = mManager->getOutputs().valueFor(mOutput);
+ ASSERT_NE(nullptr, outputDesc);
+ ASSERT_EQ(mmapDirectFlags, outputDesc->getFlags().output);
+
+ // After releasing the client, the output is closed. APM should reselect output for the policy
+ // mix.
+ mManager->releaseOutput(mPortId);
+ ASSERT_EQ(nullptr, mManager->getOutputs().valueFor(mOutput));
+ outputFlags = AUDIO_OUTPUT_FLAG_NONE;
+ mPortId = AUDIO_PORT_HANDLE_NONE;
+ ASSERT_EQ(NO_ERROR,
+ mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
+ createAttributionSourceState(testUid), &audioConfig,
+ &outputFlags, &mSelectedDeviceId, &mPortId, {},
+ &mOutputType, &mIsSpatialized, &mIsBitPerfect));
+ ASSERT_EQ(usbDevicePort.id, mSelectedDeviceId);
+ outputDesc = mManager->getOutputs().valueFor(mOutput);
+ ASSERT_NE(nullptr, outputDesc);
+ ASSERT_NE(mmapDirectFlags, outputDesc->getFlags().output);
+
+ ASSERT_EQ(NO_ERROR, mManager->setDeviceConnectionState(
+ AUDIO_DEVICE_OUT_USB_DEVICE, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+ usbAddress.c_str(), "", AUDIO_FORMAT_DEFAULT));
}
TEST_F(AudioPolicyManagerTestMMapPlaybackRerouting,
@@ -1981,14 +2040,15 @@
// Add render-only mix matching the test uid.
const int testUid = 12345;
// Per test_audio_policy_configuration.xml AUDIO_DEVICE_OUT_SPEAKER doesn't support mmap.
- status_t ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
- AUDIO_DEVICE_OUT_SPEAKER, /*mixAddress=*/"", audioConfig,
- {createUidCriterion(testUid)});
- ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_EQ(NO_ERROR,
+ addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_SPEAKER, /*mixAddress=*/"", audioConfig,
+ {createUidCriterion(testUid)}));
- // Geting output for matching uid should fail for mmaped stream, because
+ // Getting output for matching uid should fail for mmaped stream, because
// matched mix redirects to device which doesn't support mmap.
- audio_output_flags_t outputFlags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+ audio_output_flags_t outputFlags =
+ (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(INVALID_OPERATION,
mManager->getOutputForAttr(&attr, &mOutput, AUDIO_SESSION_NONE, &mStream,
createAttributionSourceState(testUid), &audioConfig,
@@ -2322,6 +2382,7 @@
static const std::string sCarBusNavigationOutput;
static const std::string sCarRearZoneOneOutput;
static const std::string sCarRearZoneTwoOutput;
+ static const std::string sCarBusMmapOutput;
};
const std::string AudioPolicyManagerCarTest::sCarConfig =
@@ -2335,6 +2396,8 @@
const std::string AudioPolicyManagerCarTest::sCarRearZoneTwoOutput = "bus200_audio_zone_2";
+const std::string AudioPolicyManagerCarTest::sCarBusMmapOutput = "bus8_mmap_out";
+
TEST_F(AudioPolicyManagerCarTest, InitSuccess) {
// SetUp must finish with no assertions.
}
@@ -2784,6 +2847,37 @@
ASSERT_EQ(navDevicePort.id, selectedDeviceId);
}
+TEST_F(AudioPolicyManagerCarTest, GetOutputForAttrForMMapWithPolicyMatched) {
+ status_t ret;
+ audio_config_t audioConfig = AUDIO_CONFIG_INITIALIZER;
+ audioConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+ audioConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ audioConfig.sample_rate = k48000SamplingRate;
+ std::vector<AudioMixMatchCriterion> mediaMatchCriteria = {
+ createUsageCriterion(AUDIO_USAGE_MEDIA, /*exclude=*/ false)};
+ ret = addPolicyMix(MIX_TYPE_PLAYERS, MIX_ROUTE_FLAG_RENDER,
+ AUDIO_DEVICE_OUT_BUS, sCarBusMmapOutput, audioConfig, mediaMatchCriteria);
+ ASSERT_EQ(NO_ERROR, ret);
+ ASSERT_EQ(NO_ERROR, ret);
+ audio_port_v7 mmapDevicePort;
+ ASSERT_TRUE(findDevicePort(AUDIO_PORT_ROLE_SINK, AUDIO_DEVICE_OUT_BUS,
+ sCarBusMmapOutput, &mmapDevicePort));
+ audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
+ audio_io_handle_t output;
+ audio_port_handle_t portId;
+ const audio_attributes_t mediaAttribute = {
+ AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA,
+ AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""};
+
+ getOutputForAttr(
+ &selectedDeviceId, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO,
+ k48000SamplingRate,
+ (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT),
+ &output, &portId, mediaAttribute);
+
+ ASSERT_EQ(mmapDevicePort.id, selectedDeviceId);
+}
+
class AudioPolicyManagerTVTest : public AudioPolicyManagerTestWithConfigurationFile {
protected:
std::string getConfigFile() override { return sTvConfig; }
diff --git a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
index 9e092c6..4efdf8a 100644
--- a/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_audio_policy_configuration.xml
@@ -56,7 +56,7 @@
</mixPort>
<mixPort name="hifi_output" role="source" flags="AUDIO_OUTPUT_FLAG_BIT_PERFECT"/>
<mixPort name="mmap_no_irq_out" role="source"
- flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+ flags="AUDIO_OUTPUT_FLAG_DIRECT AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
diff --git a/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
index d131ed8..d40ebfc 100644
--- a/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
+++ b/services/audiopolicy/tests/resources/test_car_ap_atmos_offload_configuration.xml
@@ -30,6 +30,7 @@
<item>bus5_alarm_out</item>
<item>bus6_notification_out</item>
<item>bus7_system_sound_out</item>
+ <item>bus8_mmap_out</item>
<!-- names with _audio_zone_# are used for defined an emulator rear seat audio zone
where each number # is the zone id number -->
<item>bus100_audio_zone_1</item>
@@ -96,6 +97,11 @@
samplingRates="48000"
channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
+ <mixPort name="mixport_bus8_mmap_out" role="source"
+ flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
<mixPort name="mixport_bus100_audio_zone_1" role="source">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000"
@@ -213,6 +219,16 @@
stepValueMB="100"/>
</gains>
</devicePort>
+ <devicePort tagName="bus8_mmap_out" role="sink" type="AUDIO_DEVICE_OUT_BUS"
+ address="bus8_mmap_out">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ <gains>
+ <gain name="" mode="AUDIO_GAIN_MODE_JOINT"
+ minValueMB="-3200" maxValueMB="600" defaultValueMB="0"
+ stepValueMB="100"/>
+ </gains>
+ </devicePort>
<devicePort tagName="bus100_audio_zone_1" role="sink" type="AUDIO_DEVICE_OUT_BUS"
address="bus100_audio_zone_1">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
@@ -295,6 +311,8 @@
sources="mixport_bus6_notification_out"/>
<route type="mix" sink="bus7_system_sound_out"
sources="mixport_bus7_system_sound_out"/>
+ <route type="mix" sink="bus8_mmap_out"
+ sources="mixport_bus8_mmap_out"/>
<route type="mix" sink="bus100_audio_zone_1" sources="mixport_bus100_audio_zone_1"/>
<route type="mix" sink="bus200_audio_zone_2" sources="mixport_bus200_audio_zone_2"/>
<route type="mix" sink="primary input"
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index feb9c75..3f43af5 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -123,6 +123,10 @@
android_atomic_write(level, &gLogLevel);
}
+int32_t format_as(CameraService::StatusInternal s) {
+ return fmt::underlying(s);
+}
+
// ----------------------------------------------------------------------------
static const std::string sDumpPermission("android.permission.DUMP");
@@ -510,8 +514,8 @@
}
if (newStatus == StatusInternal::NOT_PRESENT) {
- logDeviceRemoved(cameraId, fmt::sprintf("Device status changed from %d to %d", oldStatus,
- newStatus));
+ logDeviceRemoved(cameraId, fmt::format("Device status changed from {} to {}",
+ oldStatus, newStatus));
// Set the device status to NOT_PRESENT, clients will no longer be able to connect
// to this device until the status changes
@@ -537,8 +541,8 @@
removeStates(cameraId);
} else {
if (oldStatus == StatusInternal::NOT_PRESENT) {
- logDeviceAdded(cameraId, fmt::sprintf("Device status changed from %d to %d", oldStatus,
- newStatus));
+ logDeviceAdded(cameraId, fmt::format("Device status changed from {} to {}",
+ oldStatus, newStatus));
}
updateStatus(newStatus, cameraId);
}
@@ -578,9 +582,9 @@
if (updated) {
std::string idCombo = id + " : " + physicalId;
if (newStatus == StatusInternal::PRESENT) {
- logDeviceAdded(idCombo, fmt::sprintf("Device status changed to %d", newStatus));
+ logDeviceAdded(idCombo, fmt::format("Device status changed to {}", newStatus));
} else {
- logDeviceRemoved(idCombo, fmt::sprintf("Device status changed to %d", newStatus));
+ logDeviceRemoved(idCombo, fmt::format("Device status changed to {}", newStatus));
}
// Avoid calling getSystemCameraKind() with mStatusListenerLock held (b/141756275)
SystemCameraKind deviceKind = SystemCameraKind::PUBLIC;
@@ -1092,6 +1096,39 @@
}
}
+Status CameraService::injectSessionParams(
+ const std::string& cameraId,
+ const CameraMetadata& sessionParams) {
+ if (!checkCallingPermission(toString16(sCameraInjectExternalCameraPermission))) {
+ const int pid = CameraThreadState::getCallingPid();
+ const int uid = CameraThreadState::getCallingUid();
+ ALOGE("%s: Permission Denial: can't inject session params pid=%d, uid=%d",
+ __FUNCTION__, pid, uid);
+ return STATUS_ERROR(ERROR_PERMISSION_DENIED,
+ "Permission Denial: no permission to inject session params");
+ }
+
+ std::unique_ptr<AutoConditionLock> serviceLockWrapper =
+ AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
+
+ auto clientDescriptor = mActiveClientManager.get(cameraId);
+ if (clientDescriptor == nullptr) {
+ ALOGI("%s: No active client for camera id %s", __FUNCTION__, cameraId.c_str());
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+ "No active client for camera id %s", cameraId.c_str());
+ }
+
+ sp<BasicClient> clientSp = clientDescriptor->getValue();
+ status_t res = clientSp->injectSessionParams(sessionParams);
+
+ if (res != OK) {
+ return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+ "Error injecting session params into camera \"%s\": %s (%d)",
+ cameraId.c_str(), strerror(-res), res);
+ }
+ return Status::ok();
+}
+
std::vector<std::string> CameraService::findOriginalIdsForRemappedCameraId(
const std::string& inputCameraId, int clientUid) {
std::string packageName = getPackageNameFromUid(clientUid);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index cabac17..1487013 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -232,7 +232,11 @@
const hardware::CameraExtensionSessionStats& stats, std::string* sessionKey /*out*/);
virtual binder::Status remapCameraIds(const hardware::CameraIdRemapping&
- cameraIdRemapping);
+ cameraIdRemapping);
+
+ virtual binder::Status injectSessionParams(
+ const std::string& cameraId,
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams);
virtual binder::Status createDefaultRequest(const std::string& cameraId, int templateId,
/*out*/
@@ -412,6 +416,10 @@
// Stop the injection camera and restore to internal camera session.
virtual status_t stopInjection() = 0;
+ // Inject session parameters into an existing session.
+ virtual status_t injectSessionParams(
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams) = 0;
+
protected:
BasicClient(const sp<CameraService>& cameraService,
const sp<IBinder>& remoteCallback,
@@ -685,6 +693,8 @@
UNKNOWN = static_cast<int32_t>(hardware::ICameraServiceListener::STATUS_UNKNOWN)
};
+ friend int32_t format_as(StatusInternal s);
+
/**
* Container class for the state of each logical camera device, including: ID, status, and
* dependencies on other devices. The mapping of camera ID -> state saved in mCameraStates
diff --git a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
index b193be2..e403b97 100644
--- a/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
+++ b/services/camera/libcameraservice/aidl/VndkVersionMetadataTags.h
@@ -77,6 +77,7 @@
{34, {
ANDROID_CONTROL_AUTOFRAMING_AVAILABLE,
ANDROID_CONTROL_AVAILABLE_SETTINGS_OVERRIDES,
+ ANDROID_CONTROL_LOW_LIGHT_BOOST_INFO_LUMINANCE_RANGE,
ANDROID_FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL,
ANDROID_FLASH_SINGLE_STRENGTH_MAX_LEVEL,
ANDROID_FLASH_TORCH_STRENGTH_DEFAULT_LEVEL,
@@ -108,6 +109,7 @@
{34, {
ANDROID_CONTROL_AUTOFRAMING,
ANDROID_CONTROL_AUTOFRAMING_STATE,
+ ANDROID_CONTROL_LOW_LIGHT_BOOST_STATE,
ANDROID_CONTROL_SETTINGS_OVERRIDE,
ANDROID_CONTROL_SETTINGS_OVERRIDING_FRAME_NUMBER,
ANDROID_EXTENSION_CURRENT_TYPE,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 095d425..3488629 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -777,6 +777,60 @@
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);
@@ -1009,7 +1063,7 @@
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
"Camera %s: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
mCameraIdStr.c_str(), streamInfo.width, streamInfo.height, streamInfo.format,
- streamInfo.dataSpace, strerror(-err), err);
+ static_cast<int>(streamInfo.dataSpace), strerror(-err), err);
} else {
int i = 0;
for (auto& binder : binders) {
@@ -1106,7 +1160,8 @@
if (err != OK) {
res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
"Camera %s: Error creating output stream (%d x %d, fmt %x, dataSpace %x): %s (%d)",
- mCameraIdStr.c_str(), width, height, format, dataSpace, strerror(-err), err);
+ mCameraIdStr.c_str(), width, height, format, static_cast<int>(dataSpace),
+ strerror(-err), err);
} else {
// Can not add streamId to mStreamMap here, as the surface is deferred. Add it to
// a separate list to track. Once the deferred surface is set, this id will be
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index b2c9626..c2f7f56 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -109,6 +109,11 @@
/*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/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 4ed352d..dc9e0c1 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -383,5 +383,12 @@
return OK;
}
+status_t CameraOfflineSessionClient::injectSessionParams(
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams) {
+ ALOGV("%s: This client doesn't support the injecting session parameters camera.",
+ __FUNCTION__);
+ (void)sessionParams;
+ return OK;
+}
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index 8aad4e9..804498f 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -120,6 +120,8 @@
status_t injectCamera(const std::string& injectedCamId,
sp<CameraProviderManager> manager) override;
status_t stopInjection() override;
+ status_t injectSessionParams(
+ const hardware::camera2::impl::CameraMetadataNative& sessionParams) override;
private:
mutable Mutex mBinderSerializationLock;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index c730b14..a126f61 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -509,6 +509,12 @@
return mDevice->stopInjection();
}
+template <typename TClientBase>
+status_t Camera2ClientBase<TClientBase>::injectSessionParams(
+ const CameraMetadata& sessionParams) {
+ return mDevice->injectSessionParams(sessionParams);
+}
+
template class Camera2ClientBase<CameraService::Client>;
template class Camera2ClientBase<CameraDeviceClientBase>;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index bac4af8..2bb90d9 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -18,6 +18,7 @@
#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H
#include "common/CameraDeviceBase.h"
+#include "camera/CameraMetadata.h"
#include "camera/CaptureResult.h"
#include "utils/CameraServiceProxyWrapper.h"
#include "CameraServiceWatchdog.h"
@@ -136,6 +137,8 @@
sp<CameraProviderManager> manager) override;
status_t stopInjection() override;
+ status_t injectSessionParams(const CameraMetadata& sessionParams) override;
+
protected:
// The PID provided in the constructor call
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 983b2c1..cfc41c3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -509,6 +509,10 @@
*/
virtual status_t stopInjection() = 0;
+ // Inject session parameters into an existing client.
+ virtual status_t injectSessionParams(
+ const CameraMetadata& sessionParams) = 0;
+
protected:
bool mImageDumpMask = 0;
std::vector<int64_t> mStreamUseCaseOverrides;
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index fbb5e1b..1ba3de4 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -442,6 +442,23 @@
return OK;
}
+status_t CameraProviderManager::getSessionCharacteristics(const std::string& id,
+ const SessionConfiguration &configuration, bool overrideForPerfClass,
+ metadataGetter getMetadata,
+ 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;
+ }
+
+ return deviceInfo->getSessionCharacteristics(configuration,
+ overrideForPerfClass, getMetadata, sessionCharacteristics);
+}
+
status_t CameraProviderManager::getCameraIdIPCTransport(const std::string &id,
IPCTransport *providerTransport) const {
std::lock_guard<std::mutex> lock(mInterfaceMutex);
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 6142a71..53a2102 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -317,6 +317,14 @@
bool *status /*out*/) const;
/**
+ * Get session characteristics for a particular session.
+ */
+ status_t getSessionCharacteristics(const std::string& id,
+ const SessionConfiguration &configuration,
+ bool overrideForPerfClass, camera3::metadataGetter getMetadata,
+ CameraMetadata* sessionCharacteristics /*out*/) const;
+
+ /**
* Return the highest supported device interface version for this ID
*/
status_t getHighestSupportedVersion(const std::string &id,
@@ -632,6 +640,15 @@
bool * /*status*/) {
return INVALID_OPERATION;
}
+
+ virtual status_t getSessionCharacteristics(
+ const SessionConfiguration &/*configuration*/,
+ bool /*overrideForPerfClass*/,
+ camera3::metadataGetter /*getMetadata*/,
+ CameraMetadata* /*sessionCharacteristics*/) {
+ return INVALID_OPERATION;
+ }
+
virtual status_t filterSmallJpegSizes() = 0;
virtual void notifyDeviceStateChange(int64_t /*newState*/) {}
virtual status_t createDefaultRequest(
diff --git a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
index a64c9d7..d773af3 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.cpp
@@ -900,6 +900,54 @@
return res;
}
+status_t AidlProviderInfo::AidlDeviceInfo3::getSessionCharacteristics(
+ const SessionConfiguration &configuration, bool overrideForPerfClass,
+ camera3::metadataGetter getMetadata, CameraMetadata *sessionCharacteristics) {
+ camera::device::StreamConfiguration streamConfiguration;
+ bool earlyExit = false;
+ auto res = SessionConfigurationUtils::convertToHALStreamCombination(configuration,
+ mId, mCameraCharacteristics, mCompositeJpegRDisabled, getMetadata,
+ mPhysicalIds, streamConfiguration, overrideForPerfClass, mProviderTagid,
+ /*checkSessionParams*/true, &earlyExit);
+
+ if (!res.isOk()) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (earlyExit) {
+ return BAD_VALUE;
+ }
+
+ const std::shared_ptr<camera::device::ICameraDevice> interface =
+ startDeviceInterface();
+
+ if (interface == nullptr) {
+ return DEAD_OBJECT;
+ }
+
+ aidl::android::hardware::camera::device::CameraMetadata chars;
+ ::ndk::ScopedAStatus ret =
+ interface->getSessionCharacteristics(streamConfiguration, &chars);
+ std::vector<uint8_t> &metadata = chars.metadata;
+
+ camera_metadata_t *buffer = reinterpret_cast<camera_metadata_t*>(metadata.data());
+ size_t expectedSize = metadata.size();
+ int resV = validate_camera_metadata_structure(buffer, &expectedSize);
+ if (resV == OK || resV == CAMERA_METADATA_VALIDATION_SHIFTED) {
+ set_camera_metadata_vendor_id(buffer, mProviderTagid);
+ *sessionCharacteristics = buffer;
+ } else {
+ ALOGE("%s: Malformed camera metadata received from HAL", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (!ret.isOk()) {
+ ALOGE("%s: Unexpected binder error: %s", __FUNCTION__, ret.getMessage());
+ return mapToStatusT(ret);
+ }
+ return OK;
+}
+
status_t AidlProviderInfo::convertToAidlHALStreamCombinationAndCameraIdsLocked(
const std::vector<CameraIdAndSessionConfiguration> &cameraIdsAndSessionConfigs,
const std::set<std::string>& perfClassPrimaryCameraIds,
diff --git a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h
index cc5101a..0bfa7d4 100644
--- a/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h
+++ b/services/camera/libcameraservice/common/aidl/AidlProviderInfo.h
@@ -134,6 +134,11 @@
camera3::camera_request_template_t templateId,
camera_metadata_t** metadata) override;
+ virtual status_t getSessionCharacteristics(
+ const SessionConfiguration &/*configuration*/,
+ bool overrideForPerfClass, camera3::metadataGetter /*getMetadata*/,
+ CameraMetadata *sessionCharacteristics /*sessionCharacteristics*/);
+
std::shared_ptr<aidl::android::hardware::camera::device::ICameraDevice>
startDeviceInterface();
};
diff --git a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h
index f53db7f..869bba0 100644
--- a/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h
+++ b/services/camera/libcameraservice/common/hidl/HidlProviderInfo.h
@@ -107,6 +107,7 @@
const SessionConfiguration &/*configuration*/,
bool overrideForPerfClass, bool checkSessionParams,
bool *status/*status*/);
+
sp<hardware::camera::device::V3_2::ICameraDevice> startDeviceInterface();
};
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 0a5b0c4..28b2d78 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -80,7 +80,7 @@
namespace flags = com::android::internal::camera::flags;
namespace android {
-
+namespace flags = com::android::internal::camera::flags;
Camera3Device::Camera3Device(std::shared_ptr<CameraServiceProxyWrapper>& cameraServiceProxyWrapper,
const std::string &id, bool overrideForPerfClass, bool overrideToPortrait,
bool legacyClient):
@@ -3590,12 +3590,16 @@
for (size_t i = 0; i < mNextRequests.size(); i++) {
auto& nextRequest = mNextRequests.editItemAt(i);
sp<CaptureRequest> captureRequest = nextRequest.captureRequest;
+ captureRequest->mTestPatternChanged = overrideTestPattern(captureRequest);
// Do not override rotate&crop for stream configurations that include
// SurfaceViews(HW_COMPOSER) output, unless mOverrideToPortrait is set.
// The display rotation there will be compensated by NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY
captureRequest->mRotateAndCropChanged = (mComposerOutput && !mOverrideToPortrait) ? false :
overrideAutoRotateAndCrop(captureRequest);
captureRequest->mAutoframingChanged = overrideAutoframing(captureRequest);
+ if (flags::inject_session_params()) {
+ injectSessionParams(captureRequest, mInjectedSessionParams);
+ }
}
// 'mNextRequests' will at this point contain either a set of HFR batched requests
@@ -3619,7 +3623,10 @@
if (res == OK) {
sp<Camera3Device> parent = mParent.promote();
if (parent != nullptr) {
- mReconfigured |= parent->reconfigureCamera(mLatestSessionParams, mStatusId);
+ if (parent->reconfigureCamera(mLatestSessionParams, mStatusId)) {
+ mForceNewRequestAfterReconfigure = true;
+ mReconfigured = true;
+ }
}
if (mNextRequests[0].captureRequest->mInputStream != nullptr) {
@@ -3742,7 +3749,6 @@
bool triggersMixedIn = (triggerCount > 0 || mPrevTriggers > 0);
mPrevTriggers = triggerCount;
- bool testPatternChanged = overrideTestPattern(captureRequest);
bool settingsOverrideChanged = overrideSettingsOverride(captureRequest);
// If the request is the same as last, or we had triggers now or last time or
@@ -3751,12 +3757,20 @@
(mPrevRequest != captureRequest || triggersMixedIn ||
captureRequest->mRotateAndCropChanged ||
captureRequest->mAutoframingChanged ||
- testPatternChanged || settingsOverrideChanged) &&
+ captureRequest->mTestPatternChanged || settingsOverrideChanged ||
+ (flags::inject_session_params() && mForceNewRequestAfterReconfigure)) &&
// Request settings are all the same within one batch, so only treat the first
// request in a batch as new
!(batchedRequest && i > 0);
+
if (newRequest) {
std::set<std::string> cameraIdsWithZoom;
+
+ if (flags::inject_session_params() && mForceNewRequestAfterReconfigure) {
+ // This only needs to happen once.
+ mForceNewRequestAfterReconfigure = false;
+ }
+
/**
* HAL workaround:
* Insert a fake trigger ID if a trigger is set but no trigger ID is
@@ -4904,6 +4918,45 @@
return false;
}
+void Camera3Device::RequestThread::injectSessionParams(
+ const sp<CaptureRequest> &request,
+ const CameraMetadata& injectedSessionParams) {
+ CameraMetadata &requestMetadata = request->mSettingsList.begin()->metadata;
+ uint32_t tag_section;
+ camera_metadata_ro_entry entry;
+ for (auto tag : mSessionParamKeys) {
+ tag_section = tag >> 16;
+ if (tag_section < VENDOR_SECTION) {
+ // Only allow injection of vendor tags.
+ continue;
+ }
+ entry = injectedSessionParams.find(tag);
+ if (entry.count > 0) {
+ requestMetadata.update(entry);
+ }
+ }
+}
+
+status_t Camera3Device::RequestThread::setInjectedSessionParams(
+ const CameraMetadata& injectedSessionParams) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mTriggerMutex);
+ mInjectedSessionParams = injectedSessionParams;
+ return OK;
+}
+
+status_t Camera3Device::injectSessionParams(const CameraMetadata& injectedSessionParams) {
+ ATRACE_CALL();
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ if (mRequestThread == nullptr) {
+ return INVALID_OPERATION;
+ }
+
+ return mRequestThread->setInjectedSessionParams(injectedSessionParams);
+}
+
bool Camera3Device::RequestThread::overrideTestPattern(
const sp<CaptureRequest> &request) {
ATRACE_CALL();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index bde7e1b..e32a36f 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -338,6 +338,11 @@
*/
status_t stopInjection();
+ /**
+ * Inject session params into the current client.
+ */
+ status_t injectSessionParams(const CameraMetadata& sessionParams);
+
protected:
status_t disconnectImpl();
static status_t removeFwkOnlyRegionKeys(CameraMetadata *request);
@@ -385,7 +390,6 @@
int mOperatingMode;
// Current session wide parameters
hardware::camera2::impl::CameraMetadataNative mSessionParams;
-
// Constant to use for no set operating mode
static const int NO_MODE = -1;
@@ -651,6 +655,8 @@
bool mAutoframingAuto;
// Indicates that the auto framing value within 'mSettingsList' was modified
bool mAutoframingChanged = false;
+ // Indicates that the camera test pattern setting is modified
+ bool mTestPatternChanged = false;
// Whether this capture request has its zoom ratio set to 1.0x before
// the framework overrides it for camera HAL consumption.
@@ -1005,6 +1011,12 @@
status_t setHalInterface(sp<HalInterface> newHalInterface);
+ status_t setInjectedSessionParams(const CameraMetadata& sessionParams);
+
+ void injectSessionParams(
+ const sp<CaptureRequest> &request,
+ const CameraMetadata& injectedSessionParams);
+
protected:
virtual bool threadLoop();
@@ -1185,6 +1197,8 @@
Vector<int32_t> mSessionParamKeys;
CameraMetadata mLatestSessionParams;
+ CameraMetadata mInjectedSessionParams;
+ bool mForceNewRequestAfterReconfigure;
std::map<int32_t, std::set<std::string>> mGroupIdPhysicalCameraMap;
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index c59138c..152687e 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -84,10 +84,10 @@
status_t res = getEndpointUsage(&consumerUsage);
if (res != OK) consumerUsage = 0;
- lines << fmt::sprintf(" State: %d\n", mState);
+ lines << fmt::sprintf(" State: %d\n", static_cast<int>(mState));
lines << fmt::sprintf(" Dims: %d x %d, format 0x%x, dataspace 0x%x\n",
camera_stream::width, camera_stream::height,
- camera_stream::format, camera_stream::data_space);
+ camera_stream::format, static_cast<int>(camera_stream::data_space));
lines << fmt::sprintf(" Max size: %zu\n", mMaxSize);
lines << fmt::sprintf(" Combined usage: 0x%" PRIx64 ", max HAL buffers: %d\n",
mUsage | consumerUsage, camera_stream::max_buffers);
diff --git a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
index 467707e..a53d26d 100644
--- a/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
+++ b/services/camera/libcameraservice/tests/CameraProviderManagerTest.cpp
@@ -309,6 +309,12 @@
bool*) override {
return ndk::ScopedAStatus::ok();
}
+
+ ::ndk::ScopedAStatus getSessionCharacteristics(
+ const ::aidl::android::hardware::camera::device::StreamConfiguration&,
+ ::aidl::android::hardware::camera::device::CameraMetadata*) override {
+ return ndk::ScopedAStatus::ok();
+ }
};
/**
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
index 3be8e15..11ef9b7 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.cpp
@@ -623,7 +623,7 @@
if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
if (dataSpace != streamInfo.dataSpace) {
std::string msg = fmt::sprintf("Camera %s:Surface dataSpace doesn't match: %d vs %d",
- logicalCameraId.c_str(), dataSpace, streamInfo.dataSpace);
+ logicalCameraId.c_str(), static_cast<int>(dataSpace), static_cast<int>(streamInfo.dataSpace));
ALOGE("%s: %s", __FUNCTION__, msg.c_str());
return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.c_str());
}
@@ -1133,7 +1133,7 @@
}
void filterParameters(const CameraMetadata& src, const CameraMetadata& deviceInfo,
- int vendorTagId, CameraMetadata& dst) {
+ metadata_vendor_id_t vendorTagId, CameraMetadata& dst) {
const CameraMetadata params(src);
camera_metadata_ro_entry_t availableSessionKeys = deviceInfo.find(
ANDROID_REQUEST_AVAILABLE_SESSION_KEYS);
diff --git a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
index 29e3eca..5b2ea5c 100644
--- a/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
+++ b/services/camera/libcameraservice/utils/SessionConfigurationUtils.h
@@ -177,7 +177,7 @@
aidl::android::hardware::camera::device::RequestTemplate* tempId /*out*/);
void filterParameters(const CameraMetadata& src, const CameraMetadata& deviceInfo,
- int vendorTagId, CameraMetadata& dst);
+ metadata_vendor_id_t vendorTagId, CameraMetadata& dst);
constexpr int32_t MAX_SURFACES_PER_STREAM = 4;
diff --git a/services/camera/virtualcamera/Android.bp b/services/camera/virtualcamera/Android.bp
index c8fa84e..cb4e10f 100644
--- a/services/camera/virtualcamera/Android.bp
+++ b/services/camera/virtualcamera/Android.bp
@@ -54,6 +54,7 @@
"util/EglProgram.cc",
"util/EglSurfaceTexture.cc",
"util/EglUtil.cc",
+ "util/Permissions.cc"
],
defaults: [
"libvirtualcamera_defaults",
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.cc b/services/camera/virtualcamera/VirtualCameraDevice.cc
index ec72ee3..84f721b 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.cc
+++ b/services/camera/virtualcamera/VirtualCameraDevice.cc
@@ -119,9 +119,10 @@
const std::vector<SupportedStreamConfiguration>& supportedInputConfig) {
if (!std::all_of(supportedInputConfig.begin(), supportedInputConfig.end(),
[](const SupportedStreamConfiguration& config) {
- return config.pixelFormat == Format::YUV_420_888;
+ return isFormatSupportedForInput(
+ config.width, config.height, config.pixelFormat);
})) {
- ALOGE("%s: input configuration contains unsupported pixel format", __func__);
+ ALOGE("%s: input configuration contains unsupported format", __func__);
return std::nullopt;
}
@@ -322,7 +323,7 @@
ALOGV("%s", __func__);
*_aidl_return = ndk::SharedRefBase::make<VirtualCameraSession>(
- *this, in_callback, mVirtualCameraClientCallback);
+ sharedFromThis(), in_callback, mVirtualCameraClientCallback);
return ndk::ScopedAStatus::ok();
};
@@ -367,6 +368,13 @@
return std::string(kDevicePathPrefix) + std::to_string(mCameraId);
}
+std::shared_ptr<VirtualCameraDevice> VirtualCameraDevice::sharedFromThis() {
+ // SharedRefBase which BnCameraDevice inherits from breaks
+ // std::enable_shared_from_this. This is recommended replacement for
+ // shared_from_this() per documentation in binder_interface_utils.h.
+ return ref<VirtualCameraDevice>();
+}
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.h b/services/camera/virtualcamera/VirtualCameraDevice.h
index ee7a3a7..402de6c 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.h
+++ b/services/camera/virtualcamera/VirtualCameraDevice.h
@@ -98,6 +98,8 @@
uint32_t getCameraId() const { return mCameraId; }
private:
+ std::shared_ptr<VirtualCameraDevice> sharedFromThis();
+
const uint32_t mCameraId;
const std::shared_ptr<
::aidl::android::companion::virtualcamera::IVirtualCameraCallback>
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index 8621160..79c91ef 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -19,6 +19,7 @@
#include <chrono>
#include <cstddef>
+#include <cstdint>
#include <future>
#include <memory>
#include <mutex>
@@ -38,6 +39,7 @@
#include "android-base/thread_annotations.h"
#include "android/binder_auto_utils.h"
#include "android/hardware_buffer.h"
+#include "ui/GraphicBuffer.h"
#include "util/EglFramebuffer.h"
#include "util/JpegUtil.h"
#include "util/MetadataBuilder.h"
@@ -109,6 +111,45 @@
return msg;
}
+std::shared_ptr<EglFrameBuffer> allocateTemporaryFramebuffer(
+ EGLDisplay eglDisplay, const uint width, const int height) {
+ const AHardwareBuffer_Desc desc{
+ .width = static_cast<uint32_t>(width),
+ .height = static_cast<uint32_t>(height),
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420,
+ .usage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER |
+ AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ .rfu0 = 0,
+ .rfu1 = 0};
+
+ AHardwareBuffer* hwBufferPtr;
+ int status = AHardwareBuffer_allocate(&desc, &hwBufferPtr);
+ if (status != NO_ERROR) {
+ ALOGE(
+ "%s: Failed to allocate hardware buffer for temporary framebuffer: %d",
+ __func__, status);
+ return nullptr;
+ }
+
+ return std::make_shared<EglFrameBuffer>(
+ eglDisplay,
+ std::shared_ptr<AHardwareBuffer>(hwBufferPtr, AHardwareBuffer_release));
+}
+
+bool isYuvFormat(const PixelFormat pixelFormat) {
+ switch (static_cast<android_pixel_format_t>(pixelFormat)) {
+ case HAL_PIXEL_FORMAT_YCBCR_422_I:
+ case HAL_PIXEL_FORMAT_YCBCR_422_SP:
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCBCR_420_888:
+ return true;
+ default:
+ return false;
+ }
+}
+
} // namespace
CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
@@ -218,7 +259,10 @@
ALOGV("Render thread starting");
mEglDisplayContext = std::make_unique<EglDisplayContext>();
- mEglTextureProgram = std::make_unique<EglTextureProgram>();
+ mEglTextureYuvProgram =
+ std::make_unique<EglTextureProgram>(EglTextureProgram::TextureFormat::YUV);
+ mEglTextureRgbProgram = std::make_unique<EglTextureProgram>(
+ EglTextureProgram::TextureFormat::RGBA);
mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(mInputSurfaceWidth,
mInputSurfaceHeight);
mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());
@@ -371,6 +415,22 @@
return cameraStatus(Status::INTERNAL_ERROR);
}
+ // 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(
+ mEglDisplayContext->getEglDisplay(), stream->width, stream->height);
+ if (framebuffer == nullptr) {
+ ALOGE("Failed to allocate temporary framebuffer for JPEG compression");
+ return cameraStatus(Status::INTERNAL_ERROR);
+ }
+
+ // Render into temporary framebuffer.
+ ndk::ScopedAStatus status = renderIntoEglFramebuffer(*framebuffer);
+ if (!status.isOk()) {
+ ALOGE("Failed to render input texture into temporary framebuffer");
+ return status;
+ }
+
AHardwareBuffer_Planes planes_info;
int32_t rawFence = fence != nullptr ? fence->get() : -1;
@@ -382,10 +442,21 @@
return cameraStatus(Status::INTERNAL_ERROR);
}
- sp<GraphicBuffer> gBuffer = mEglSurfaceTexture->getCurrentBuffer();
+ std::shared_ptr<AHardwareBuffer> inHwBuffer = framebuffer->getHardwareBuffer();
+ GraphicBuffer* gBuffer = GraphicBuffer::fromAHardwareBuffer(inHwBuffer.get());
+
bool compressionSuccess = true;
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");
@@ -434,31 +505,7 @@
return cameraStatus(Status::ILLEGAL_ARGUMENT);
}
- // Wait for fence to clear.
- if (fence != nullptr && fence->isValid()) {
- status_t ret = fence->wait(kAcquireFenceTimeout.count());
- if (ret != 0) {
- ALOGE(
- "Timeout while waiting for the acquire fence for buffer %d"
- " for streamId %d",
- bufferId, streamId);
- return cameraStatus(Status::INTERNAL_ERROR);
- }
- }
-
- mEglDisplayContext->makeCurrent();
- framebuffer->beforeDraw();
-
- if (mEglSurfaceTexture->getCurrentBuffer() == nullptr) {
- // If there's no current buffer, nothing was written to the surface and
- // texture is not initialized yet. Let's render the framebuffer black
- // instead of rendering the texture.
- glClearColor(0.0f, 0.5f, 0.5f, 0.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- } else {
- mEglTextureProgram->draw(mEglSurfaceTexture->updateTexture());
- }
- framebuffer->afterDraw();
+ ndk::ScopedAStatus status = renderIntoEglFramebuffer(*framebuffer, fence);
const std::chrono::nanoseconds after =
std::chrono::duration_cast<std::chrono::nanoseconds>(
@@ -470,6 +517,43 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoEglFramebuffer(
+ EglFrameBuffer& framebuffer, sp<Fence> fence) {
+ ALOGV("%s", __func__);
+ // Wait for fence to clear.
+ if (fence != nullptr && fence->isValid()) {
+ status_t ret = fence->wait(kAcquireFenceTimeout.count());
+ if (ret != 0) {
+ ALOGE("Timeout while waiting for the acquire fence for buffer");
+ return cameraStatus(Status::INTERNAL_ERROR);
+ }
+ }
+
+ mEglDisplayContext->makeCurrent();
+ framebuffer.beforeDraw();
+
+ sp<GraphicBuffer> textureBuffer = mEglSurfaceTexture->getCurrentBuffer();
+ if (textureBuffer == nullptr) {
+ // If there's no current buffer, nothing was written to the surface and
+ // texture is not initialized yet. Let's render the framebuffer black
+ // instead of rendering the texture.
+ glClearColor(0.0f, 0.5f, 0.5f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ } else {
+ const bool renderSuccess =
+ isYuvFormat(static_cast<PixelFormat>(textureBuffer->getPixelFormat()))
+ ? mEglTextureYuvProgram->draw(mEglSurfaceTexture->updateTexture())
+ : mEglTextureRgbProgram->draw(mEglSurfaceTexture->updateTexture());
+ if (!renderSuccess) {
+ ALOGE("%s: Failed to render texture", __func__);
+ return cameraStatus(Status::INTERNAL_ERROR);
+ }
+ }
+ framebuffer.afterDraw();
+
+ return ndk::ScopedAStatus::ok();
+}
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.h b/services/camera/virtualcamera/VirtualCameraRenderThread.h
index a4374e6..b3aaed8 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.h
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.h
@@ -24,7 +24,9 @@
#include "VirtualCameraSessionContext.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"
@@ -135,6 +137,13 @@
ndk::ScopedAStatus renderIntoImageStreamBuffer(int streamId, int bufferId,
sp<Fence> fence = nullptr);
+ // Render current image into provided EglFramebuffer.
+ // If fence is specified, this function will block until the fence is cleared
+ // before writing to the buffer.
+ // Always called on the render thread.
+ ndk::ScopedAStatus renderIntoEglFramebuffer(EglFrameBuffer& framebuffer,
+ sp<Fence> fence = nullptr);
+
// Camera callback
const std::shared_ptr<
::aidl::android::hardware::camera::device::ICameraDeviceCallback>
@@ -156,7 +165,8 @@
// EGL helpers - constructed and accessed only from rendering thread.
std::unique_ptr<EglDisplayContext> mEglDisplayContext;
- std::unique_ptr<EglTextureProgram> mEglTextureProgram;
+ std::unique_ptr<EglTextureProgram> mEglTextureYuvProgram;
+ std::unique_ptr<EglTextureProgram> mEglTextureRgbProgram;
std::unique_ptr<EglSurfaceTexture> mEglSurfaceTexture;
std::promise<sp<Surface>> mInputSurfacePromise;
diff --git a/services/camera/virtualcamera/VirtualCameraService.cc b/services/camera/virtualcamera/VirtualCameraService.cc
index 08bfff7..370a5a8 100644
--- a/services/camera/virtualcamera/VirtualCameraService.cc
+++ b/services/camera/virtualcamera/VirtualCameraService.cc
@@ -31,6 +31,7 @@
#include "android/binder_auto_utils.h"
#include "android/binder_libbinder.h"
#include "binder/Status.h"
+#include "util/Permissions.h"
#include "util/Util.h"
using ::android::binder::Status;
@@ -54,6 +55,8 @@
* enable_test_camera
* disable_test_camera
)";
+constexpr char kCreateVirtualDevicePermission[] =
+ "android.permission.CREATE_VIRTUAL_DEVICE";
ndk::ScopedAStatus validateConfiguration(
const VirtualCameraConfiguration& configuration) {
@@ -79,17 +82,26 @@
} // namespace
VirtualCameraService::VirtualCameraService(
- std::shared_ptr<VirtualCameraProvider> virtualCameraProvider)
- : mVirtualCameraProvider(virtualCameraProvider) {
+ std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
+ const PermissionsProxy& permissionProxy)
+ : mVirtualCameraProvider(virtualCameraProvider),
+ mPermissionProxy(permissionProxy) {
}
ndk::ScopedAStatus VirtualCameraService::registerCamera(
const ::ndk::SpAIBinder& token,
const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
+ if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
+ ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
+ getpid(), getuid(), kCreateVirtualDevicePermission);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
+ }
+
if (_aidl_return == nullptr) {
return ndk::ScopedAStatus::fromServiceSpecificError(
Status::EX_ILLEGAL_ARGUMENT);
}
+
*_aidl_return = true;
auto status = validateConfiguration(configuration);
@@ -127,6 +139,12 @@
ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
const ::ndk::SpAIBinder& token) {
+ if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
+ ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
+ getpid(), getuid(), kCreateVirtualDevicePermission);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
+ }
+
std::lock_guard lock(mLock);
auto it = mTokenToCameraName.find(token);
@@ -145,6 +163,12 @@
ndk::ScopedAStatus VirtualCameraService::getCameraId(
const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
+ if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
+ ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
+ getpid(), getuid(), kCreateVirtualDevicePermission);
+ return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
+ }
+
if (_aidl_return == nullptr) {
return ndk::ScopedAStatus::fromServiceSpecificError(
Status::EX_ILLEGAL_ARGUMENT);
diff --git a/services/camera/virtualcamera/VirtualCameraService.h b/services/camera/virtualcamera/VirtualCameraService.h
index b68d43a..d573986 100644
--- a/services/camera/virtualcamera/VirtualCameraService.h
+++ b/services/camera/virtualcamera/VirtualCameraService.h
@@ -24,6 +24,7 @@
#include "VirtualCameraDevice.h"
#include "VirtualCameraProvider.h"
#include "aidl/android/companion/virtualcamera/BnVirtualCameraService.h"
+#include "util/Permissions.h"
namespace android {
namespace companion {
@@ -34,7 +35,8 @@
: public aidl::android::companion::virtualcamera::BnVirtualCameraService {
public:
VirtualCameraService(
- std::shared_ptr<VirtualCameraProvider> virtualCameraProvider);
+ std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
+ const PermissionsProxy& permissionProxy = PermissionsProxy::get());
// Register camera corresponding to the binder token.
ndk::ScopedAStatus registerCamera(
@@ -68,6 +70,8 @@
std::shared_ptr<VirtualCameraProvider> mVirtualCameraProvider;
+ const PermissionsProxy& mPermissionProxy;
+
std::mutex mLock;
struct BinderTokenHash {
std::size_t operator()(const ::ndk::SpAIBinder& key) const {
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index 9e15871..47780d8 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -153,7 +153,7 @@
} // namespace
VirtualCameraSession::VirtualCameraSession(
- VirtualCameraDevice& cameraDevice,
+ std::shared_ptr<VirtualCameraDevice> cameraDevice,
std::shared_ptr<ICameraDeviceCallback> cameraDeviceCallback,
std::shared_ptr<IVirtualCameraCallback> virtualCameraClientCallback)
: mCameraDevice(cameraDevice),
@@ -201,6 +201,12 @@
return cameraStatus(Status::ILLEGAL_ARGUMENT);
}
+ std::shared_ptr<VirtualCameraDevice> virtualCamera = mCameraDevice.lock();
+ if (virtualCamera == nullptr) {
+ ALOGW("%s: configure called on already unregistered camera", __func__);
+ return cameraStatus(Status::CAMERA_DISCONNECTED);
+ }
+
mSessionContext.removeStreamsNotInStreamConfiguration(
in_requestedConfiguration);
@@ -213,7 +219,7 @@
int inputWidth;
int inputHeight;
- if (!mCameraDevice.isStreamCombinationSupported(in_requestedConfiguration)) {
+ if (!virtualCamera->isStreamCombinationSupported(in_requestedConfiguration)) {
ALOGE("%s: Requested stream configuration is not supported", __func__);
return cameraStatus(Status::ILLEGAL_ARGUMENT);
}
diff --git a/services/camera/virtualcamera/VirtualCameraSession.h b/services/camera/virtualcamera/VirtualCameraSession.h
index 50962e5..82a7a34 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.h
+++ b/services/camera/virtualcamera/VirtualCameraSession.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERASESSION_H
#define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERASESSION_H
+#include <atomic>
#include <memory>
#include <set>
@@ -46,7 +47,7 @@
// When virtualCameraClientCallback is null, the input surface will be filled
// with test pattern.
VirtualCameraSession(
- VirtualCameraDevice& mCameraDevice,
+ std::shared_ptr<VirtualCameraDevice> mCameraDevice,
std::shared_ptr<
::aidl::android::hardware::camera::device::ICameraDeviceCallback>
cameraDeviceCallback,
@@ -116,7 +117,7 @@
const ::aidl::android::hardware::camera::device::CaptureRequest& request)
EXCLUDES(mLock);
- VirtualCameraDevice& mCameraDevice;
+ std::weak_ptr<VirtualCameraDevice> mCameraDevice;
mutable std::mutex mLock;
diff --git a/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl b/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl
index d9b90a6..b155147 100644
--- a/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl
+++ b/services/camera/virtualcamera/aidl/android/companion/virtualcamera/Format.aidl
@@ -24,5 +24,6 @@
@Backing(type="int")
enum Format {
UNKNOWN = 0,
+ RGBA_8888 = 1,
YUV_420_888 = 0x23,
}
diff --git a/services/camera/virtualcamera/main.cc b/services/camera/virtualcamera/main.cc
index b7e9c38..43b0219 100644
--- a/services/camera/virtualcamera/main.cc
+++ b/services/camera/virtualcamera/main.cc
@@ -49,16 +49,16 @@
auto aidlBinder = defaultProvider->asBinder();
AIBinder_forceDowngradeToLocalStability(aidlBinder.get());
- binder_exception_t ret =
- AServiceManager_addService(aidlBinder.get(), serviceName.c_str());
+ binder_exception_t ret = AServiceManager_registerLazyService(
+ aidlBinder.get(), serviceName.c_str());
LOG_ALWAYS_FATAL_IF(
ret != EX_NONE,
"Error while registering virtual camera provider service: %d", ret);
std::shared_ptr<VirtualCameraService> virtualCameraService =
ndk::SharedRefBase::make<VirtualCameraService>(defaultProvider);
- ret = AServiceManager_addService(virtualCameraService->asBinder().get(),
- kVirtualCameraServiceName);
+ ret = AServiceManager_registerLazyService(
+ virtualCameraService->asBinder().get(), kVirtualCameraServiceName);
LOG_ALWAYS_FATAL_IF(ret != EX_NONE,
"Error while registering virtual camera service: %d", ret);
diff --git a/services/camera/virtualcamera/tests/EglUtilTest.cc b/services/camera/virtualcamera/tests/EglUtilTest.cc
index d0b7218..589e312 100644
--- a/services/camera/virtualcamera/tests/EglUtilTest.cc
+++ b/services/camera/virtualcamera/tests/EglUtilTest.cc
@@ -61,12 +61,24 @@
EXPECT_TRUE(eglTestPatternProgram.isInitialized());
}
-TEST_F(EglTest, EglTextureProgramSuccessfulInit) {
+TEST_F(EglTest, EglTextureProgramYuvSuccessfulInit) {
if (!isGlExtensionSupported(kGlExtYuvTarget)) {
GTEST_SKIP() << "Skipping test because of missing required GL extension " << kGlExtYuvTarget;
}
- EglTextureProgram eglTextureProgram;
+ EglTextureProgram eglTextureProgram(EglTextureProgram::TextureFormat::YUV);
+
+ // Verify the shaders compiled and linked successfully.
+ EXPECT_TRUE(eglTextureProgram.isInitialized());
+}
+
+TEST_F(EglTest, EglTextureProgramRgbaSuccessfulInit) {
+ if (!isGlExtensionSupported(kGlExtYuvTarget)) {
+ GTEST_SKIP() << "Skipping test because of missing required GL extension "
+ << kGlExtYuvTarget;
+ }
+
+ EglTextureProgram eglTextureProgram(EglTextureProgram::TextureFormat::RGBA);
// Verify the shaders compiled and linked successfully.
EXPECT_TRUE(eglTextureProgram.isInitialized());
diff --git a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
index f931cb4..38261fb 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraServiceTest.cc
@@ -29,6 +29,7 @@
#include "binder/Binder.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "util/Permissions.h"
#include "utils/Errors.h"
namespace android {
@@ -50,10 +51,13 @@
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::Not;
+using ::testing::Return;
using ::testing::SizeIs;
constexpr int kVgaWidth = 640;
constexpr int kVgaHeight = 480;
+constexpr char kCreateVirtualDevicePermissions[] =
+ "android.permission.CREATE_VIRTUAL_DEVICE";
const VirtualCameraConfiguration kEmptyVirtualCameraConfiguration;
@@ -76,6 +80,12 @@
(override));
};
+class MockPermissionsProxy : public PermissionsProxy {
+ public:
+ MOCK_METHOD(bool, checkCallingPermission, (const std::string&),
+ (const override));
+};
+
class VirtualCameraServiceTest : public ::testing::Test {
public:
void SetUp() override {
@@ -87,8 +97,11 @@
return ndk::ScopedAStatus::ok();
});
mCameraProvider->setCallback(mMockCameraProviderCallback);
- mCameraService =
- ndk::SharedRefBase::make<VirtualCameraService>(mCameraProvider);
+ mCameraService = ndk::SharedRefBase::make<VirtualCameraService>(
+ mCameraProvider, mMockPermissionsProxy);
+
+ ON_CALL(mMockPermissionsProxy, checkCallingPermission)
+ .WillByDefault(Return(true));
mDevNullFd = open("/dev/null", O_RDWR);
ASSERT_THAT(mDevNullFd, Ge(0));
@@ -129,6 +142,7 @@
std::shared_ptr<VirtualCameraProvider> mCameraProvider;
std::shared_ptr<MockCameraProviderCallback> mMockCameraProviderCallback =
ndk::SharedRefBase::make<MockCameraProviderCallback>();
+ MockPermissionsProxy mMockPermissionsProxy;
sp<BBinder> mOwnerToken;
ndk::SpAIBinder mNdkOwnerToken;
@@ -139,7 +153,7 @@
createConfiguration(kVgaWidth, kVgaHeight, Format::YUV_420_888);
};
-TEST_F(VirtualCameraServiceTest, RegisterCameraSucceeds) {
+TEST_F(VirtualCameraServiceTest, RegisterCameraWithYuvInputSucceeds) {
sp<BBinder> token = sp<BBinder>::make();
ndk::SpAIBinder ndkToken(AIBinder_fromPlatformBinder(token));
bool aidlRet;
@@ -153,6 +167,20 @@
EXPECT_THAT(getCameraIds(), SizeIs(1));
}
+TEST_F(VirtualCameraServiceTest, RegisterCameraWithRgbaInputSucceeds) {
+ sp<BBinder> token = sp<BBinder>::make();
+ ndk::SpAIBinder ndkToken(AIBinder_fromPlatformBinder(token));
+ bool aidlRet;
+
+ VirtualCameraConfiguration config =
+ createConfiguration(kVgaWidth, kVgaHeight, Format::RGBA_8888);
+
+ ASSERT_TRUE(mCameraService->registerCamera(ndkToken, config, &aidlRet).isOk());
+
+ EXPECT_TRUE(aidlRet);
+ EXPECT_THAT(getCameraIds(), SizeIs(1));
+}
+
TEST_F(VirtualCameraServiceTest, RegisterCameraTwiceSecondReturnsFalse) {
createCamera();
bool aidlRet;
@@ -242,6 +270,40 @@
EXPECT_THAT(mCameraService->getCamera(mNdkOwnerToken), IsNull());
}
+TEST_F(VirtualCameraServiceTest, RegisterCameraWithoutPermissionFails) {
+ bool aidlRet;
+ EXPECT_CALL(mMockPermissionsProxy,
+ checkCallingPermission(kCreateVirtualDevicePermissions))
+ .WillOnce(Return(false));
+
+ EXPECT_THAT(mCameraService
+ ->registerCamera(mNdkOwnerToken, mVgaYUV420OnlyConfiguration,
+ &aidlRet)
+ .getExceptionCode(),
+ Eq(EX_SECURITY));
+}
+
+TEST_F(VirtualCameraServiceTest, UnregisterCameraWithoutPermissionFails) {
+ EXPECT_CALL(mMockPermissionsProxy,
+ checkCallingPermission(kCreateVirtualDevicePermissions))
+ .WillOnce(Return(false));
+
+ EXPECT_THAT(
+ mCameraService->unregisterCamera(mNdkOwnerToken).getExceptionCode(),
+ Eq(EX_SECURITY));
+}
+
+TEST_F(VirtualCameraServiceTest, GetIdWithoutPermissionFails) {
+ int32_t aidlRet;
+ EXPECT_CALL(mMockPermissionsProxy,
+ checkCallingPermission(kCreateVirtualDevicePermissions))
+ .WillOnce(Return(false));
+
+ EXPECT_THAT(
+ mCameraService->getCameraId(mNdkOwnerToken, &aidlRet).getExceptionCode(),
+ Eq(EX_SECURITY));
+}
+
TEST_F(VirtualCameraServiceTest, UnregisterCameraWithUnknownToken) {
createCamera();
diff --git a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
index 9edfe77..30bd2b6 100644
--- a/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
+++ b/services/camera/virtualcamera/tests/VirtualCameraSessionTest.cc
@@ -20,8 +20,8 @@
#include "VirtualCameraDevice.h"
#include "VirtualCameraSession.h"
#include "aidl/android/companion/virtualcamera/BnVirtualCameraCallback.h"
-#include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h"
#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
+#include "aidl/android/hardware/camera/common/Status.h"
#include "aidl/android/hardware/camera/device/BnCameraDeviceCallback.h"
#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
#include "aidl/android/hardware/graphics/common/PixelFormat.h"
@@ -44,6 +44,7 @@
using ::aidl::android::companion::virtualcamera::BnVirtualCameraCallback;
using ::aidl::android::companion::virtualcamera::Format;
using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
+using ::aidl::android::hardware::camera::common::Status;
using ::aidl::android::hardware::camera::device::BnCameraDeviceCallback;
using ::aidl::android::hardware::camera::device::BufferRequest;
using ::aidl::android::hardware::camera::device::BufferRequestStatus;
@@ -110,7 +111,7 @@
.pixelFormat = Format::YUV_420_888}},
mMockVirtualCameraClientCallback);
mVirtualCameraSession = ndk::SharedRefBase::make<VirtualCameraSession>(
- *mVirtualCameraDevice, mMockCameraDeviceCallback,
+ mVirtualCameraDevice, mMockCameraDeviceCallback,
mMockVirtualCameraClientCallback);
// Explicitly defining default actions below to prevent gmock from
@@ -223,6 +224,22 @@
EXPECT_THAT(aidlReturn, Eq(requests.size()));
}
+TEST_F(VirtualCameraSessionTest, configureAfterCameraRelease) {
+ StreamConfiguration streamConfiguration;
+ streamConfiguration.streams = {
+ createStream(kStreamId, kWidth, kHeight, PixelFormat::YCBCR_420_888)};
+ std::vector<HalStream> halStreams;
+
+ // Release virtual camera.
+ mVirtualCameraDevice.reset();
+
+ // Expect configuration attempt returns CAMERA_DISCONNECTED service specific code.
+ EXPECT_THAT(
+ mVirtualCameraSession->configureStreams(streamConfiguration, &halStreams)
+ .getServiceSpecificError(),
+ Eq(static_cast<int32_t>(Status::CAMERA_DISCONNECTED)));
+}
+
} // namespace
} // namespace virtualcamera
} // namespace companion
diff --git a/services/camera/virtualcamera/util/EglFramebuffer.cc b/services/camera/virtualcamera/util/EglFramebuffer.cc
index acf0122..57b94d3 100644
--- a/services/camera/virtualcamera/util/EglFramebuffer.cc
+++ b/services/camera/virtualcamera/util/EglFramebuffer.cc
@@ -112,6 +112,10 @@
return mHeight;
}
+std::shared_ptr<AHardwareBuffer> EglFrameBuffer::getHardwareBuffer() {
+ return mHardwareBuffer;
+}
+
} // namespace virtualcamera
} // namespace companion
} // namespace android
diff --git a/services/camera/virtualcamera/util/EglFramebuffer.h b/services/camera/virtualcamera/util/EglFramebuffer.h
index 35f85e2..b39df6d 100644
--- a/services/camera/virtualcamera/util/EglFramebuffer.h
+++ b/services/camera/virtualcamera/util/EglFramebuffer.h
@@ -50,6 +50,9 @@
// Return height of framebuffer (in pixels).
int getHeight() const;
+ // Return underlying hardware buffer.
+ std::shared_ptr<AHardwareBuffer> getHardwareBuffer();
+
private:
// Keeping shared_ptr to hardware buffer instance here prevents it from being
// freed while tied to EGL framebufer / EGL texture.
diff --git a/services/camera/virtualcamera/util/EglProgram.cc b/services/camera/virtualcamera/util/EglProgram.cc
index c468d39..510fd33 100644
--- a/services/camera/virtualcamera/util/EglProgram.cc
+++ b/services/camera/virtualcamera/util/EglProgram.cc
@@ -76,17 +76,29 @@
vTextureCoord = aTextureCoord;
})";
-constexpr char kExternalTextureFragmentShader[] = R"(#version 300 es
+constexpr char kExternalYuvTextureFragmentShader[] = R"(#version 300 es
#extension GL_OES_EGL_image_external_essl3 : require
#extension GL_EXT_YUV_target : require
precision mediump float;
in vec2 vTextureCoord;
- out vec4 fragColor;
+ layout (yuv) out vec4 fragColor;
uniform __samplerExternal2DY2YEXT uTexture;
void main() {
fragColor = texture(uTexture, vTextureCoord);
})";
+constexpr char kExternalRgbaTextureFragmentShader[] = R"(#version 300 es
+ #extension GL_OES_EGL_image_external : require
+ #extension GL_EXT_YUV_target : require
+ precision mediump float;
+ in vec2 vTextureCoord;
+ layout (yuv) out vec4 fragColor;
+ uniform samplerExternalOES uTexture;
+ void main() {
+ vec4 rgbaColor = texture(uTexture, vTextureCoord);
+ fragColor = vec4(rgb_2_yuv(rgbaColor.xyz, itu_601_full_range), 0.0);
+ })";
+
constexpr int kCoordsPerVertex = 3;
constexpr std::array<float, 12> kSquareCoords{-1.f, 1.0f, 0.0f, // top left
-1.f, -1.f, 0.0f, // bottom left
@@ -237,7 +249,7 @@
return true;
}
-EglTextureProgram::EglTextureProgram() {
+EglTextureProgram::EglTextureProgram(const TextureFormat textureFormat) {
if (!isGlExtensionSupported(kGlExtYuvTarget)) {
ALOGE(
"Cannot initialize external texture program due to missing "
@@ -245,7 +257,10 @@
return;
}
- if (initialize(kExternalTextureVertexShader, kExternalTextureFragmentShader)) {
+ const char* fragmentShaderSrc = textureFormat == TextureFormat::YUV
+ ? kExternalYuvTextureFragmentShader
+ : kExternalRgbaTextureFragmentShader;
+ if (initialize(kExternalTextureVertexShader, fragmentShaderSrc)) {
ALOGV("Successfully initialized EGL shaders for external texture program.");
} else {
ALOGE("External texture EGL shader program initialization failed.");
diff --git a/services/camera/virtualcamera/util/EglProgram.h b/services/camera/virtualcamera/util/EglProgram.h
index 8e394e7..1b5f2cd 100644
--- a/services/camera/virtualcamera/util/EglProgram.h
+++ b/services/camera/virtualcamera/util/EglProgram.h
@@ -55,7 +55,9 @@
// TODO(b/301023410) Add support for translation / cropping.
class EglTextureProgram : public EglProgram {
public:
- EglTextureProgram();
+ enum class TextureFormat { RGBA, YUV };
+
+ EglTextureProgram(TextureFormat textureFormat = TextureFormat::YUV);
bool draw(GLuint textureId);
};
diff --git a/services/camera/virtualcamera/util/Permissions.cc b/services/camera/virtualcamera/util/Permissions.cc
new file mode 100644
index 0000000..634bca3
--- /dev/null
+++ b/services/camera/virtualcamera/util/Permissions.cc
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "VirtualCameraPermissions"
+
+#include "Permissions.h"
+
+#include "binder/PermissionCache.h"
+#include "log/log.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+namespace {
+
+class PermissionsProxyImpl : public PermissionsProxy {
+ public:
+ bool checkCallingPermission(const std::string& permission) const override;
+};
+
+bool PermissionsProxyImpl::checkCallingPermission(
+ const std::string& permission) const {
+ int32_t uid;
+ int32_t pid;
+ const bool hasPermission = PermissionCache::checkCallingPermission(
+ String16(permission.c_str()), &pid, &uid);
+
+ ALOGV("%s: Checking %s permission for pid %d uid %d: %s", __func__,
+ permission.c_str(), pid, uid, hasPermission ? "granted" : "denied");
+ return hasPermission;
+}
+} // namespace
+
+const PermissionsProxy& PermissionsProxy::get() {
+ static PermissionsProxyImpl sPermissionProxyImpl;
+ return sPermissionProxyImpl;
+}
+
+} // namespace virtualcamera
+} // namespace companion
+} // namespace android
diff --git a/services/camera/virtualcamera/util/Permissions.h b/services/camera/virtualcamera/util/Permissions.h
new file mode 100644
index 0000000..014989e
--- /dev/null
+++ b/services/camera/virtualcamera/util/Permissions.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_COMPANION_VIRTUALCAMERA_PERMISSIONS_H
+#define ANDROID_COMPANION_VIRTUALCAMERA_PERMISSIONS_H
+
+#include <string>
+
+#include "sys/types.h"
+
+namespace android {
+namespace companion {
+namespace virtualcamera {
+
+class PermissionsProxy {
+ public:
+ virtual ~PermissionsProxy() = default;
+
+ // Checks whether caller holds permission. Do not use with runtime permissions
+ // as the default implementation uses PermissionCache which doesn't reflect
+ // possible runtime changes of permissions.
+ //
+ // Returns true in case caller holds the permission, false otherwise or if
+ // there was any error while verifying the permission.
+ virtual bool checkCallingPermission(const std::string& permission) const = 0;
+
+ // Get instance of PermissionProxy.
+ static const PermissionsProxy& get();
+};
+
+} // namespace virtualcamera
+} // namespace companion
+} // namespace android
+
+#endif // ANDROID_COMPANION_VIRTUALCAMERA_PERMISSIONS_H
diff --git a/services/camera/virtualcamera/util/Util.cc b/services/camera/virtualcamera/util/Util.cc
index 11dc3a6..df771b1 100644
--- a/services/camera/virtualcamera/util/Util.cc
+++ b/services/camera/virtualcamera/util/Util.cc
@@ -18,20 +18,26 @@
#include <unistd.h>
+#include <algorithm>
+#include <array>
+
#include "jpeglib.h"
namespace android {
namespace companion {
namespace virtualcamera {
+using ::aidl::android::companion::virtualcamera::Format;
+using ::aidl::android::hardware::common::NativeHandle;
+
// Lower bound for maximal supported texture size is at least 2048x2048
// but on most platforms will be more.
// TODO(b/301023410) - Query actual max texture size.
constexpr int kMaxTextureSize = 2048;
constexpr int kLibJpegDctSize = DCTSIZE;
-using ::aidl::android::companion::virtualcamera::Format;
-using ::aidl::android::hardware::common::NativeHandle;
+constexpr std::array<Format, 2> kSupportedFormats{Format::YUV_420_888,
+ Format::RGBA_8888};
sp<Fence> importFence(const NativeHandle& aidlHandle) {
if (aidlHandle.fds.size() != 1) {
@@ -41,11 +47,15 @@
return sp<Fence>::make(::dup(aidlHandle.fds[0].get()));
}
+bool isPixelFormatSupportedForInput(const Format format) {
+ return std::find(kSupportedFormats.begin(), kSupportedFormats.end(),
+ format) != kSupportedFormats.end();
+}
+
// Returns true if specified format is supported for virtual camera input.
bool isFormatSupportedForInput(const int width, const int height,
const Format format) {
- if (format != Format::YUV_420_888) {
- // For now only YUV_420_888 is supported for input.
+ if (!isPixelFormatSupportedForInput(format)) {
return false;
}
diff --git a/services/camera/virtualcamera/util/Util.h b/services/camera/virtualcamera/util/Util.h
index b778321..a73c99b 100644
--- a/services/camera/virtualcamera/util/Util.h
+++ b/services/camera/virtualcamera/util/Util.h
@@ -43,6 +43,10 @@
sp<Fence> importFence(
const ::aidl::android::hardware::common::NativeHandle& handle);
+// Returns true if specified pixel format is supported for virtual camera input.
+bool isPixelFormatSupportedForInput(
+ ::aidl::android::companion::virtualcamera::Format format);
+
// Returns true if specified format is supported for virtual camera input.
bool isFormatSupportedForInput(
int width, int height,
diff --git a/services/camera/virtualcamera/virtual_camera.hal.rc b/services/camera/virtualcamera/virtual_camera.hal.rc
index fd86965..a8bb61d 100644
--- a/services/camera/virtualcamera/virtual_camera.hal.rc
+++ b/services/camera/virtualcamera/virtual_camera.hal.rc
@@ -5,3 +5,7 @@
capabilities SYS_NICE
rlimit rtprio 10 10
task_profiles CameraServiceCapacity CameraServicePerformance
+ interface aidl virtual_camera
+ interface aidl android.hardware.camera.provider.ICameraProvider/virtual/0
+ oneshot
+ disabled
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index 2ef6fe5..6b48075 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -76,7 +76,8 @@
const static std::map<audio_format_t, audio_format_t> NEXT_FORMAT_TO_TRY = {
{AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_PCM_32_BIT},
{AUDIO_FORMAT_PCM_32_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED},
- {AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_16_BIT}
+ {AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_8_24_BIT},
+ {AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_16_BIT}
};
audio_format_t getNextFormatToTry(audio_format_t curFormat) {
@@ -421,10 +422,17 @@
ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
if (getDeviceId() != deviceId) {
if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+ // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
+ // as false here so that there won't be a new stream connect to this endpoint.
+ mConnected.store(false);
const android::sp<AAudioServiceEndpointMMAP> holdEndpoint(this);
std::thread asyncTask([holdEndpoint, deviceId]() {
ALOGD("onRoutingChanged() asyncTask launched");
- holdEndpoint->disconnectRegisteredStreams();
+ // When routing changed, the stream is disconnected and cannot be used except for
+ // closing. In that case, it should be safe to release all registered streams.
+ // This can help release service side resource in case the client doesn't close
+ // the stream after receiving disconnect event.
+ holdEndpoint->releaseRegisteredStreams();
holdEndpoint->setDeviceId(deviceId);
});
asyncTask.detach();
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
index 1dd0c3a..5e1e594 100644
--- a/services/oboeservice/AAudioServiceEndpointShared.cpp
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -219,9 +219,13 @@
void AAudioServiceEndpointShared::handleDisconnectRegisteredStreamsAsync() {
android::sp<AAudioServiceEndpointShared> holdEndpoint(this);
+ // When there is a routing changed, mmap stream should be disconnected. Set `mConnected`
+ // as false here so that there won't be a new stream connect to this endpoint.
+ mConnected.store(false);
std::thread asyncTask([holdEndpoint]() {
- // We do not need the returned vector.
- holdEndpoint->disconnectRegisteredStreams();
+ // When handling disconnection, the service side has disconnected. In that case,
+ // it should be safe to release all registered streams.
+ holdEndpoint->releaseRegisteredStreams();
});
asyncTask.detach();
}
diff --git a/services/oboeservice/Android.bp b/services/oboeservice/Android.bp
index 3521979..9fe06b7 100644
--- a/services/oboeservice/Android.bp
+++ b/services/oboeservice/Android.bp
@@ -55,7 +55,7 @@
"modernize-use-emplace",
"modernize-use-equals-default",
"modernize-use-equals-delete",
- // "modernize-use-nodiscard", // Maybe add this in the future
+ "modernize-use-nodiscard",
"modernize-use-noexcept",
"modernize-use-nullptr",
"modernize-use-override",