Merge "AAudio: Completely sync flowgraph with Oboe" into tm-dev
diff --git a/apex/Android.bp b/apex/Android.bp
index b9b9bde..570ca01 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -56,6 +56,7 @@
prebuilts: [
"code_coverage.policy",
"com.android.media-mediatranscoding.rc",
+ "com.android.media-mediatranscoding.32rc",
"crash_dump.policy",
"mediaextractor.policy",
"media-linker-config",
@@ -177,6 +178,7 @@
],
prebuilts: [
"com.android.media.swcodec-mediaswcodec.rc",
+ "com.android.media.swcodec-mediaswcodec.32rc",
"com.android.media.swcodec-ld.config.txt",
"mediaswcodec.policy",
"code_coverage.policy",
@@ -201,17 +203,34 @@
compressible: true,
}
+// install as mediatranscoding.* and mediaswcodec.* instead of init.*
+// so we are ready for day we have more than 1 *rc file within the apex.
+
prebuilt_etc {
name: "com.android.media-mediatranscoding.rc",
src: "mediatranscoding.rc",
- filename: "init.rc",
+ filename: "mediatranscoding.rc",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "com.android.media-mediatranscoding.32rc",
+ src: "mediatranscoding.32rc",
+ filename: "mediatranscoding.32rc",
installable: false,
}
prebuilt_etc {
name: "com.android.media.swcodec-mediaswcodec.rc",
src: "mediaswcodec.rc",
- filename: "init.rc",
+ filename: "mediaswcodec.rc",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "com.android.media.swcodec-mediaswcodec.32rc",
+ src: "mediaswcodec.32rc",
+ filename: "mediaswcodec.32rc",
installable: false,
}
diff --git a/apex/manifest.json b/apex/manifest.json
index 2cf7296..752c2b5 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,6 +1,6 @@
{
"name": "com.android.media",
- "version": 339999900,
+ "version": 330100000,
"requireNativeLibs": [
"libandroid.so",
"libbinder_ndk.so",
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 82463a2..3732a76 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,6 +1,6 @@
{
"name": "com.android.media.swcodec",
- "version": 339999900,
+ "version": 330100000,
"requireNativeLibs": [
":sphal"
]
diff --git a/apex/mediaswcodec.32rc b/apex/mediaswcodec.32rc
index 79aef36..f40d172 100644
--- a/apex/mediaswcodec.32rc
+++ b/apex/mediaswcodec.32rc
@@ -1,3 +1,5 @@
+## for SDK releases >= 32
+##
service media.swcodec /apex/com.android.media.swcodec/bin/mediaswcodec
class main
user mediacodec
diff --git a/apex/mediaswcodec.rc b/apex/mediaswcodec.rc
index 0c9b8c8..46799c7 100644
--- a/apex/mediaswcodec.rc
+++ b/apex/mediaswcodec.rc
@@ -1,3 +1,6 @@
+## for SDK releases 29..31
+## where writepid has not yet been replaced by task_profiles
+##
service media.swcodec /apex/com.android.media.swcodec/bin/mediaswcodec
class main
user mediacodec
diff --git a/apex/mediatranscoding.32rc b/apex/mediatranscoding.32rc
index 5169462..edba9b9 100644
--- a/apex/mediatranscoding.32rc
+++ b/apex/mediatranscoding.32rc
@@ -1,3 +1,6 @@
+## for SDK releases >= 32
+##
+#
# media.transcoding service is defined on com.android.media apex which goes back
# to API29, but we only want it started on API31+ devices. So we declare it as
# "disabled" and start it explicitly on boot.
diff --git a/apex/mediatranscoding.rc b/apex/mediatranscoding.rc
index ae9f8ba..6e453be 100644
--- a/apex/mediatranscoding.rc
+++ b/apex/mediatranscoding.rc
@@ -1,3 +1,7 @@
+## for SDK releases 29..31
+## where writepid has not yet been replaced by task_profiles
+##
+#
# media.transcoding service is defined on com.android.media apex which goes back
# to API29, but we only want it started on API31+ devices. So we declare it as
# "disabled" and start it explicitly on boot.
diff --git a/camera/CameraSessionStats.cpp b/camera/CameraSessionStats.cpp
index 05341bf..8088d06 100644
--- a/camera/CameraSessionStats.cpp
+++ b/camera/CameraSessionStats.cpp
@@ -375,6 +375,12 @@
return err;
}
+ String16 userTag;
+ if ((err = parcel->readString16(&userTag)) != OK) {
+ ALOGE("%s: Failed to read user tag!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
mCameraId = id;
mFacing = facing;
mNewCameraState = newCameraState;
@@ -389,6 +395,7 @@
mResultErrorCount = resultErrorCount;
mDeviceError = deviceError;
mStreamStats = std::move(streamStats);
+ mUserTag = userTag;
return OK;
}
@@ -471,6 +478,10 @@
return err;
}
+ if ((err = parcel->writeString16(mUserTag)) != OK) {
+ ALOGE("%s: Failed to write user tag!", __FUNCTION__);
+ return err;
+ }
return OK;
}
diff --git a/camera/VendorTagDescriptor.cpp b/camera/VendorTagDescriptor.cpp
index 24fa912..b37803a 100644
--- a/camera/VendorTagDescriptor.cpp
+++ b/camera/VendorTagDescriptor.cpp
@@ -760,7 +760,7 @@
Mutex::Autolock al(sLock);
if (sGlobalVendorTagDescriptorCache == NULL) {
ALOGE("%s: Vendor tag descriptor cache not initialized.", __FUNCTION__);
- return VENDOR_TAG_NAME_ERR;
+ return VENDOR_TAG_TYPE_ERR;
}
return sGlobalVendorTagDescriptorCache->getTagType(tag, id);
}
diff --git a/camera/camera2/CaptureRequest.cpp b/camera/camera2/CaptureRequest.cpp
index ebc09d7..7a8a4ba 100644
--- a/camera/camera2/CaptureRequest.cpp
+++ b/camera/camera2/CaptureRequest.cpp
@@ -146,6 +146,20 @@
mSurfaceIdxList.push_back(surfaceIdx);
}
+ int32_t hasUserTag;
+ if ((err = parcel->readInt32(&hasUserTag)) != OK) {
+ ALOGE("%s: Failed to read user tag availability flag", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ if (hasUserTag) {
+ String16 userTag;
+ if ((err = parcel->readString16(&userTag)) != OK) {
+ ALOGE("%s: Failed to read user tag!", __FUNCTION__);
+ return BAD_VALUE;
+ }
+ mUserTag = String8(userTag).c_str();
+ }
+
return OK;
}
@@ -213,6 +227,14 @@
return err;
}
}
+
+ if (mUserTag.empty()) {
+ parcel->writeInt32(0);
+ } else {
+ parcel->writeInt32(1);
+ parcel->writeString16(String16(mUserTag.c_str()));
+ }
+
return OK;
}
diff --git a/camera/include/camera/CameraSessionStats.h b/camera/include/camera/CameraSessionStats.h
index 15f5622..e1ec6cf 100644
--- a/camera/include/camera/CameraSessionStats.h
+++ b/camera/include/camera/CameraSessionStats.h
@@ -136,6 +136,7 @@
// Whether the device runs into an error state
bool mDeviceError;
std::vector<CameraStreamStats> mStreamStats;
+ String16 mUserTag;
// Constructors
CameraSessionStats();
diff --git a/camera/include/camera/camera2/CaptureRequest.h b/camera/include/camera/camera2/CaptureRequest.h
index 506abab..28dbc7c 100644
--- a/camera/include/camera/camera2/CaptureRequest.h
+++ b/camera/include/camera/camera2/CaptureRequest.h
@@ -63,6 +63,8 @@
void* mContext; // arbitrary user context from NDK apps, null for java apps
+ std::string mUserTag; // The string representation of object passed into setTag.
+
/**
* Keep impl up-to-date with CaptureRequest.java in frameworks/base
*/
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 4891034..3f7ff8b 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -521,6 +521,14 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
+ * <p>When setting the AE metering regions, the application must consider the additional
+ * crop resulted from the aspect ratio differences between the preview stream and
+ * ACAMERA_SCALER_CROP_REGION. For example, if the ACAMERA_SCALER_CROP_REGION is the full
+ * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+ * the boundary of AE regions will be [0, y_crop] and
+ * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+ * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+ * mismatch.</p>
* <p>Starting from API level 30, the coordinate system of activeArraySize or
* preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
* pre-zoom field of view. This means that the same aeRegions values at different
@@ -722,6 +730,14 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
+ * <p>When setting the AF metering regions, the application must consider the additional
+ * crop resulted from the aspect ratio differences between the preview stream and
+ * ACAMERA_SCALER_CROP_REGION. For example, if the ACAMERA_SCALER_CROP_REGION is the full
+ * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+ * the boundary of AF regions will be [0, y_crop] and
+ * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+ * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+ * mismatch.</p>
* <p>Starting from API level 30, the coordinate system of activeArraySize or
* preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
* pre-zoom field of view. This means that the same afRegions values at different
@@ -917,6 +933,14 @@
* region and output only the intersection rectangle as the metering region in the result
* metadata. If the region is entirely outside the crop region, it will be ignored and
* not reported in the result metadata.</p>
+ * <p>When setting the AWB metering regions, the application must consider the additional
+ * crop resulted from the aspect ratio differences between the preview stream and
+ * ACAMERA_SCALER_CROP_REGION. For example, if the ACAMERA_SCALER_CROP_REGION is the full
+ * active array size with 4:3 aspect ratio, and the preview stream is 16:9,
+ * the boundary of AWB regions will be [0, y_crop] and
+ * [active_width, active_height - 2 * y_crop] rather than [0, 0] and
+ * [active_width, active_height], where y_crop is the additional crop due to aspect ratio
+ * mismatch.</p>
* <p>Starting from API level 30, the coordinate system of activeArraySize or
* preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not
* pre-zoom field of view. This means that the same awbRegions values at different
diff --git a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
index 4cc1292..85ab0c2 100644
--- a/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
+++ b/camera/ndk/ndk_vendor/impl/ACameraDevice.cpp
@@ -330,7 +330,8 @@
return ACAMERA_ERROR_UNKNOWN;
}
- mConfiguredOutputs[streamId] = std::make_pair(output->mWindow, outConfigW);
+ mConfiguredOutputs[streamId] =
+ std::move(std::make_pair(std::move(output->mWindow), std::move(outConfigW)));
return ACAMERA_OK;
}
@@ -623,7 +624,8 @@
outConfigInsert.windowHandles[0] = anw;
outConfigInsert.physicalCameraId = outConfig.mPhysicalCameraId;
native_handle_ptr_wrapper wrap(anw);
- outputSet.insert(std::make_pair(anw, outConfigInsertW));
+
+ outputSet.emplace(std::make_pair(std::move(anw), std::move(outConfigInsertW)));
}
std::set<std::pair<native_handle_ptr_wrapper, OutputConfigurationWrapper>> addSet = outputSet;
std::vector<int32_t> deleteList;
@@ -680,7 +682,7 @@
}
// add new streams
- for (auto outputPair : addSet) {
+ for (const auto &outputPair : addSet) {
int streamId;
Status status = Status::UNKNOWN_ERROR;
auto ret = mRemote->createStream(outputPair.second,
@@ -845,12 +847,32 @@
return;
}
- const auto& windowHandles = outputPairIt->second.second.mOutputConfiguration.windowHandles;
- for (const auto& outHandle : windowHandles) {
- for (auto streamAndWindowId : request->mCaptureRequest.streamAndWindowIds) {
- int32_t windowId = streamAndWindowId.windowId;
- if (utils::isWindowNativeHandleEqual(windowHandles[windowId],outHandle)) {
- const native_handle_t* anw = windowHandles[windowId].getNativeHandle();
+ // Get the surfaces corresponding to the error stream id, go through
+ // them and try to match the surfaces in the corresponding
+ // CaptureRequest.
+ const auto& errorWindowHandles =
+ outputPairIt->second.second.mOutputConfiguration.windowHandles;
+ for (const auto& errorWindowHandle : errorWindowHandles) {
+ for (const auto &requestStreamAndWindowId :
+ request->mCaptureRequest.streamAndWindowIds) {
+ // Go through the surfaces in the capture request and see which
+ // ones match the surfaces in the error stream.
+ int32_t requestWindowId = requestStreamAndWindowId.windowId;
+ auto requestSurfacePairIt =
+ mConfiguredOutputs.find(requestStreamAndWindowId.streamId);
+ if (requestSurfacePairIt == mConfiguredOutputs.end()) {
+ ALOGE("%s: Error: request stream id %d does not exist", __FUNCTION__,
+ requestStreamAndWindowId.streamId);
+ setCameraDeviceErrorLocked(ACAMERA_ERROR_CAMERA_SERVICE);
+ return;
+ }
+
+ const auto &requestWindowHandles =
+ requestSurfacePairIt->second.second.mOutputConfiguration.windowHandles;
+ if (utils::isWindowNativeHandleEqual(
+ requestWindowHandles[requestWindowId], errorWindowHandle)) {
+ const native_handle_t* anw =
+ requestWindowHandles[requestWindowId].getNativeHandle();
ALOGV("Camera %s Lost output buffer for ANW %p frame %" PRId64,
getId(), anw, frameNumber);
diff --git a/camera/ndk/ndk_vendor/impl/utils.h b/camera/ndk/ndk_vendor/impl/utils.h
index 6f5820e..62779a4 100644
--- a/camera/ndk/ndk_vendor/impl/utils.h
+++ b/camera/ndk/ndk_vendor/impl/utils.h
@@ -109,8 +109,30 @@
mOutputConfiguration.windowGroupId = -1;
};
- OutputConfigurationWrapper(OutputConfiguration &outputConfiguration)
- : mOutputConfiguration((outputConfiguration)) { }
+ OutputConfigurationWrapper(const OutputConfigurationWrapper &other) {
+ *this = other;
+ }
+
+ // Needed to make sure that OutputConfiguration in
+ // OutputConfigurationWrapper, when copied doesn't call hidl_handle's
+ // assignment operator / copy constructor, which will lead to native handle
+ // cloning, which is not what we want for app callbacks which have the native
+ // handle as parameter.
+ OutputConfigurationWrapper &operator=(const OutputConfigurationWrapper &other) {
+ const OutputConfiguration &outputConfiguration = other.mOutputConfiguration;
+ mOutputConfiguration.rotation = outputConfiguration.rotation;
+ mOutputConfiguration.isDeferred = outputConfiguration.isDeferred;
+ mOutputConfiguration.width = outputConfiguration.width;
+ mOutputConfiguration.height = outputConfiguration.height;
+ mOutputConfiguration.windowGroupId = outputConfiguration.windowGroupId;
+ mOutputConfiguration.windowHandles.resize(outputConfiguration.windowHandles.size());
+ mOutputConfiguration.physicalCameraId = outputConfiguration.physicalCameraId;
+ size_t i = 0;
+ for (const auto &handle : outputConfiguration.windowHandles) {
+ mOutputConfiguration.windowHandles[i++] = handle.getNativeHandle();
+ }
+ return *this;
+ }
bool operator ==(const OutputConfiguration &other) const {
const OutputConfiguration &self = mOutputConfiguration;
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
index 5295822..b30eed5 100644
--- a/media/codec2/components/base/SimpleC2Component.cpp
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -32,6 +32,13 @@
constexpr uint8_t kNeutralUVBitDepth8 = 128;
constexpr uint16_t kNeutralUVBitDepth10 = 512;
+bool isAtLeastT() {
+ char deviceCodeName[PROP_VALUE_MAX];
+ __system_property_get("ro.build.version.codename", deviceCodeName);
+ return android_get_device_api_level() >= __ANDROID_API_T__ ||
+ !strcmp(deviceCodeName, "Tiramisu");
+}
+
void convertYUV420Planar8ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, const uint8_t *srcY,
const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride,
size_t srcUStride, size_t srcVStride, size_t dstYStride,
@@ -767,9 +774,9 @@
// Save supported hal pixel formats for bit depth of 10, the first time this is called
if (!mBitDepth10HalPixelFormats.size()) {
std::vector<int> halPixelFormats;
- // TODO(b/178229371) Enable HAL_PIXEL_FORMAT_YCBCR_P010 once framework supports it
- // halPixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
-
+ if (isAtLeastT()) {
+ halPixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
+ }
// since allowRGBA1010102 can chance in each call, but mBitDepth10HalPixelFormats
// is populated only once, allowRGBA1010102 is not considered at this stage.
halPixelFormats.push_back(HAL_PIXEL_FORMAT_RGBA_1010102);
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
index d244f45..52ae3b8 100644
--- a/media/codec2/components/base/include/SimpleC2Component.h
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -27,7 +27,7 @@
#include <media/stagefright/foundation/Mutexed.h>
namespace android {
-
+bool isAtLeastT();
void convertYUV420Planar8ToYV12(uint8_t *dstY, uint8_t *dstU, uint8_t *dstV, const uint8_t *srcY,
const uint8_t *srcU, const uint8_t *srcV, size_t srcYStride,
size_t srcUStride, size_t srcVStride, size_t dstYStride,
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index e5fbe99..c4cbb78 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -190,11 +190,18 @@
.withSetter(ColorAspectsSetter, mDefaultColorAspects, mCodedColorAspects)
.build());
+ std::vector<uint32_t> pixelFormats = {HAL_PIXEL_FORMAT_YCBCR_420_888};
+ if (isAtLeastT()) {
+ pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
+ }
// TODO: support more formats?
- addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
- .withConstValue(new C2StreamPixelFormatInfo::output(
- 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
- .build());
+ addParameter(
+ DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
+ .withDefault(new C2StreamPixelFormatInfo::output(
+ 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+ .withFields({C2F(mPixelFormat, value).oneOf(pixelFormats)})
+ .withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
+ .build());
}
static C2R SizeSetter(bool mayBlock,
@@ -403,6 +410,7 @@
bool C2SoftGav1Dec::initDecoder() {
mSignalledError = false;
mSignalledOutputEos = false;
+ mHalPixelFormat = HAL_PIXEL_FORMAT_YV12;
mCodecCtx.reset(new libgav1::Decoder());
if (mCodecCtx == nullptr) {
@@ -648,6 +656,24 @@
return false;
}
}
+
+ if (mHalPixelFormat != format) {
+ C2StreamPixelFormatInfo::output pixelFormat(0u, format);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&pixelFormat }, C2_MAY_BLOCK, &failures);
+ if (err == C2_OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(pixelFormat));
+ } else {
+ ALOGE("Config update pixelFormat failed");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return UNKNOWN_ERROR;
+ }
+ mHalPixelFormat = format;
+ }
+
C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE};
// We always create a graphic block that is width aligned to 16 and height
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.h b/media/codec2/components/gav1/C2SoftGav1Dec.h
index 134fa0d..a69a863 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.h
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.h
@@ -54,6 +54,7 @@
std::shared_ptr<IntfImpl> mIntf;
std::unique_ptr<libgav1::Decoder> mCodecCtx;
+ uint32_t mHalPixelFormat;
uint32_t mWidth;
uint32_t mHeight;
bool mSignalledOutputEos;
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.cpp b/media/codec2/components/vpx/C2SoftVpxDec.cpp
index 5fc89be..e81f044 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxDec.cpp
@@ -19,7 +19,6 @@
#include <log/log.h>
#include <algorithm>
-
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/MediaDefs.h>
@@ -218,11 +217,20 @@
.build());
// TODO: support more formats?
+ std::vector<uint32_t> pixelFormats = {HAL_PIXEL_FORMAT_YCBCR_420_888};
+#ifdef VP9
+ if (isAtLeastT()) {
+ pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
+ }
+#endif
addParameter(
DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
- .withConstValue(new C2StreamPixelFormatInfo::output(
- 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+ .withDefault(new C2StreamPixelFormatInfo::output(
+ 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
+ .withFields({C2F(mPixelFormat, value).oneOf(pixelFormats)})
+ .withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
.build());
+
}
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::output> &oldMe,
@@ -424,7 +432,7 @@
#else
mMode = MODE_VP8;
#endif
-
+ mHalPixelFormat = HAL_PIXEL_FORMAT_YV12;
mWidth = 320;
mHeight = 240;
mFrameParallelMode = false;
@@ -690,6 +698,24 @@
}
format = getHalPixelFormatForBitDepth10(allowRGBA1010102);
}
+
+ if (mHalPixelFormat != format) {
+ C2StreamPixelFormatInfo::output pixelFormat(0u, format);
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ c2_status_t err = mIntf->config({&pixelFormat }, C2_MAY_BLOCK, &failures);
+ if (err == C2_OK) {
+ work->worklets.front()->output.configUpdate.push_back(
+ C2Param::Copy(pixelFormat));
+ } else {
+ ALOGE("Config update pixelFormat failed");
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return UNKNOWN_ERROR;
+ }
+ mHalPixelFormat = format;
+ }
+
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
c2_status_t err = pool->fetchGraphicBlock(align(mWidth, 16), mHeight, format, usage, &block);
if (err != C2_OK) {
diff --git a/media/codec2/components/vpx/C2SoftVpxDec.h b/media/codec2/components/vpx/C2SoftVpxDec.h
index 2065165..5564766 100644
--- a/media/codec2/components/vpx/C2SoftVpxDec.h
+++ b/media/codec2/components/vpx/C2SoftVpxDec.h
@@ -67,6 +67,7 @@
vpx_codec_ctx_t *mCodecCtx;
bool mFrameParallelMode; // Frame parallel is only supported by VP9 decoder.
+ uint32_t mHalPixelFormat;
uint32_t mWidth;
uint32_t mHeight;
bool mSignalledOutputEos;
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 2b9ec7d..3302dd3 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1803,9 +1803,16 @@
if (tryAndReportOnError(setRunning) != OK) {
return;
}
+
+ err2 = mChannel->requestInitialInputBuffers();
+
+ if (err2 != OK) {
+ ALOGE("Initial request for Input Buffers failed");
+ mCallback->onError(err2,ACTION_CODE_FATAL);
+ return;
+ }
mCallback->onStartCompleted();
- (void)mChannel->requestInitialInputBuffers();
}
void CCodec::initiateShutdown(bool keepComponentAllocated) {
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 99aa593..674714f 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -16,7 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "CCodecBufferChannel"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <algorithm>
#include <atomic>
@@ -44,7 +46,6 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/hexdump.h>
-#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <media/MediaCodecBuffer.h>
@@ -327,6 +328,8 @@
}
c2_status_t err = C2_OK;
if (!items.empty()) {
+ ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf(
+ "CCodecBufferChannel::queue(%s@ts=%lld)", mName, (long long)timeUs).c_str());
{
Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);
PipelineWatcher::Clock::time_point now = PipelineWatcher::Clock::now();
@@ -924,6 +927,11 @@
hdr.validTypes |= HdrMetadata::CTA861_3;
hdr.cta8613 = cta861_meta;
}
+
+ // does not have valid info
+ if (!(hdr.validTypes & (HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3))) {
+ hdrStaticInfo.reset();
+ }
}
if (hdrDynamicInfo
&& hdrDynamicInfo->m.type_ == C2Config::HDR_DYNAMIC_METADATA_TYPE_SMPTE_2094_40) {
@@ -1892,7 +1900,7 @@
int32_t flags = 0;
if (worklet->output.flags & C2FrameData::FLAG_END_OF_STREAM) {
- flags |= MediaCodec::BUFFER_FLAG_EOS;
+ flags |= BUFFER_FLAG_END_OF_STREAM;
ALOGV("[%s] onWorkDone: output EOS", mName);
}
@@ -1909,6 +1917,8 @@
// When using input surface we need to restore the original input timestamp.
timestamp = work->input.ordinal.customOrdinal;
}
+ ScopedTrace trace(ATRACE_TAG, android::base::StringPrintf(
+ "CCodecBufferChannel::onWorkDone(%s@ts=%lld)", mName, timestamp.peekll()).c_str());
ALOGV("[%s] onWorkDone: input %lld, codec %lld => output %lld => %lld",
mName,
work->input.ordinal.customOrdinal.peekll(),
@@ -1930,7 +1940,7 @@
sp<MediaCodecBuffer> outBuffer;
if (output->buffers && output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
outBuffer->meta()->setInt64("timeUs", timestamp.peek());
- outBuffer->meta()->setInt32("flags", MediaCodec::BUFFER_FLAG_CODECCONFIG);
+ outBuffer->meta()->setInt32("flags", BUFFER_FLAG_CODEC_CONFIG);
ALOGV("[%s] onWorkDone: csd index = %zu [%p]", mName, index, outBuffer.get());
output.unlock();
@@ -1966,7 +1976,7 @@
switch (info->coreIndex().coreIndex()) {
case C2StreamPictureTypeMaskInfo::CORE_INDEX:
if (((C2StreamPictureTypeMaskInfo *)info.get())->value & C2Config::SYNC_FRAME) {
- flags |= MediaCodec::BUFFER_FLAG_SYNCFRAME;
+ flags |= BUFFER_FLAG_KEY_FRAME;
}
break;
default:
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 20f2ecf..57c70c1 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -22,7 +22,6 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/MediaDefs.h>
-#include <media/stagefright/MediaCodec.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <mediadrm/ICrypto.h>
@@ -34,6 +33,8 @@
namespace {
+constexpr uint32_t PIXEL_FORMAT_UNKNOWN = 0;
+
sp<GraphicBlockBuffer> AllocateInputGraphicBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
@@ -292,7 +293,7 @@
int32_t flags,
const sp<AMessage>& format,
const C2WorkOrdinalStruct& ordinal) {
- bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
+ bool eos = flags & BUFFER_FLAG_END_OF_STREAM;
if (!buffer && eos) {
// TRICKY: we may be violating ordering of the stash here. Because we
// don't expect any more emplace() calls after this, the ordering should
@@ -300,7 +301,7 @@
mReorderStash.emplace_back(
buffer, notify, timestamp, flags, format, ordinal);
} else {
- flags = flags & ~MediaCodec::BUFFER_FLAG_EOS;
+ flags = flags & ~BUFFER_FLAG_END_OF_STREAM;
auto it = mReorderStash.begin();
for (; it != mReorderStash.end(); ++it) {
if (less(ordinal, it->ordinal)) {
@@ -311,7 +312,7 @@
buffer, notify, timestamp, flags, format, ordinal);
if (eos) {
mReorderStash.back().flags =
- mReorderStash.back().flags | MediaCodec::BUFFER_FLAG_EOS;
+ mReorderStash.back().flags | BUFFER_FLAG_END_OF_STREAM;
}
}
while (!mReorderStash.empty() && mReorderStash.size() > mDepth) {
@@ -348,7 +349,7 @@
// Flushing mReorderStash because no other buffers should come after output
// EOS.
- if (entry.flags & MediaCodec::BUFFER_FLAG_EOS) {
+ if (entry.flags & BUFFER_FLAG_END_OF_STREAM) {
// Flush reorder stash
setReorderDepth(0);
}
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index dd37c4b..c15b5ca 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -1896,7 +1896,9 @@
names->clear();
// TODO: expand to standard params
for (const auto &[key, desc] : mVendorParams) {
- names->push_back(key);
+ if (desc->isVisible()) {
+ names->push_back(key);
+ }
}
return OK;
}
diff --git a/media/codec2/sfplugin/Codec2Buffer.cpp b/media/codec2/sfplugin/Codec2Buffer.cpp
index 2d3c70a..c2405e8 100644
--- a/media/codec2/sfplugin/Codec2Buffer.cpp
+++ b/media/codec2/sfplugin/Codec2Buffer.cpp
@@ -16,7 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2Buffer"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <aidl/android/hardware/graphics/common/Cta861_3.h>
#include <aidl/android/hardware/graphics/common/Smpte2086.h>
@@ -229,6 +231,7 @@
mAllocatedDepth(0),
mBackBufferSize(0),
mMediaImage(new ABuffer(sizeof(MediaImage2))) {
+ ATRACE_CALL();
if (!format->findInt32(KEY_COLOR_FORMAT, &mClientColorFormat)) {
mClientColorFormat = COLOR_FormatYUV420Flexible;
}
@@ -581,6 +584,7 @@
* Copy C2GraphicView to MediaImage2.
*/
status_t copyToMediaImage() {
+ ATRACE_CALL();
if (mInitCheck != OK) {
return mInitCheck;
}
@@ -619,7 +623,9 @@
const sp<AMessage> &format,
const std::shared_ptr<C2GraphicBlock> &block,
std::function<sp<ABuffer>(size_t)> alloc) {
+ ATRACE_BEGIN("GraphicBlockBuffer::Allocate block->map()");
C2GraphicView view(block->map().get());
+ ATRACE_END();
if (view.error() != C2_OK) {
ALOGD("C2GraphicBlock::map failed: %d", view.error());
return nullptr;
@@ -664,6 +670,7 @@
}
std::shared_ptr<C2Buffer> GraphicBlockBuffer::asC2Buffer() {
+ ATRACE_CALL();
uint32_t width = mView.width();
uint32_t height = mView.height();
if (!mWrapped) {
@@ -752,8 +759,10 @@
ALOGD("C2Buffer precond fail");
return nullptr;
}
+ ATRACE_BEGIN("ConstGraphicBlockBuffer::Allocate block->map()");
std::unique_ptr<const C2GraphicView> view(std::make_unique<const C2GraphicView>(
buffer->data().graphicBlocks()[0].map().get()));
+ ATRACE_END();
std::unique_ptr<const C2GraphicView> holder;
GraphicView2MediaImageConverter converter(*view, format, false /* copy */);
@@ -854,11 +863,13 @@
return false;
}
+ ATRACE_BEGIN("ConstGraphicBlockBuffer::canCopy block->map()");
GraphicView2MediaImageConverter converter(
buffer->data().graphicBlocks()[0].map().get(),
// FIXME: format() is not const, but we cannot change it, so do a const cast here
const_cast<ConstGraphicBlockBuffer *>(this)->format(),
true /* copy */);
+ ATRACE_END();
if (converter.initCheck() != OK) {
ALOGD("ConstGraphicBlockBuffer::canCopy: converter init failed: %d", converter.initCheck());
return false;
@@ -973,16 +984,47 @@
return sMapper;
}
-class NativeHandleDeleter {
+class Gralloc4Buffer {
public:
- explicit NativeHandleDeleter(native_handle_t *handle) : mHandle(handle) {}
- ~NativeHandleDeleter() {
- if (mHandle) {
- native_handle_delete(mHandle);
+ Gralloc4Buffer(const C2Handle *const handle) : mBuffer(nullptr) {
+ sp<IMapper4> mapper = GetMapper4();
+ if (!mapper) {
+ return;
+ }
+ // Unwrap raw buffer handle from the C2Handle
+ native_handle_t *nh = UnwrapNativeCodec2GrallocHandle(handle);
+ if (!nh) {
+ return;
+ }
+ // Import the raw handle so IMapper can use the buffer. The imported
+ // handle must be freed when the client is done with the buffer.
+ mapper->importBuffer(
+ hardware::hidl_handle(nh),
+ [&](const Error4 &error, void *buffer) {
+ if (error == Error4::NONE) {
+ mBuffer = buffer;
+ }
+ });
+
+ // TRICKY: UnwrapNativeCodec2GrallocHandle creates a new handle but
+ // does not clone the fds. Thus we need to delete the handle
+ // without closing it.
+ native_handle_delete(nh);
+ }
+
+ ~Gralloc4Buffer() {
+ sp<IMapper4> mapper = GetMapper4();
+ if (mapper && mBuffer) {
+ // Free the imported buffer handle. This does not release the
+ // underlying buffer itself.
+ mapper->freeBuffer(mBuffer);
}
}
+
+ void *get() const { return mBuffer; }
+ operator bool() const { return (mBuffer != nullptr); }
private:
- native_handle_t *mHandle;
+ void *mBuffer;
};
} // namspace
@@ -992,24 +1034,15 @@
std::shared_ptr<C2StreamHdrStaticMetadataInfo::input> *staticInfo,
std::shared_ptr<C2StreamHdrDynamicMetadataInfo::input> *dynamicInfo) {
c2_status_t err = C2_OK;
- native_handle_t *nativeHandle = UnwrapNativeCodec2GrallocHandle(handle);
- if (nativeHandle == nullptr) {
- // Nothing to do
- return err;
- }
- // TRICKY: UnwrapNativeCodec2GrallocHandle creates a new handle but
- // does not clone the fds. Thus we need to delete the handle
- // without closing it when going out of scope.
- // NativeHandle cannot solve this problem, as it would close and
- // delete the handle, while we need delete only.
- NativeHandleDeleter nhd(nativeHandle);
sp<IMapper4> mapper = GetMapper4();
- if (!mapper) {
+ Gralloc4Buffer buffer(handle);
+ if (!mapper || !buffer) {
// Gralloc4 not supported; nothing to do
return err;
}
Error4 mapperErr = Error4::NONE;
if (staticInfo) {
+ ALOGV("Grabbing static HDR info from gralloc4 metadata");
staticInfo->reset(new C2StreamHdrStaticMetadataInfo::input(0u));
memset(&(*staticInfo)->mastering, 0, sizeof((*staticInfo)->mastering));
(*staticInfo)->maxCll = 0;
@@ -1038,7 +1071,7 @@
mapperErr = Error4::BAD_VALUE;
}
};
- Return<void> ret = mapper->get(nativeHandle, MetadataType_Smpte2086, cb);
+ Return<void> ret = mapper->get(buffer.get(), MetadataType_Smpte2086, cb);
if (!ret.isOk()) {
err = C2_REFUSED;
} else if (mapperErr != Error4::NONE) {
@@ -1059,7 +1092,7 @@
mapperErr = Error4::BAD_VALUE;
}
};
- ret = mapper->get(nativeHandle, MetadataType_Cta861_3, cb);
+ ret = mapper->get(buffer.get(), MetadataType_Cta861_3, cb);
if (!ret.isOk()) {
err = C2_REFUSED;
} else if (mapperErr != Error4::NONE) {
@@ -1067,6 +1100,7 @@
}
}
if (dynamicInfo) {
+ ALOGV("Grabbing dynamic HDR info from gralloc4 metadata");
dynamicInfo->reset();
IMapper4::get_cb cb = [&mapperErr, dynamicInfo](Error4 err, const hidl_vec<uint8_t> &vec) {
mapperErr = err;
@@ -1080,7 +1114,7 @@
vec.size(), 0u, C2Config::HDR_DYNAMIC_METADATA_TYPE_SMPTE_2094_40);
memcpy((*dynamicInfo)->m.data, vec.data(), vec.size());
};
- Return<void> ret = mapper->get(nativeHandle, MetadataType_Smpte2094_40, cb);
+ Return<void> ret = mapper->get(buffer.get(), MetadataType_Smpte2094_40, cb);
if (!ret.isOk() || mapperErr != Error4::NONE) {
dynamicInfo->reset();
}
@@ -1094,21 +1128,14 @@
const std::shared_ptr<const C2StreamHdrDynamicMetadataInfo::output> &dynamicInfo,
const C2Handle *const handle) {
c2_status_t err = C2_OK;
- native_handle_t *nativeHandle = UnwrapNativeCodec2GrallocHandle(handle);
- if (nativeHandle == nullptr) {
- // Nothing to do
- return err;
- }
- // TRICKY: UnwrapNativeCodec2GrallocHandle creates a new handle but
- // does not clone the fds. Thus we need to delete the handle
- // without closing it when going out of scope.
- NativeHandleDeleter nhd(nativeHandle);
sp<IMapper4> mapper = GetMapper4();
- if (!mapper) {
+ Gralloc4Buffer buffer(handle);
+ if (!mapper || !buffer) {
// Gralloc4 not supported; nothing to do
return err;
}
if (staticInfo && *staticInfo) {
+ ALOGV("Setting static HDR info as gralloc4 metadata");
std::optional<Smpte2086> smpte2086 = Smpte2086{
{staticInfo->mastering.red.x, staticInfo->mastering.red.y},
{staticInfo->mastering.green.x, staticInfo->mastering.green.y},
@@ -1118,8 +1145,17 @@
staticInfo->mastering.minLuminance,
};
hidl_vec<uint8_t> vec;
- if (gralloc4::encodeSmpte2086(smpte2086, &vec) == OK) {
- Return<Error4> ret = mapper->set(nativeHandle, MetadataType_Smpte2086, vec);
+ if (0.0 <= smpte2086->primaryRed.x && smpte2086->primaryRed.x <= 1.0
+ && 0.0 <= smpte2086->primaryRed.y && smpte2086->primaryRed.y <= 1.0
+ && 0.0 <= smpte2086->primaryGreen.x && smpte2086->primaryGreen.x <= 1.0
+ && 0.0 <= smpte2086->primaryGreen.y && smpte2086->primaryGreen.y <= 1.0
+ && 0.0 <= smpte2086->primaryBlue.x && smpte2086->primaryBlue.x <= 1.0
+ && 0.0 <= smpte2086->primaryBlue.y && smpte2086->primaryBlue.y <= 1.0
+ && 0.0 <= smpte2086->whitePoint.x && smpte2086->whitePoint.x <= 1.0
+ && 0.0 <= smpte2086->whitePoint.y && smpte2086->whitePoint.y <= 1.0
+ && 0.0 <= smpte2086->maxLuminance && 0.0 <= smpte2086->minLuminance
+ && gralloc4::encodeSmpte2086(smpte2086, &vec) == OK) {
+ Return<Error4> ret = mapper->set(buffer.get(), MetadataType_Smpte2086, vec);
if (!ret.isOk()) {
err = C2_REFUSED;
} else if (ret != Error4::NONE) {
@@ -1130,8 +1166,9 @@
staticInfo->maxCll,
staticInfo->maxFall,
};
- if (gralloc4::encodeCta861_3(cta861_3, &vec) == OK) {
- Return<Error4> ret = mapper->set(nativeHandle, MetadataType_Cta861_3, vec);
+ if (0.0 <= cta861_3->maxContentLightLevel && 0.0 <= cta861_3->maxFrameAverageLightLevel
+ && gralloc4::encodeCta861_3(cta861_3, &vec) == OK) {
+ Return<Error4> ret = mapper->set(buffer.get(), MetadataType_Cta861_3, vec);
if (!ret.isOk()) {
err = C2_REFUSED;
} else if (ret != Error4::NONE) {
@@ -1139,7 +1176,8 @@
}
}
}
- if (dynamicInfo && *dynamicInfo) {
+ if (dynamicInfo && *dynamicInfo && dynamicInfo->flexCount() > 0) {
+ ALOGV("Setting dynamic HDR info as gralloc4 metadata");
hidl_vec<uint8_t> vec;
vec.resize(dynamicInfo->flexCount());
memcpy(vec.data(), dynamicInfo->m.data, dynamicInfo->flexCount());
@@ -1153,7 +1191,7 @@
break;
}
if (metadataType) {
- Return<Error4> ret = mapper->set(nativeHandle, *metadataType, vec);
+ Return<Error4> ret = mapper->set(buffer.get(), *metadataType, vec);
if (!ret.isOk()) {
err = C2_REFUSED;
} else if (ret != Error4::NONE) {
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 63bd64b..2b8a160 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -281,6 +281,11 @@
}
};
+ // The color format is ordered by preference. The intention here is to advertise:
+ // c2.android.* codecs: YUV420s, Surface, <the rest>
+ // all other codecs: Surface, YUV420s, <the rest>
+ // TODO: get this preference via Codec2 API
+
// vendor video codecs prefer opaque format
if (trait.name.find("android") == std::string::npos) {
addDefaultColorFormat(COLOR_FormatSurface);
@@ -290,9 +295,8 @@
addDefaultColorFormat(COLOR_FormatYUV420SemiPlanar);
addDefaultColorFormat(COLOR_FormatYUV420PackedPlanar);
addDefaultColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
- // framework video encoders must support surface format, though it is unclear
- // that they will be able to map it if it is opaque
- if (encoder && trait.name.find("android") != std::string::npos) {
+ // Android video codecs prefer CPU-readable formats
+ if (trait.name.find("android") != std::string::npos) {
addDefaultColorFormat(COLOR_FormatSurface);
}
for (int32_t colorFormat : supportedColorFormats) {
diff --git a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
index bff9db5..7fc4c27 100644
--- a/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
+++ b/media/codec2/sfplugin/utils/Codec2BufferUtils.cpp
@@ -16,7 +16,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Codec2BufferUtils"
+#define ATRACE_TAG ATRACE_TAG_VIDEO
#include <utils/Log.h>
+#include <utils/Trace.h>
#include <libyuv.h>
@@ -36,8 +38,8 @@
namespace {
/**
- * A flippable, optimizable memcpy. Constructs such as (from ? src : dst) do not work as the results are
- * always const.
+ * A flippable, optimizable memcpy. Constructs such as (from ? src : dst)
+ * do not work as the results are always const.
*/
template<bool ToA, size_t S>
struct MemCopier {
@@ -139,15 +141,18 @@
if (IsNV12(view)) {
if (IsNV12(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV12->NV12");
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height / 2);
return OK;
} else if (IsNV21(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV12->NV21");
if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_u, src_stride_u,
dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
return OK;
}
} else if (IsI420(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV12->I420");
if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
@@ -155,15 +160,18 @@
}
} else if (IsNV21(view)) {
if (IsNV12(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV21->NV12");
if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_v, src_stride_v,
dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
} else if (IsNV21(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV21->NV21");
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height / 2);
return OK;
} else if (IsI420(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV21->I420");
if (!libyuv::NV21ToI420(src_y, src_stride_y, src_v, src_stride_v, dst_y, dst_stride_y,
dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
@@ -171,22 +179,26 @@
}
} else if (IsI420(view)) {
if (IsNV12(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: I420->NV12");
if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
} else if (IsNV21(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: I420->NV21");
if (!libyuv::I420ToNV21(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
return OK;
}
} else if (IsI420(img)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: I420->I420");
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width / 2, height / 2);
libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width / 2, height / 2);
return OK;
}
}
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: generic");
return _ImageCopy<true>(view, img, imgBase);
}
@@ -210,15 +222,18 @@
int height = view.crop().height;
if (IsNV12(img)) {
if (IsNV12(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV12->NV12");
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width, height / 2);
return OK;
} else if (IsNV21(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV12->NV21");
if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_u, src_stride_u,
dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
return OK;
}
} else if (IsI420(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV12->I420");
if (!libyuv::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, dst_y, dst_stride_y,
dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
@@ -226,15 +241,18 @@
}
} else if (IsNV21(img)) {
if (IsNV12(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV21->NV12");
if (!libyuv::NV21ToNV12(src_y, src_stride_y, src_v, src_stride_v,
dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
} else if (IsNV21(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV21->NV21");
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width, height / 2);
return OK;
} else if (IsI420(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: NV21->I420");
if (!libyuv::NV21ToI420(src_y, src_stride_y, src_v, src_stride_v, dst_y, dst_stride_y,
dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)) {
return OK;
@@ -242,22 +260,26 @@
}
} else if (IsI420(img)) {
if (IsNV12(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: I420->NV12");
if (!libyuv::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
dst_y, dst_stride_y, dst_u, dst_stride_u, width, height)) {
return OK;
}
} else if (IsNV21(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: I420->NV21");
if (!libyuv::I420ToNV21(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
dst_y, dst_stride_y, dst_v, dst_stride_v, width, height)) {
return OK;
}
} else if (IsI420(view)) {
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: I420->I420");
libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, width / 2, height / 2);
libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, width / 2, height / 2);
return OK;
}
}
+ ScopedTrace trace(ATRACE_TAG, "ImageCopy: generic");
return _ImageCopy<false>(view, img, imgBase);
}
diff --git a/media/codec2/vndk/Android.bp b/media/codec2/vndk/Android.bp
index 4047173..1d8aea3 100644
--- a/media/codec2/vndk/Android.bp
+++ b/media/codec2/vndk/Android.bp
@@ -78,6 +78,7 @@
export_shared_lib_headers: [
"libbase",
+ "libdmabufheap",
"android.hardware.media.bufferpool@2.0",
],
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index d8d6f06..bc4053d 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -261,7 +261,7 @@
for (const ui::PlaneLayout &plane : planes) {
layout->rootPlanes++;
uint32_t lastOffsetInBits = 0;
- uint32_t rootIx = 0;
+ uint32_t rootIx = layout->numPlanes;
for (const PlaneLayoutComponent &component : plane.components) {
if (!gralloc4::isStandardPlaneLayoutComponentType(component.type)) {
@@ -309,7 +309,6 @@
layout->numPlanes++;
lastOffsetInBits = component.offsetInBits + component.sizeInBits;
- rootIx++;
}
}
return C2_OK;
@@ -699,17 +698,6 @@
C2PlanarLayout::PLANE_V, // rootIx
0, // offset
};
- // handle interleaved formats
- intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
- if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chroma_step) {
- layout->rootPlanes = 2;
- layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
- layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
- } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chroma_step) {
- layout->rootPlanes = 2;
- layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
- layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
- }
break;
}
@@ -830,17 +818,6 @@
C2PlanarLayout::PLANE_V, // rootIx
0, // offset
};
- // handle interleaved formats
- intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
- if (uvOffset > 0 && uvOffset < (intptr_t)ycbcrLayout.chroma_step) {
- layout->rootPlanes = 2;
- layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
- layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
- } else if (uvOffset < 0 && uvOffset > -(intptr_t)ycbcrLayout.chroma_step) {
- layout->rootPlanes = 2;
- layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
- layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
- }
break;
}
@@ -886,6 +863,29 @@
}
mLocked = true;
+ // handle interleaved formats
+ if (layout->type == C2PlanarLayout::TYPE_YUV && layout->rootPlanes == 3) {
+ intptr_t uvOffset = addr[C2PlanarLayout::PLANE_V] - addr[C2PlanarLayout::PLANE_U];
+ intptr_t uvColInc = layout->planes[C2PlanarLayout::PLANE_U].colInc;
+ if (uvOffset > 0 && uvOffset < uvColInc) {
+ layout->rootPlanes = 2;
+ layout->planes[C2PlanarLayout::PLANE_V].rootIx = C2PlanarLayout::PLANE_U;
+ layout->planes[C2PlanarLayout::PLANE_V].offset = uvOffset;
+ } else if (uvOffset < 0 && uvOffset > -uvColInc) {
+ layout->rootPlanes = 2;
+ layout->planes[C2PlanarLayout::PLANE_U].rootIx = C2PlanarLayout::PLANE_V;
+ layout->planes[C2PlanarLayout::PLANE_U].offset = -uvOffset;
+ }
+ }
+
+ ALOGV("C2AllocationGralloc::map: layout: type=%d numPlanes=%d rootPlanes=%d",
+ layout->type, layout->numPlanes, layout->rootPlanes);
+ for (int i = 0; i < layout->numPlanes; ++i) {
+ const C2PlaneInfo &plane = layout->planes[i];
+ ALOGV("C2AllocationGralloc::map: plane[%d]: colInc=%d rowInc=%d rootIx=%u offset=%u",
+ i, plane.colInc, plane.rowInc, plane.rootIx, plane.offset);
+ }
+
return C2_OK;
}
diff --git a/media/codec2/vndk/include/C2BqBufferPriv.h b/media/codec2/vndk/include/C2BqBufferPriv.h
index b2636e9..bec978a 100644
--- a/media/codec2/vndk/include/C2BqBufferPriv.h
+++ b/media/codec2/vndk/include/C2BqBufferPriv.h
@@ -101,6 +101,8 @@
uint32_t generationId,
uint64_t consumerUsage);
+ virtual void getConsumerUsage(uint64_t *consumerUsage);
+
private:
const std::shared_ptr<C2Allocator> mAllocator;
const local_id_t mLocalId;
@@ -138,7 +140,6 @@
uint32_t toGeneration, uint64_t toUsage, uint64_t toBqId,
android::sp<android::GraphicBuffer>& graphicBuffer, uint32_t oldGeneration,
std::shared_ptr<C2SurfaceSyncMemory> syncMem);
-
private:
friend struct _C2BlockFactory;
diff --git a/media/codec2/vndk/platform/C2BqBuffer.cpp b/media/codec2/vndk/platform/C2BqBuffer.cpp
index 01995fd..2cca3c8 100644
--- a/media/codec2/vndk/platform/C2BqBuffer.cpp
+++ b/media/codec2/vndk/platform/C2BqBuffer.cpp
@@ -571,8 +571,8 @@
public:
Impl(const std::shared_ptr<C2Allocator> &allocator)
: mInit(C2_OK), mProducerId(0), mGeneration(0),
- mDqFailure(0), mLastDqTs(0), mLastDqLogTs(0),
- mAllocator(allocator) {
+ mConsumerUsage(0), mDqFailure(0), mLastDqTs(0),
+ mLastDqLogTs(0), mAllocator(allocator) {
}
~Impl() {
@@ -747,6 +747,11 @@
"bqId: %llu migrated buffers # %d",
generation, (unsigned long long)producerId, migrated);
}
+ mConsumerUsage = usage;
+ }
+
+ void getConsumerUsage(uint64_t *consumeUsage) {
+ *consumeUsage = mConsumerUsage;
}
private:
@@ -755,6 +760,7 @@
c2_status_t mInit;
uint64_t mProducerId;
uint32_t mGeneration;
+ uint64_t mConsumerUsage;
OnRenderCallback mRenderCallback;
size_t mDqFailure;
@@ -1086,3 +1092,10 @@
mImpl->setRenderCallback(renderCallback);
}
}
+
+void C2BufferQueueBlockPool::getConsumerUsage(uint64_t *consumeUsage) {
+ if (mImpl) {
+ mImpl->getConsumerUsage(consumeUsage);
+ }
+}
+
diff --git a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
index e55bdc0..2115cc3 100644
--- a/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
+++ b/media/codec2/vndk/platform/C2SurfaceSyncObj.cpp
@@ -228,10 +228,10 @@
tv.tv_nsec = timeoutNs % 1000000000;
int ret = syscall(__NR_futex, &mCond, FUTEX_WAIT, waitId, &tv, NULL, 0);
- if (ret == 0 || ret == EAGAIN) {
+ if (ret == 0 || errno == EAGAIN) {
return C2_OK;
}
- if (ret == EINTR || ret == ETIMEDOUT) {
+ if (errno == EINTR || errno == ETIMEDOUT) {
return C2_TIMED_OUT;
}
return C2_BAD_VALUE;
diff --git a/media/codecs/m4v_h263/dec/src/vop.cpp b/media/codecs/m4v_h263/dec/src/vop.cpp
index 7b32498..abc0861 100644
--- a/media/codecs/m4v_h263/dec/src/vop.cpp
+++ b/media/codecs/m4v_h263/dec/src/vop.cpp
@@ -107,26 +107,57 @@
#ifndef PV_TOLERATE_VOL_ERRORS
if (layer) /* */
{
- /* support SSPL0-2 */
- if (tmpvar != 0x10 && tmpvar != 0x11 && tmpvar != 0x12 &&
- tmpvar != 0xA1 && tmpvar != 0xA2 && tmpvar != 0xA3/* Core SP@L1-L3 */)
- return PV_FAIL;
+ switch (tmpvar)
+ {
+ /* Simple Scalable Profile Levels */
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ /* Core Scalable Profile Levels */
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ // Do Nothing, the cases listed above are supported values
+ break;
+ default:
+ // Unsupport profile level
+ return PV_FAIL;
+ }
}
else
{
- /* support SPL0-3 & SSPL0-2 */
- if (tmpvar != 0x01 && tmpvar != 0x02 && tmpvar != 0x03 && tmpvar != 0x08 &&
- /* While not technically supported, try to decode SPL4&SPL5 files as well. */
- /* We'll fail later if the size is too large. This is to allow playback of */
- /* some <=CIF files generated by other encoders. */
- tmpvar != 0x04 && tmpvar != 0x05 &&
- tmpvar != 0x10 && tmpvar != 0x11 && tmpvar != 0x12 &&
- tmpvar != 0x21 && tmpvar != 0x22 && /* Core Profile Levels */
- tmpvar != 0xA1 && tmpvar != 0xA2 && tmpvar != 0xA3 &&
- tmpvar != 0xF0 && tmpvar != 0xF1 && /* Advanced Simple Profile Levels*/
- tmpvar != 0xF2 && tmpvar != 0xF3 &&
- tmpvar != 0xF4 && tmpvar != 0xF5)
- return PV_FAIL;
+ switch (tmpvar)
+ {
+ /* Simple Profile Levels */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x08:
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ /* Core Profile Levels */
+ case 0x21:
+ case 0x22:
+ case 0xA1:
+ case 0xA2:
+ case 0xA3:
+ /* Advanced Simple Profile Levels*/
+ case 0xF0:
+ case 0xF1:
+ case 0xF2:
+ case 0xF3:
+ case 0xF4:
+ case 0xF5:
+ // Do Nothing, the cases listed above are supported values
+ break;
+ default:
+ // Unsupport profile level
+ return PV_FAIL;
+ }
}
#else
profile = tmpvar;
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index efa9941..2ff9f5a 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -778,8 +778,16 @@
__INTRODUCED_IN(26);
/**
- * Request an audio device identified device using an ID.
- * On Android, for example, the ID could be obtained from the Java AudioManager.
+ * Request an audio device identified by an ID.
+ *
+ * The ID could be obtained from the Java AudioManager.
+ * AudioManager.getDevices() returns an array of {@link AudioDeviceInfo},
+ * which contains a getId() method. That ID can be passed to this function.
+ *
+ * It is possible that you may not get the device that you requested.
+ * So if it is important to you, you should call
+ * AAudioStream_getDeviceId() after the stream is opened to
+ * verify the actual ID.
*
* The default, if you do not call this function, is {@link #AAUDIO_UNSPECIFIED},
* in which case the primary device will be used.
diff --git a/media/libaaudio/scripts/measure_device_power.py b/media/libaaudio/scripts/measure_device_power.py
index 9603f88..fd7f85f 100755
--- a/media/libaaudio/scripts/measure_device_power.py
+++ b/media/libaaudio/scripts/measure_device_power.py
@@ -225,13 +225,13 @@
line = fp.readline()
while line:
command = line.strip()
- if command.endswith('\\'):
- command = command[:-1].strip() # remove \\:
- runCommand(command)
- elif command.startswith("#"):
+ if command.startswith("#"):
# ignore comment
print((command + "\n"))
- comment = command
+ comment = command[1:].strip() # remove leading '#'
+ elif command.endswith('\\'):
+ command = command[:-1].strip() # remove \\
+ runCommand(command)
elif command:
report = averageEnergyForCommand(command, DEFAULT_NUM_ITERATIONS)
finalReport += comment + ", " + command + ", " + formatEnergyData(report) + "\n"
diff --git a/media/libaaudio/scripts/synthmark_tests.txt b/media/libaaudio/scripts/synthmark_tests.txt
new file mode 100644
index 0000000..24e719d
--- /dev/null
+++ b/media/libaaudio/scripts/synthmark_tests.txt
@@ -0,0 +1,45 @@
+# Measure energy consumption with synthmark.
+
+# ADPF <400 RR
+adb root \
+adb shell setprop vendor.powerhal.adpf.uclamp_min.high_limit 400 \
+adb shell synthmark -tj -n1 -N50 -B2 -z1
+adb shell synthmark -tj -n1 -N75 -B2 -z1
+adb shell synthmark -tj -n1 -N100 -B2 -z1
+
+# ADPF <500 RR
+adb root \
+adb shell setprop vendor.powerhal.adpf.uclamp_min.high_limit 500 \
+adb shell synthmark -tj -n1 -N50 -B2 -z1
+adb shell synthmark -tj -n1 -N75 -B2 -z1
+adb shell synthmark -tj -n1 -N100 -B2 -z1
+
+# ADPF <600 RR
+adb root \
+adb shell setprop vendor.powerhal.adpf.uclamp_min.high_limit 600 \
+adb shell synthmark -tj -n1 -N50 -B2 -z1
+adb shell synthmark -tj -n1 -N75 -B2 -z1
+adb shell synthmark -tj -n1 -N100 -B2 -z1
+
+# None
+adb shell synthmark -tj -n1 -N50 -B2 -z0
+adb shell synthmark -tj -n1 -N75 -B2 -z0
+adb shell synthmark -tj -n1 -N100 -B2 -z0
+
+# uclamp
+# adb root \
+# adb shell synthmark -tj -n1 -N75 -B2 -u1
+
+# steady
+# adb shell synthmark -tj -n75 -B2 -u0
+
+# CPU affinity
+# adb shell synthmark -tj -n1 -N75 -B2 -u0 -c1
+# adb shell synthmark -tj -n1 -N75 -B2 -u0 -c4
+# adb shell synthmark -tj -n1 -N75 -B2 -u0 -c6
+
+# steady + affinity
+# adb shell synthmark -tj -n75 -B2 -u0 -c1
+# adb shell synthmark -tj -n75 -B2 -u0 -c4
+# adb shell synthmark -tj -n75 -B2 -u0 -c6
+
diff --git a/media/libaaudio/tests/test_flowgraph.cpp b/media/libaaudio/tests/test_flowgraph.cpp
index 0354d39..913feb0 100644
--- a/media/libaaudio/tests/test_flowgraph.cpp
+++ b/media/libaaudio/tests/test_flowgraph.cpp
@@ -37,22 +37,69 @@
constexpr int kBytesPerI24Packed = 3;
+// Simple test that tries to reproduce a Clang compiler bug.
+__attribute__((noinline))
+void local_convert_float_to_int16(const float *input,
+ int16_t *output,
+ int count) {
+ for (int i = 0; i < count; i++) {
+ int32_t n = (int32_t) (*input++ * 32768.0f);
+ *output++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
+ }
+}
+
+TEST(test_flowgraph, local_convert_float_int16) {
+ static constexpr int kNumSamples = 8;
+ static constexpr std::array<float, kNumSamples> input = {
+ 1.0f, 0.5f, -0.25f, -1.0f,
+ 0.0f, 53.9f, -87.2f, -1.02f};
+ static constexpr std::array<int16_t, kNumSamples> expected = {
+ 32767, 16384, -8192, -32768,
+ 0, 32767, -32768, -32768};
+ std::array<int16_t, kNumSamples> output;
+
+ // Do it inline, which will probably work even with the buggy compiler.
+ // This validates the expected data.
+ const float *in = input.data();
+ int16_t *out = output.data();
+ output.fill(777);
+ for (int i = 0; i < kNumSamples; i++) {
+ int32_t n = (int32_t) (*in++ * 32768.0f);
+ *out++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
+ }
+ for (int i = 0; i < kNumSamples; i++) {
+ EXPECT_EQ(expected.at(i), output.at(i)) << ", i = " << i;
+ }
+
+ // Convert audio signal using the function.
+ output.fill(777);
+ local_convert_float_to_int16(input.data(), output.data(), kNumSamples);
+ for (int i = 0; i < kNumSamples; i++) {
+ EXPECT_EQ(expected.at(i), output.at(i)) << ", i = " << i;
+ }
+}
+
TEST(test_flowgraph, module_sinki16) {
- static const float input[] = {1.0f, 0.5f, -0.25f, -1.0f, 0.0f, 53.9f, -87.2f};
- static const int16_t expected[] = {32767, 16384, -8192, -32768, 0, 32767, -32768};
- int16_t output[20];
+ static constexpr int kNumSamples = 8;
+ static constexpr std::array<float, kNumSamples> input = {
+ 1.0f, 0.5f, -0.25f, -1.0f,
+ 0.0f, 53.9f, -87.2f, -1.02f};
+ static constexpr std::array<int16_t, kNumSamples> expected = {
+ 32767, 16384, -8192, -32768,
+ 0, 32767, -32768, -32768};
+ std::array<int16_t, kNumSamples + 10> output; // larger than input
+
SourceFloat sourceFloat{1};
SinkI16 sinkI16{1};
- int numInputFrames = sizeof(input) / sizeof(input[0]);
- sourceFloat.setData(input, numInputFrames);
+ sourceFloat.setData(input.data(), kNumSamples);
sourceFloat.output.connect(&sinkI16.input);
- int numOutputFrames = sizeof(output) / sizeof(int16_t);
- int32_t numRead = sinkI16.read(output, numOutputFrames);
- ASSERT_EQ(numInputFrames, numRead);
+ output.fill(777);
+ int32_t numRead = sinkI16.read(output.data(), output.size());
+ ASSERT_EQ(kNumSamples, numRead);
for (int i = 0; i < numRead; i++) {
- EXPECT_EQ(expected[i], output[i]);
+ EXPECT_EQ(expected.at(i), output.at(i)) << ", i = " << i;
}
}
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index a7b10b2..9a7b9c1 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -959,7 +959,8 @@
audio_output_flags_t flags,
audio_port_handle_t* selectedDeviceId,
audio_port_handle_t* portId,
- std::vector<audio_io_handle_t>* secondaryOutputs) {
+ std::vector<audio_io_handle_t>* secondaryOutputs,
+ bool *isSpatialized) {
if (attr == nullptr) {
ALOGE("%s NULL audio attributes", __func__);
return BAD_VALUE;
@@ -1012,6 +1013,7 @@
*portId = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_port_handle_t(responseAidl.portId));
*secondaryOutputs = VALUE_OR_RETURN_STATUS(convertContainer<std::vector<audio_io_handle_t>>(
responseAidl.secondaryOutputs, aidl2legacy_int32_t_audio_io_handle_t));
+ *isSpatialized = responseAidl.isSpatialized;
return OK;
}
diff --git a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
index 963877a..f1848b6 100644
--- a/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
+++ b/media/libaudioclient/aidl/android/media/GetOutputForAttrResponse.aidl
@@ -31,4 +31,6 @@
int portId;
/** Interpreted as audio_io_handle_t[]. */
int[] secondaryOutputs;
+ /** True if the track is connected to a spatializer mixer and actually spatialized */
+ boolean isSpatialized;
}
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index e89ce15..1cc22a0 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -286,7 +286,8 @@
audio_output_flags_t flags,
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
- std::vector<audio_io_handle_t> *secondaryOutputs);
+ std::vector<audio_io_handle_t> *secondaryOutputs,
+ bool *isSpatialized);
static status_t startOutput(audio_port_handle_t portId);
static status_t stopOutput(audio_port_handle_t portId);
static void releaseOutput(audio_port_handle_t portId);
diff --git a/media/libaudiohal/DevicesFactoryHalInterface.cpp b/media/libaudiohal/DevicesFactoryHalInterface.cpp
index 325a547..5ad26fc 100644
--- a/media/libaudiohal/DevicesFactoryHalInterface.cpp
+++ b/media/libaudiohal/DevicesFactoryHalInterface.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string>
+
#include <media/audiohal/DevicesFactoryHalInterface.h>
#include <media/audiohal/FactoryHalHidl.h>
@@ -21,8 +23,10 @@
// static
sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
+ using namespace std::string_literals;
return createPreferredImpl<DevicesFactoryHalInterface>(
- "android.hardware.audio", "IDevicesFactory");
+ std::make_pair("android.hardware.audio"s, "IDevicesFactory"s),
+ std::make_pair("android.hardware.audio.effect"s, "IEffectsFactory"s));
}
} // namespace android
diff --git a/media/libaudiohal/EffectsFactoryHalInterface.cpp b/media/libaudiohal/EffectsFactoryHalInterface.cpp
index bc3b4c1..8a28f64 100644
--- a/media/libaudiohal/EffectsFactoryHalInterface.cpp
+++ b/media/libaudiohal/EffectsFactoryHalInterface.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string>
+
#include <media/audiohal/EffectsFactoryHalInterface.h>
#include <media/audiohal/FactoryHalHidl.h>
@@ -21,8 +23,10 @@
// static
sp<EffectsFactoryHalInterface> EffectsFactoryHalInterface::create() {
+ using namespace std::string_literals;
return createPreferredImpl<EffectsFactoryHalInterface>(
- "android.hardware.audio.effect", "IEffectsFactory");
+ std::make_pair("android.hardware.audio.effect"s, "IEffectsFactory"s),
+ std::make_pair("android.hardware.audio"s, "IDevicesFactory"s));
}
// static
diff --git a/media/libaudiohal/FactoryHalHidl.cpp b/media/libaudiohal/FactoryHalHidl.cpp
index 804edcc..590fec5 100644
--- a/media/libaudiohal/FactoryHalHidl.cpp
+++ b/media/libaudiohal/FactoryHalHidl.cpp
@@ -16,6 +16,10 @@
#define LOG_TAG "FactoryHalHidl"
+#include <algorithm>
+#include <array>
+#include <utility>
+
#include <media/audiohal/FactoryHalHidl.h>
#include <dlfcn.h>
@@ -28,15 +32,16 @@
namespace android::detail {
namespace {
-/** Supported HAL versions, in order of preference.
+/** Supported HAL versions, from most recent to least recent.
*/
-const char* sAudioHALVersions[] = {
- "7.1",
- "7.0",
- "6.0",
- "5.0",
- "4.0",
- nullptr
+#define CONC_VERSION(maj, min) #maj "." #min
+#define DECLARE_VERSION(maj, min) std::make_pair(std::make_pair(maj, min), CONC_VERSION(maj, min))
+static constexpr std::array<std::pair<std::pair<int, int>, const char*>, 5> sAudioHALVersions = {
+ DECLARE_VERSION(7, 1),
+ DECLARE_VERSION(7, 0),
+ DECLARE_VERSION(6, 0),
+ DECLARE_VERSION(5, 0),
+ DECLARE_VERSION(4, 0)
};
bool createHalService(const std::string& version, const std::string& interface,
@@ -94,11 +99,22 @@
} // namespace
-void* createPreferredImpl(const std::string& package, const std::string& interface) {
- for (auto version = detail::sAudioHALVersions; *version != nullptr; ++version) {
- void* rawInterface = nullptr;
- if (hasHalService(package, *version, interface)
- && createHalService(*version, interface, &rawInterface)) {
+void* createPreferredImpl(const InterfaceName& iface, const InterfaceName& siblingIface) {
+ auto findMostRecentVersion = [](const InterfaceName& iface) {
+ return std::find_if(detail::sAudioHALVersions.begin(), detail::sAudioHALVersions.end(),
+ [&](const auto& v) { return hasHalService(iface.first, v.second, iface.second); });
+ };
+ auto ifaceVersionIt = findMostRecentVersion(iface);
+ auto siblingVersionIt = findMostRecentVersion(siblingIface);
+ if (ifaceVersionIt != detail::sAudioHALVersions.end() &&
+ siblingVersionIt != detail::sAudioHALVersions.end() &&
+ // same major version
+ ifaceVersionIt->first.first == siblingVersionIt->first.first) {
+ std::string libraryVersion =
+ ifaceVersionIt->first >= siblingVersionIt->first ?
+ ifaceVersionIt->second : siblingVersionIt->second;
+ void* rawInterface;
+ if (createHalService(libraryVersion, iface.second, &rawInterface)) {
return rawInterface;
}
}
diff --git a/media/libaudiohal/impl/Android.bp b/media/libaudiohal/impl/Android.bp
index 4002fbf..d30883a 100644
--- a/media/libaudiohal/impl/Android.bp
+++ b/media/libaudiohal/impl/Android.bp
@@ -10,6 +10,7 @@
filegroup {
name: "audio_core_hal_client_sources",
srcs: [
+ "CoreConversionHelperHidl.cpp",
"DeviceHalHidl.cpp",
"DevicesFactoryHalHidl.cpp",
"StreamHalHidl.cpp",
@@ -20,6 +21,7 @@
name: "audio_effect_hal_client_sources",
srcs: [
"EffectBufferHalHidl.cpp",
+ "EffectConversionHelperHidl.cpp",
"EffectHalHidl.cpp",
"EffectsFactoryHalHidl.cpp",
],
@@ -28,10 +30,6 @@
cc_defaults {
name: "libaudiohal_default",
- srcs: [
- "ConversionHelperHidl.cpp",
- ],
-
cflags: [
"-Wall",
"-Wextra",
@@ -76,6 +74,7 @@
srcs: [
":audio_core_hal_client_sources",
":audio_effect_hal_client_sources",
+ "EffectsFactoryHalHidlEntry.cpp",
],
shared_libs: [
"android.hardware.audio.common@4.0",
@@ -98,6 +97,7 @@
srcs: [
":audio_core_hal_client_sources",
":audio_effect_hal_client_sources",
+ "EffectsFactoryHalHidlEntry.cpp",
],
shared_libs: [
"android.hardware.audio.common@5.0",
@@ -120,6 +120,7 @@
srcs: [
":audio_core_hal_client_sources",
":audio_effect_hal_client_sources",
+ "EffectsFactoryHalHidlEntry.cpp",
],
shared_libs: [
"android.hardware.audio.common@6.0",
@@ -136,12 +137,31 @@
]
}
+cc_library_static {
+ name: "libaudiohal.effect@7.0",
+ defaults: ["libaudiohal_default"],
+ srcs: [
+ ":audio_effect_hal_client_sources",
+ ],
+ static_libs: [
+ "android.hardware.audio.common@7.0",
+ "android.hardware.audio.common@7.0-util",
+ "android.hardware.audio.effect@7.0",
+ "android.hardware.audio.effect@7.0-util",
+ ],
+ cflags: [
+ "-DMAJOR_VERSION=7",
+ "-DMINOR_VERSION=0",
+ "-include common/all-versions/VersionMacro.h",
+ ]
+}
+
cc_library_shared {
name: "libaudiohal@7.0",
defaults: ["libaudiohal_default"],
srcs: [
":audio_core_hal_client_sources",
- ":audio_effect_hal_client_sources",
+ "EffectsFactoryHalHidlEntry.cpp",
],
static_libs: [
"android.hardware.audio.common@7.0",
@@ -151,6 +171,7 @@
"android.hardware.audio.effect@7.0-util",
"android.hardware.audio@7.0",
"android.hardware.audio@7.0-util",
+ "libaudiohal.effect@7.0",
],
cflags: [
"-DMAJOR_VERSION=7",
@@ -164,14 +185,19 @@
defaults: ["libaudiohal_default"],
srcs: [
":audio_core_hal_client_sources",
+ "EffectsFactoryHalHidlEntry.cpp",
],
static_libs: [
"android.hardware.audio.common@7.0",
+ "android.hardware.audio.common@7.0-util",
"android.hardware.audio.common@7.1-enums",
"android.hardware.audio.common@7.1-util",
+ "android.hardware.audio.effect@7.0",
+ "android.hardware.audio.effect@7.0-util",
"android.hardware.audio@7.0",
"android.hardware.audio@7.1",
"android.hardware.audio@7.1-util",
+ "libaudiohal.effect@7.0",
],
cflags: [
"-DMAJOR_VERSION=7",
diff --git a/media/libaudiohal/impl/ConversionHelperHidl.h b/media/libaudiohal/impl/ConversionHelperHidl.h
index 9368551..6e2c831 100644
--- a/media/libaudiohal/impl/ConversionHelperHidl.h
+++ b/media/libaudiohal/impl/ConversionHelperHidl.h
@@ -17,33 +17,25 @@
#ifndef ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
#define ANDROID_HARDWARE_CONVERSION_HELPER_HIDL_H
-#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/types.h)
+#include <functional>
+
#include <hidl/HidlSupport.h>
#include <system/audio.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::ParameterValue;
-using CoreResult = ::android::hardware::audio::CORE_TYPES_CPP_VERSION::Result;
-
-using ::android::hardware::Return;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
namespace android {
+template<typename HalResult>
class ConversionHelperHidl {
protected:
- static status_t keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys);
- static status_t parametersFromHal(const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams);
- static void parametersToHal(const hidl_vec<ParameterValue>& parameters, String8 *values);
- static void argsFromHal(const Vector<String16>& args, hidl_vec<hidl_string> *hidlArgs);
+ using HalResultConverter = std::function<status_t(const HalResult&)>;
+ const std::string mClassName;
- ConversionHelperHidl(const char* className);
+ ConversionHelperHidl(std::string_view className, HalResultConverter resultConv)
+ : mClassName(className), mResultConverter(resultConv) {}
template<typename R, typename T>
- status_t processReturn(const char* funcName, const Return<R>& ret, T *retval) {
+ status_t processReturn(const char* funcName,
+ const ::android::hardware::Return<R>& ret, T *retval) {
if (ret.isOk()) {
// This way it also works for enum class to unscoped enum conversion.
*retval = static_cast<T>(static_cast<R>(ret));
@@ -53,35 +45,40 @@
}
template<typename T>
- status_t processReturn(const char* funcName, const Return<T>& ret) {
+ status_t processReturn(const char* funcName, const ::android::hardware::Return<T>& ret) {
if (!ret.isOk()) {
emitError(funcName, ret.description().c_str());
}
return ret.isOk() ? OK : FAILED_TRANSACTION;
}
- status_t processReturn(const char* funcName, const Return<CoreResult>& ret) {
+ status_t processReturn(const char* funcName,
+ const ::android::hardware::Return<HalResult>& ret) {
if (!ret.isOk()) {
emitError(funcName, ret.description().c_str());
}
- return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION;
+ return ret.isOk() ? mResultConverter(ret) : FAILED_TRANSACTION;
}
template<typename T>
status_t processReturn(
- const char* funcName, const Return<T>& ret, CoreResult retval) {
+ const char* funcName, const ::android::hardware::Return<T>& ret, HalResult retval) {
if (!ret.isOk()) {
emitError(funcName, ret.description().c_str());
}
- return ret.isOk() ? analyzeResult(retval) : FAILED_TRANSACTION;
+ return ret.isOk() ? mResultConverter(retval) : FAILED_TRANSACTION;
+ }
+
+ const std::string& getClassName() const {
+ return mClassName;
}
private:
- const char* mClassName;
+ HalResultConverter mResultConverter;
- static status_t analyzeResult(const CoreResult& result);
-
- void emitError(const char* funcName, const char* description);
+ void emitError(const char* funcName, const char* description) {
+ ALOGE("%s %p %s: %s (from rpc)", mClassName.c_str(), this, funcName, description);
+ }
};
} // namespace android
diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/CoreConversionHelperHidl.cpp
similarity index 87%
rename from media/libaudiohal/impl/ConversionHelperHidl.cpp
rename to media/libaudiohal/impl/CoreConversionHelperHidl.cpp
index 1d34814..2ac8a42 100644
--- a/media/libaudiohal/impl/ConversionHelperHidl.cpp
+++ b/media/libaudiohal/impl/CoreConversionHelperHidl.cpp
@@ -21,7 +21,7 @@
#include <media/AudioParameter.h>
#include <utils/Log.h>
-#include "ConversionHelperHidl.h"
+#include "CoreConversionHelperHidl.h"
namespace android {
@@ -29,7 +29,8 @@
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
// static
-status_t ConversionHelperHidl::keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys) {
+status_t CoreConversionHelperHidl::keysFromHal(
+ const String8& keys, hidl_vec<hidl_string> *hidlKeys) {
AudioParameter halKeys(keys);
if (halKeys.size() == 0) return BAD_VALUE;
hidlKeys->resize(halKeys.size());
@@ -74,7 +75,7 @@
}
// static
-status_t ConversionHelperHidl::parametersFromHal(
+status_t CoreConversionHelperHidl::parametersFromHal(
const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams) {
AudioParameter params(kvPairs);
if (params.size() == 0) return BAD_VALUE;
@@ -90,7 +91,7 @@
}
// static
-void ConversionHelperHidl::parametersToHal(
+void CoreConversionHelperHidl::parametersToHal(
const hidl_vec<ParameterValue>& parameters, String8 *values) {
AudioParameter params;
for (size_t i = 0; i < parameters.size(); ++i) {
@@ -99,12 +100,11 @@
values->setTo(params.toString());
}
-ConversionHelperHidl::ConversionHelperHidl(const char* className)
- : mClassName(className) {
-}
+CoreConversionHelperHidl::CoreConversionHelperHidl(std::string_view className)
+ : ConversionHelperHidl<CoreResult>(className, analyzeResult) {}
// static
-void ConversionHelperHidl::argsFromHal(
+void CoreConversionHelperHidl::argsFromHal(
const Vector<String16>& args, hidl_vec<hidl_string> *hidlArgs) {
hidlArgs->resize(args.size());
for (size_t i = 0; i < args.size(); ++i) {
@@ -113,19 +113,15 @@
}
// static
-status_t ConversionHelperHidl::analyzeResult(const Result& result) {
+status_t CoreConversionHelperHidl::analyzeResult(const CoreResult& result) {
switch (result) {
case Result::OK: return OK;
case Result::INVALID_ARGUMENTS: return BAD_VALUE;
case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
case Result::NOT_INITIALIZED: return NO_INIT;
case Result::NOT_SUPPORTED: return INVALID_OPERATION;
- default: return NO_INIT;
}
-}
-
-void ConversionHelperHidl::emitError(const char* funcName, const char* description) {
- ALOGE("%s %p %s: %s (from rpc)", mClassName, this, funcName, description);
+ return NO_INIT;
}
} // namespace android
diff --git a/media/libaudiohal/impl/CoreConversionHelperHidl.h b/media/libaudiohal/impl/CoreConversionHelperHidl.h
new file mode 100644
index 0000000..a4d76f3
--- /dev/null
+++ b/media/libaudiohal/impl/CoreConversionHelperHidl.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CORE_CONVERSION_HELPER_HIDL_H
+#define ANDROID_HARDWARE_CORE_CONVERSION_HELPER_HIDL_H
+
+#include "ConversionHelperHidl.h"
+
+#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/types.h)
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::ParameterValue;
+using CoreResult = ::android::hardware::audio::CORE_TYPES_CPP_VERSION::Result;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+
+namespace android {
+
+class CoreConversionHelperHidl : public ConversionHelperHidl<CoreResult> {
+ protected:
+ static status_t keysFromHal(const String8& keys, hidl_vec<hidl_string> *hidlKeys);
+ static status_t parametersFromHal(const String8& kvPairs, hidl_vec<ParameterValue> *hidlParams);
+ static void parametersToHal(const hidl_vec<ParameterValue>& parameters, String8 *values);
+ static void argsFromHal(const Vector<String16>& args, hidl_vec<hidl_string> *hidlArgs);
+
+ CoreConversionHelperHidl(std::string_view className);
+
+ private:
+ static status_t analyzeResult(const CoreResult& result);
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_CORE_CONVERSION_HELPER_HIDL_H
diff --git a/media/libaudiohal/impl/DeviceHalHidl.cpp b/media/libaudiohal/impl/DeviceHalHidl.cpp
index 16863e4..0cdf621 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalHidl.cpp
@@ -23,6 +23,7 @@
#include <cutils/properties.h>
#include <hwbinder/IPCThreadState.h>
#include <media/AudioContainers.h>
+#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
#include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h)
@@ -45,13 +46,16 @@
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
+#define TIME_CHECK() auto timeCheck = \
+ mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__)
+
DeviceHalHidl::DeviceHalHidl(const sp<::android::hardware::audio::CPP_VERSION::IDevice>& device)
- : ConversionHelperHidl("Device"), mDevice(device) {
+ : CoreConversionHelperHidl("DeviceHalHidl"), mDevice(device) {
}
DeviceHalHidl::DeviceHalHidl(
const sp<::android::hardware::audio::CPP_VERSION::IPrimaryDevice>& device)
- : ConversionHelperHidl("Device"),
+ : CoreConversionHelperHidl("DeviceHalHidl"),
#if MAJOR_VERSION <= 6 || (MAJOR_VERSION == 7 && MINOR_VERSION == 0)
mDevice(device),
#endif
@@ -84,22 +88,26 @@
}
status_t DeviceHalHidl::initCheck() {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("initCheck", mDevice->initCheck());
}
status_t DeviceHalHidl::setVoiceVolume(float volume) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
if (mPrimaryDevice == 0) return INVALID_OPERATION;
return processReturn("setVoiceVolume", mPrimaryDevice->setVoiceVolume(volume));
}
status_t DeviceHalHidl::setMasterVolume(float volume) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("setMasterVolume", mDevice->setMasterVolume(volume));
}
status_t DeviceHalHidl::getMasterVolume(float *volume) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
Result retval;
Return<void> ret = mDevice->getMasterVolume(
@@ -113,17 +121,20 @@
}
status_t DeviceHalHidl::setMode(audio_mode_t mode) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
if (mPrimaryDevice == 0) return INVALID_OPERATION;
return processReturn("setMode", mPrimaryDevice->setMode(AudioMode(mode)));
}
status_t DeviceHalHidl::setMicMute(bool state) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("setMicMute", mDevice->setMicMute(state));
}
status_t DeviceHalHidl::getMicMute(bool *state) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
Result retval;
Return<void> ret = mDevice->getMicMute(
@@ -137,11 +148,13 @@
}
status_t DeviceHalHidl::setMasterMute(bool state) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("setMasterMute", mDevice->setMasterMute(state));
}
status_t DeviceHalHidl::getMasterMute(bool *state) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
Result retval;
Return<void> ret = mDevice->getMasterMute(
@@ -155,6 +168,7 @@
}
status_t DeviceHalHidl::setParameters(const String8& kvPairs) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
hidl_vec<ParameterValue> hidlParams;
status_t status = parametersFromHal(kvPairs, &hidlParams);
@@ -165,6 +179,7 @@
}
status_t DeviceHalHidl::getParameters(const String8& keys, String8 *values) {
+ TIME_CHECK();
values->clear();
if (mDevice == 0) return NO_INIT;
hidl_vec<hidl_string> hidlKeys;
@@ -185,6 +200,7 @@
status_t DeviceHalHidl::getInputBufferSize(
const struct audio_config *config, size_t *size) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
AudioConfig hidlConfig;
HidlUtils::audioConfigFromHal(*config, true /*isInput*/, &hidlConfig);
@@ -207,6 +223,7 @@
struct audio_config *config,
const char *address,
sp<StreamOutHalInterface> *outStream) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
DeviceAddress hidlDevice;
if (status_t status = CoreUtils::deviceAddressFromHal(deviceType, address, &hidlDevice);
@@ -263,6 +280,7 @@
audio_devices_t outputDevice,
const char *outputDeviceAddress,
sp<StreamInHalInterface> *inStream) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
DeviceAddress hidlDevice;
if (status_t status = CoreUtils::deviceAddressFromHal(devices, address, &hidlDevice);
@@ -326,6 +344,7 @@
}
status_t DeviceHalHidl::supportsAudioPatches(bool *supportsPatches) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("supportsAudioPatches", mDevice->supportsAudioPatches(), supportsPatches);
}
@@ -336,6 +355,7 @@
unsigned int num_sinks,
const struct audio_port_config *sinks,
audio_patch_handle_t *patch) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
if (patch == nullptr) return BAD_VALUE;
@@ -381,6 +401,7 @@
}
status_t DeviceHalHidl::releaseAudioPatch(audio_patch_handle_t patch) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("releaseAudioPatch", mDevice->releaseAudioPatch(patch));
}
@@ -403,10 +424,12 @@
}
status_t DeviceHalHidl::getAudioPort(struct audio_port *port) {
+ TIME_CHECK();
return getAudioPortImpl(port);
}
status_t DeviceHalHidl::getAudioPort(struct audio_port_v7 *port) {
+ TIME_CHECK();
#if MAJOR_VERSION >= 7
return getAudioPortImpl(port);
#else
@@ -427,6 +450,7 @@
}
status_t DeviceHalHidl::setAudioPortConfig(const struct audio_port_config *config) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
AudioPortConfig hidlConfig;
HidlUtils::audioPortConfigFromHal(*config, &hidlConfig);
@@ -441,6 +465,7 @@
}
#elif MAJOR_VERSION >= 4
status_t DeviceHalHidl::getMicrophones(std::vector<media::MicrophoneInfo> *microphonesInfo) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
Result retval;
Return<void> ret = mDevice->getMicrophones(
@@ -461,6 +486,7 @@
#if MAJOR_VERSION >= 6
status_t DeviceHalHidl::addDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("addDeviceEffect", mDevice->addDeviceEffect(
static_cast<AudioPortHandle>(device), effect->effectId()));
@@ -475,6 +501,7 @@
#if MAJOR_VERSION >= 6
status_t DeviceHalHidl::removeDeviceEffect(
audio_port_handle_t device, sp<EffectHalInterface> effect) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
return processReturn("removeDeviceEffect", mDevice->removeDeviceEffect(
static_cast<AudioPortHandle>(device), effect->effectId()));
@@ -487,6 +514,7 @@
#endif
status_t DeviceHalHidl::setConnectedState(const struct audio_port_v7 *port, bool connected) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
if (supportsSetConnectedState7_1) {
@@ -513,6 +541,7 @@
}
error::Result<audio_hw_sync_t> DeviceHalHidl::getHwAvSync() {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
audio_hw_sync_t value;
Result result;
@@ -525,6 +554,7 @@
}
status_t DeviceHalHidl::dump(int fd, const Vector<String16>& args) {
+ TIME_CHECK();
if (mDevice == 0) return NO_INIT;
native_handle_t* hidlHandle = native_handle_create(1, 0);
hidlHandle->data[0] = fd;
diff --git a/media/libaudiohal/impl/DeviceHalHidl.h b/media/libaudiohal/impl/DeviceHalHidl.h
index 8a97a55..f6519b6 100644
--- a/media/libaudiohal/impl/DeviceHalHidl.h
+++ b/media/libaudiohal/impl/DeviceHalHidl.h
@@ -22,11 +22,11 @@
#include <media/audiohal/DeviceHalInterface.h>
#include <media/audiohal/EffectHalInterface.h>
-#include "ConversionHelperHidl.h"
+#include "CoreConversionHelperHidl.h"
namespace android {
-class DeviceHalHidl : public DeviceHalInterface, public ConversionHelperHidl
+class DeviceHalHidl : public DeviceHalInterface, public CoreConversionHelperHidl
{
public:
// Sets the value of 'devices' to a bitmask of 1 or more values of audio_devices_t.
diff --git a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
index 8f3c907..4069a6b 100644
--- a/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/DevicesFactoryHalHidl.cpp
@@ -26,7 +26,6 @@
#include <media/audiohal/hidl/HalDeathHandler.h>
#include <utils/Log.h>
-#include "ConversionHelperHidl.h"
#include "DeviceHalHidl.h"
#include "DevicesFactoryHalHidl.h"
diff --git a/media/libaudiohal/impl/EffectBufferHalHidl.cpp b/media/libaudiohal/impl/EffectBufferHalHidl.cpp
index 65297af..9d5f72e 100644
--- a/media/libaudiohal/impl/EffectBufferHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectBufferHalHidl.cpp
@@ -23,7 +23,6 @@
#include <hidlmemory/mapping.h>
#include <utils/Log.h>
-#include "ConversionHelperHidl.h"
#include "EffectBufferHalHidl.h"
using ::android::hardware::Return;
diff --git a/media/libaudiohal/impl/EffectConversionHelperHidl.cpp b/media/libaudiohal/impl/EffectConversionHelperHidl.cpp
new file mode 100644
index 0000000..9e4f79c
--- /dev/null
+++ b/media/libaudiohal/impl/EffectConversionHelperHidl.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HalHidl"
+#include <utils/Log.h>
+
+#include "EffectConversionHelperHidl.h"
+
+namespace android {
+
+EffectConversionHelperHidl::EffectConversionHelperHidl(std::string_view className)
+ : ConversionHelperHidl<EffectResult>(className, analyzeResult) {
+}
+
+// static
+status_t EffectConversionHelperHidl::analyzeResult(const EffectResult& result) {
+ switch (result) {
+ case EffectResult::OK: return OK;
+ case EffectResult::INVALID_ARGUMENTS: return BAD_VALUE;
+ case EffectResult::INVALID_STATE: return NOT_ENOUGH_DATA;
+ case EffectResult::NOT_INITIALIZED: return NO_INIT;
+ case EffectResult::NOT_SUPPORTED: return INVALID_OPERATION;
+ case EffectResult::RESULT_TOO_BIG: return NO_MEMORY;
+ }
+ return NO_INIT;
+}
+
+} // namespace android
diff --git a/media/libaudiohal/impl/EffectConversionHelperHidl.h b/media/libaudiohal/impl/EffectConversionHelperHidl.h
new file mode 100644
index 0000000..4371d12
--- /dev/null
+++ b/media/libaudiohal/impl/EffectConversionHelperHidl.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_EFFECT_CONVERSION_HELPER_HIDL_H
+#define ANDROID_HARDWARE_EFFECT_CONVERSION_HELPER_HIDL_H
+
+#include "ConversionHelperHidl.h"
+
+#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h)
+
+using EffectResult = ::android::hardware::audio::effect::CPP_VERSION::Result;
+
+namespace android {
+
+class EffectConversionHelperHidl : public ConversionHelperHidl<EffectResult> {
+ protected:
+ static status_t analyzeResult(const EffectResult& result);
+
+ EffectConversionHelperHidl(std::string_view className);
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_EFFECT_CONVERSION_HELPER_HIDL_H
diff --git a/media/libaudiohal/impl/EffectHalHidl.cpp b/media/libaudiohal/impl/EffectHalHidl.cpp
index 1bb1e5f..fdfe225 100644
--- a/media/libaudiohal/impl/EffectHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectHalHidl.cpp
@@ -21,6 +21,7 @@
#include <cutils/native_handle.h>
#include <hwbinder/IPCThreadState.h>
#include <media/EffectsFactoryApi.h>
+#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
#include <util/EffectUtils.h>
@@ -40,8 +41,12 @@
using namespace ::android::hardware::audio::common::CPP_VERSION;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
+#define TIME_CHECK() auto timeCheck = \
+ mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__)
+
EffectHalHidl::EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId)
- : mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) {
+ : EffectConversionHelperHidl("EffectHalHidl"),
+ mEffect(effect), mEffectId(effectId), mBuffersChanged(true), mEfGroup(nullptr) {
effect_descriptor_t halDescriptor{};
if (EffectHalHidl::getDescriptor(&halDescriptor) == NO_ERROR) {
mIsInput = (halDescriptor.flags & EFFECT_FLAG_TYPE_PRE_PROC) == EFFECT_FLAG_TYPE_PRE_PROC;
@@ -59,20 +64,9 @@
}
}
-// static
-status_t EffectHalHidl::analyzeResult(const Result& result) {
- switch (result) {
- case Result::OK: return OK;
- case Result::INVALID_ARGUMENTS: return BAD_VALUE;
- case Result::INVALID_STATE: return NOT_ENOUGH_DATA;
- case Result::NOT_INITIALIZED: return NO_INIT;
- case Result::NOT_SUPPORTED: return INVALID_OPERATION;
- case Result::RESULT_TOO_BIG: return NO_MEMORY;
- default: return NO_INIT;
- }
-}
-
status_t EffectHalHidl::setInBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ TIME_CHECK();
+
if (!mBuffersChanged) {
if (buffer.get() == nullptr || mInBuffer.get() == nullptr) {
mBuffersChanged = buffer.get() != mInBuffer.get();
@@ -85,6 +79,8 @@
}
status_t EffectHalHidl::setOutBuffer(const sp<EffectBufferHalInterface>& buffer) {
+ TIME_CHECK();
+
if (!mBuffersChanged) {
if (buffer.get() == nullptr || mOutBuffer.get() == nullptr) {
mBuffersChanged = buffer.get() != mOutBuffer.get();
@@ -97,10 +93,14 @@
}
status_t EffectHalHidl::process() {
+ TIME_CHECK();
+
return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS));
}
status_t EffectHalHidl::processReverse() {
+ TIME_CHECK();
+
return processImpl(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_PROCESS_REVERSE));
}
@@ -183,6 +183,8 @@
status_t EffectHalHidl::command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
uint32_t *replySize, void *pReplyData) {
+ TIME_CHECK();
+
if (mEffect == 0) return NO_INIT;
// Special cases.
@@ -214,6 +216,8 @@
}
status_t EffectHalHidl::getDescriptor(effect_descriptor_t *pDescriptor) {
+ TIME_CHECK();
+
if (mEffect == 0) return NO_INIT;
Result retval = Result::NOT_INITIALIZED;
Return<void> ret = mEffect->getDescriptor(
@@ -227,12 +231,16 @@
}
status_t EffectHalHidl::close() {
+ TIME_CHECK();
+
if (mEffect == 0) return NO_INIT;
Return<Result> ret = mEffect->close();
return ret.isOk() ? analyzeResult(ret) : FAILED_TRANSACTION;
}
status_t EffectHalHidl::dump(int fd) {
+ TIME_CHECK();
+
if (mEffect == 0) return NO_INIT;
native_handle_t* hidlHandle = native_handle_create(1, 0);
hidlHandle->data[0] = fd;
diff --git a/media/libaudiohal/impl/EffectHalHidl.h b/media/libaudiohal/impl/EffectHalHidl.h
index 07745db..e139768 100644
--- a/media/libaudiohal/impl/EffectHalHidl.h
+++ b/media/libaudiohal/impl/EffectHalHidl.h
@@ -23,6 +23,8 @@
#include <fmq/MessageQueue.h>
#include <system/audio_effect.h>
+#include "EffectConversionHelperHidl.h"
+
using ::android::hardware::EventFlag;
using ::android::hardware::MessageQueue;
@@ -31,7 +33,7 @@
using namespace ::android::hardware::audio::effect::CPP_VERSION;
-class EffectHalHidl : public EffectHalInterface
+class EffectHalHidl : public EffectHalInterface, public EffectConversionHelperHidl
{
public:
// Set the input buffer.
@@ -77,8 +79,6 @@
EventFlag* mEfGroup;
bool mIsInput = false;
- static status_t analyzeResult(const Result& result);
-
// Can not be constructed directly by clients.
EffectHalHidl(const sp<IEffect>& effect, uint64_t effectId);
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
index 90954b2..d7217fc 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
@@ -21,8 +21,9 @@
#include <UuidUtils.h>
#include <util/EffectUtils.h>
+#include <utils/Log.h>
-#include "ConversionHelperHidl.h"
+#include "EffectConversionHelperHidl.h"
#include "EffectBufferHalHidl.h"
#include "EffectHalHidl.h"
#include "EffectsFactoryHalHidl.h"
@@ -38,7 +39,7 @@
using namespace ::android::hardware::audio::effect::CPP_VERSION;
EffectsFactoryHalHidl::EffectsFactoryHalHidl(sp<IEffectsFactory> effectsFactory)
- : ConversionHelperHidl("EffectsFactory") {
+ : EffectConversionHelperHidl("EffectsFactory") {
ALOG_ASSERT(effectsFactory != nullptr, "Provided IEffectsFactory service is NULL");
mEffectsFactory = effectsFactory;
}
@@ -205,7 +206,10 @@
} // namespace effect
-extern "C" __attribute__((visibility("default"))) void* createIEffectsFactory() {
+// When a shared library is built from a static library, even explicit
+// exports from a static library are optimized out unless actually used by
+// the shared library. See EffectsFactoryHalHidlEntry.cpp.
+extern "C" void* createIEffectsFactoryImpl() {
auto service = hardware::audio::effect::CPP_VERSION::IEffectsFactory::getService();
return service ? new effect::EffectsFactoryHalHidl(service) : nullptr;
}
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
index 7491133..e1882e1 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
@@ -20,16 +20,15 @@
#include PATH(android/hardware/audio/effect/FILE_VERSION/IEffectsFactory.h)
#include <media/audiohal/EffectsFactoryHalInterface.h>
-#include "ConversionHelperHidl.h"
+#include "EffectConversionHelperHidl.h"
namespace android {
namespace effect {
using ::android::hardware::hidl_vec;
-using ::android::ConversionHelperHidl;
using namespace ::android::hardware::audio::effect::CPP_VERSION;
-class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public ConversionHelperHidl
+class EffectsFactoryHalHidl : public EffectsFactoryHalInterface, public EffectConversionHelperHidl
{
public:
EffectsFactoryHalHidl(sp<IEffectsFactory> effectsFactory);
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidlEntry.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidlEntry.cpp
new file mode 100644
index 0000000..2c6f2c6
--- /dev/null
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidlEntry.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" void* createIEffectsFactoryImpl();
+
+extern "C" __attribute__((visibility("default"))) void* createIEffectsFactory() {
+ return createIEffectsFactoryImpl();
+}
diff --git a/media/libaudiohal/impl/StreamHalHidl.cpp b/media/libaudiohal/impl/StreamHalHidl.cpp
index 8ba0f72..021ec51 100644
--- a/media/libaudiohal/impl/StreamHalHidl.cpp
+++ b/media/libaudiohal/impl/StreamHalHidl.cpp
@@ -22,6 +22,7 @@
#include <media/AudioParameter.h>
#include <mediautils/memory.h>
#include <mediautils/SchedulingPolicyService.h>
+#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
#include PATH(android/hardware/audio/CORE_TYPES_FILE_VERSION/IStreamOutCallback.h)
@@ -45,8 +46,11 @@
using namespace ::android::hardware::audio::common::COMMON_TYPES_CPP_VERSION;
using namespace ::android::hardware::audio::CORE_TYPES_CPP_VERSION;
-StreamHalHidl::StreamHalHidl(IStream *stream)
- : ConversionHelperHidl("Stream"),
+#define TIME_CHECK() auto TimeCheck = \
+ mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__)
+
+StreamHalHidl::StreamHalHidl(std::string_view className, IStream *stream)
+ : CoreConversionHelperHidl(className),
mStream(stream),
mHalThreadPriority(HAL_THREAD_PRIORITY_DEFAULT),
mCachedBufferSize(0){
@@ -67,6 +71,7 @@
}
status_t StreamHalHidl::getBufferSize(size_t *size) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
status_t status = processReturn("getBufferSize", mStream->getBufferSize(), size);
if (status == OK) {
@@ -76,6 +81,7 @@
}
status_t StreamHalHidl::getAudioProperties(audio_config_base_t *configBase) {
+ TIME_CHECK();
*configBase = AUDIO_CONFIG_BASE_INITIALIZER;
if (!mStream) return NO_INIT;
#if MAJOR_VERSION <= 6
@@ -105,6 +111,7 @@
}
status_t StreamHalHidl::setParameters(const String8& kvPairs) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
hidl_vec<ParameterValue> hidlParams;
status_t status = parametersFromHal(kvPairs, &hidlParams);
@@ -114,6 +121,7 @@
}
status_t StreamHalHidl::getParameters(const String8& keys, String8 *values) {
+ TIME_CHECK();
values->clear();
if (!mStream) return NO_INIT;
hidl_vec<hidl_string> hidlKeys;
@@ -134,21 +142,25 @@
}
status_t StreamHalHidl::addEffect(sp<EffectHalInterface> effect) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("addEffect", mStream->addEffect(effect->effectId()));
}
status_t StreamHalHidl::removeEffect(sp<EffectHalInterface> effect) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("removeEffect", mStream->removeEffect(effect->effectId()));
}
status_t StreamHalHidl::standby() {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("standby", mStream->standby());
}
status_t StreamHalHidl::dump(int fd, const Vector<String16>& args) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
native_handle_t* hidlHandle = native_handle_create(1, 0);
hidlHandle->data[0] = fd;
@@ -173,17 +185,20 @@
}
status_t StreamHalHidl::start() {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("start", mStream->start());
}
status_t StreamHalHidl::stop() {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("stop", mStream->stop());
}
status_t StreamHalHidl::createMmapBuffer(int32_t minSizeFrames,
struct audio_mmap_buffer_info *info) {
+ TIME_CHECK();
Result retval;
Return<void> ret = mStream->createMmapBuffer(
minSizeFrames,
@@ -216,6 +231,7 @@
}
status_t StreamHalHidl::getMmapPosition(struct audio_mmap_position *position) {
+ TIME_CHECK();
Result retval;
Return<void> ret = mStream->getMmapPosition(
[&](Result r, const MmapPosition& hidlPosition) {
@@ -244,7 +260,7 @@
status_t StreamHalHidl::getHalPid(pid_t *pid) {
using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::manager::V1_0::IServiceManager;
-
+ TIME_CHECK();
DebugInfo debugInfo;
auto ret = mStream->getDebugInfo([&] (const auto &info) {
debugInfo = info;
@@ -275,6 +291,7 @@
status_t StreamHalHidl::legacyCreateAudioPatch(const struct audio_port_config& port,
std::optional<audio_source_t> source,
audio_devices_t type) {
+ TIME_CHECK();
LOG_ALWAYS_FATAL_IF(port.type != AUDIO_PORT_TYPE_DEVICE, "port type must be device");
unique_malloced_ptr<char> address;
if (strcmp(port.ext.device.address, "") != 0) {
@@ -293,6 +310,7 @@
}
status_t StreamHalHidl::legacyReleaseAudioPatch() {
+ TIME_CHECK();
AudioParameter param;
param.addInt(String8(AudioParameter::keyRouting), 0);
return setParameters(param.toString());
@@ -352,7 +370,8 @@
StreamOutHalHidl::StreamOutHalHidl(
const sp<::android::hardware::audio::CPP_VERSION::IStreamOut>& stream)
- : StreamHalHidl(stream.get()), mStream(stream), mWriterClient(0), mEfGroup(nullptr) {
+ : StreamHalHidl("StreamOutHalHidl", stream.get())
+ , mStream(stream), mWriterClient(0), mEfGroup(nullptr) {
}
StreamOutHalHidl::~StreamOutHalHidl() {
@@ -376,11 +395,13 @@
}
status_t StreamOutHalHidl::getFrameSize(size_t *size) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("getFrameSize", mStream->getFrameSize(), size);
}
status_t StreamOutHalHidl::getLatency(uint32_t *latency) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
if (mWriterClient == gettid() && mCommandMQ) {
return callWriterThread(
@@ -394,12 +415,14 @@
}
status_t StreamOutHalHidl::setVolume(float left, float right) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("setVolume", mStream->setVolume(left, right));
}
#if MAJOR_VERSION == 2
status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
std::vector<ParameterValue> parameters;
String8 halParameters;
@@ -410,6 +433,7 @@
}
#elif MAJOR_VERSION >= 4
status_t StreamOutHalHidl::selectPresentation(int presentationId, int programId) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("selectPresentation",
mStream->selectPresentation(presentationId, programId));
@@ -417,6 +441,7 @@
#endif
status_t StreamOutHalHidl::write(const void *buffer, size_t bytes, size_t *written) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
*written = 0;
@@ -562,6 +587,7 @@
}
status_t StreamOutHalHidl::getRenderPosition(uint32_t *dspFrames) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getRenderPosition(
@@ -575,6 +601,7 @@
}
status_t StreamOutHalHidl::getNextWriteTimestamp(int64_t *timestamp) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getNextWriteTimestamp(
@@ -588,6 +615,7 @@
}
status_t StreamOutHalHidl::setCallback(wp<StreamOutHalInterfaceCallback> callback) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
status_t status = processReturn(
"setCallback", mStream->setCallback(new StreamOutCallback(this)));
@@ -598,6 +626,7 @@
}
status_t StreamOutHalHidl::supportsPauseAndResume(bool *supportsPause, bool *supportsResume) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
Return<void> ret = mStream->supportsPauseAndResume(
[&](bool p, bool r) {
@@ -608,32 +637,38 @@
}
status_t StreamOutHalHidl::pause() {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("pause", mStream->pause());
}
status_t StreamOutHalHidl::resume() {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("pause", mStream->resume());
}
status_t StreamOutHalHidl::supportsDrain(bool *supportsDrain) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("supportsDrain", mStream->supportsDrain(), supportsDrain);
}
status_t StreamOutHalHidl::drain(bool earlyNotify) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn(
"drain", mStream->drain(earlyNotify ? AudioDrain::EARLY_NOTIFY : AudioDrain::ALL));
}
status_t StreamOutHalHidl::flush() {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("pause", mStream->flush());
}
status_t StreamOutHalHidl::getPresentationPosition(uint64_t *frames, struct timespec *timestamp) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
if (mWriterClient == gettid() && mCommandMQ) {
return callWriterThread(
@@ -667,6 +702,7 @@
#elif MAJOR_VERSION >= 4
status_t StreamOutHalHidl::updateSourceMetadata(
const StreamOutHalInterface::SourceMetadata& sourceMetadata) {
+ TIME_CHECK();
#if MAJOR_VERSION == 4
::android::hardware::audio::CORE_TYPES_CPP_VERSION::SourceMetadata hidlMetadata;
#else
@@ -717,6 +753,7 @@
#else
status_t StreamOutHalHidl::getDualMonoMode(audio_dual_mono_mode_t* mode) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getDualMonoMode(
@@ -730,12 +767,14 @@
}
status_t StreamOutHalHidl::setDualMonoMode(audio_dual_mono_mode_t mode) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn(
"setDualMonoMode", mStream->setDualMonoMode(static_cast<DualMonoMode>(mode)));
}
status_t StreamOutHalHidl::getAudioDescriptionMixLevel(float* leveldB) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getAudioDescriptionMixLevel(
@@ -749,12 +788,14 @@
}
status_t StreamOutHalHidl::setAudioDescriptionMixLevel(float leveldB) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn(
"setAudioDescriptionMixLevel", mStream->setAudioDescriptionMixLevel(leveldB));
}
status_t StreamOutHalHidl::getPlaybackRateParameters(audio_playback_rate_t* playbackRate) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
Result retval;
Return<void> ret = mStream->getPlaybackRateParameters(
@@ -775,6 +816,7 @@
}
status_t StreamOutHalHidl::setPlaybackRateParameters(const audio_playback_rate_t& playbackRate) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn(
"setPlaybackRateParameters", mStream->setPlaybackRateParameters(
@@ -809,6 +851,7 @@
status_t StreamOutHalHidl::setEventCallback(
const sp<StreamOutHalInterfaceEventCallback>& callback) {
+ TIME_CHECK();
if (mStream == nullptr) return NO_INIT;
mEventCallback = callback;
status_t status = processReturn(
@@ -823,12 +866,14 @@
using hardware::audio::V7_1::LatencyMode;
status_t StreamOutHalHidl::setLatencyMode(audio_latency_mode_t mode) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn(
"setLatencyMode", mStream->setLatencyMode(static_cast<LatencyMode>(mode)));
};
status_t StreamOutHalHidl::getRecommendedLatencyModes(std::vector<audio_latency_mode_t> *modes) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
Result retval;
Return<void> ret = mStream->getRecommendedLatencyModes(
@@ -869,6 +914,7 @@
status_t StreamOutHalHidl::setLatencyModeCallback(
const sp<StreamOutHalInterfaceLatencyModeCallback>& callback) {
+ TIME_CHECK();
if (mStream == nullptr) return NO_INIT;
mLatencyModeCallback = callback;
@@ -940,7 +986,8 @@
StreamInHalHidl::StreamInHalHidl(
const sp<::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStreamIn>& stream)
- : StreamHalHidl(stream.get()), mStream(stream), mReaderClient(0), mEfGroup(nullptr) {
+ : StreamHalHidl("StreamInHalHidl", stream.get())
+ , mStream(stream), mReaderClient(0), mEfGroup(nullptr) {
}
StreamInHalHidl::~StreamInHalHidl() {
@@ -953,16 +1000,19 @@
}
status_t StreamInHalHidl::getFrameSize(size_t *size) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("getFrameSize", mStream->getFrameSize(), size);
}
status_t StreamInHalHidl::setGain(float gain) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("setGain", mStream->setGain(gain));
}
status_t StreamInHalHidl::read(void *buffer, size_t bytes, size_t *read) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
*read = 0;
@@ -1090,11 +1140,13 @@
}
status_t StreamInHalHidl::getInputFramesLost(uint32_t *framesLost) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
return processReturn("getInputFramesLost", mStream->getInputFramesLost(), framesLost);
}
status_t StreamInHalHidl::getCapturePosition(int64_t *frames, int64_t *time) {
+ TIME_CHECK();
if (mStream == 0) return NO_INIT;
if (mReaderClient == gettid() && mCommandMQ) {
ReadParameters params;
@@ -1134,6 +1186,7 @@
#elif MAJOR_VERSION >= 4
status_t StreamInHalHidl::getActiveMicrophones(
std::vector<media::MicrophoneInfo> *microphonesInfo) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
Result retval;
Return<void> ret = mStream->getActiveMicrophones(
@@ -1152,6 +1205,7 @@
status_t StreamInHalHidl::updateSinkMetadata(const
StreamInHalInterface::SinkMetadata& sinkMetadata) {
+ TIME_CHECK();
#if MAJOR_VERSION == 4
::android::hardware::audio::CORE_TYPES_CPP_VERSION::SinkMetadata hidlMetadata;
#else
@@ -1179,12 +1233,14 @@
}
#else
status_t StreamInHalHidl::setPreferredMicrophoneDirection(audio_microphone_direction_t direction) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("setPreferredMicrophoneDirection",
mStream->setMicrophoneDirection(static_cast<MicrophoneDirection>(direction)));
}
status_t StreamInHalHidl::setPreferredMicrophoneFieldDimension(float zoom) {
+ TIME_CHECK();
if (!mStream) return NO_INIT;
return processReturn("setPreferredMicrophoneFieldDimension",
mStream->setMicrophoneFieldDimension(zoom));
diff --git a/media/libaudiohal/impl/StreamHalHidl.h b/media/libaudiohal/impl/StreamHalHidl.h
index 4e80e88..54fbefe 100644
--- a/media/libaudiohal/impl/StreamHalHidl.h
+++ b/media/libaudiohal/impl/StreamHalHidl.h
@@ -28,7 +28,7 @@
#include <media/audiohal/StreamHalInterface.h>
#include <mediautils/Synchronization.h>
-#include "ConversionHelperHidl.h"
+#include "CoreConversionHelperHidl.h"
#include "StreamPowerLog.h"
using ::android::hardware::audio::CORE_TYPES_CPP_VERSION::IStream;
@@ -45,7 +45,7 @@
class DeviceHalHidl;
-class StreamHalHidl : public virtual StreamHalInterface, public ConversionHelperHidl
+class StreamHalHidl : public virtual StreamHalInterface, public CoreConversionHelperHidl
{
public:
// Return size of input/output buffer in bytes for this stream - eg. 4800.
@@ -97,7 +97,7 @@
protected:
// Subclasses can not be constructed directly by clients.
- explicit StreamHalHidl(IStream *stream);
+ StreamHalHidl(std::string_view className, IStream *stream);
~StreamHalHidl() override;
diff --git a/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h b/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h
index d353ed0..866dd3e 100644
--- a/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h
+++ b/media/libaudiohal/include/media/audiohal/FactoryHalHidl.h
@@ -18,21 +18,42 @@
#define ANDROID_HARDWARE_FACTORY_HAL_HIDL_H
#include <string>
+#include <utility>
#include <utils/StrongPointer.h>
namespace android {
+// The pair of the interface's package name and the interface name,
+// e.g. <"android.hardware.audio", "IDevicesFactory">.
+// Splitting is used for easier construction of versioned names (FQNs).
+using InterfaceName = std::pair<std::string, std::string>;
+
namespace detail {
-void* createPreferredImpl(const std::string& package, const std::string& interface);
+void* createPreferredImpl(const InterfaceName& iface, const InterfaceName& siblingIface);
} // namespace detail
-/** @Return the preferred available implementation or nullptr if none are available. */
+/**
+ * Create a client for the "preferred" (most recent) implementation of an interface.
+ * by loading the appropriate version of the shared library containing the implementation.
+ *
+ * In the audio HAL, there are two families of interfaces: core and effects. Both are
+ * packed into the same shared library for memory efficiency. Since the core and the effects
+ * interface can have different minor versions on the device, in order to avoid loading multiple
+ * shared libraries the loader function considers which interface among two has the most
+ * recent version. Thus, a pair of interface names must be passed in.
+ *
+ * @param iface the interface that needs to be created.
+ * @param siblingIface the interface which occupies the same shared library.
+ * @return the preferred available implementation or nullptr if none are available.
+ */
template <class Interface>
-static sp<Interface> createPreferredImpl(const std::string& package, const std::string& interface) {
- return sp<Interface>{static_cast<Interface*>(detail::createPreferredImpl(package, interface))};
+static sp<Interface> createPreferredImpl(
+ const InterfaceName& iface, const InterfaceName& siblingIface) {
+ return sp<Interface>{
+ static_cast<Interface*>(detail::createPreferredImpl(iface, siblingIface))};
}
} // namespace android
diff --git a/media/libmediaplayerservice/Android.bp b/media/libmediaplayerservice/Android.bp
index a23d1d9..266cb17 100644
--- a/media/libmediaplayerservice/Android.bp
+++ b/media/libmediaplayerservice/Android.bp
@@ -72,6 +72,7 @@
"media_plugin_headers",
"libmediautils_headers",
"libstagefright_rtsp_headers",
+ "libstagefright_webm_headers",
],
static_libs: [
@@ -79,9 +80,6 @@
"libstagefright_nuplayer",
"libstagefright_rtsp",
"libstagefright_timedtext",
- // this needs it, but it can get it transitively through libstagefright.
- // i'm going to leave it here.
- "libstagefright_webm",
"framework-permission-aidl-cpp",
],
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 50a3f0d..2f3792e 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1460,9 +1460,14 @@
if (mDomain == DOMAIN_VIDEO) {
// video codec needs dedicated looper
if (mCodecLooper == NULL) {
+ status_t err = OK;
mCodecLooper = new ALooper;
mCodecLooper->setName("CodecLooper");
- mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ err = mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ if (OK != err) {
+ ALOGE("Codec Looper failed to start");
+ return err;
+ }
}
mCodecLooper->registerHandler(mCodec);
diff --git a/media/libstagefright/VideoFrameScheduler.cpp b/media/libstagefright/VideoFrameScheduler.cpp
index 4e5b5e2..de8e425 100644
--- a/media/libstagefright/VideoFrameScheduler.cpp
+++ b/media/libstagefright/VideoFrameScheduler.cpp
@@ -22,8 +22,8 @@
#include <utils/String16.h>
#include <binder/IServiceManager.h>
-#include <gui/ISurfaceComposer.h>
-#include <ui/DisplayStatInfo.h>
+#include <android/gui/ISurfaceComposer.h>
+#include <android/gui/DisplayStatInfo.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AUtils.h>
@@ -39,21 +39,22 @@
mVsyncTime = 0;
mVsyncPeriod = 0;
+ // TODO(b/220021255): wrap this into SurfaceComposerClient
if (mComposer == NULL) {
- String16 name("SurfaceFlinger");
+ String16 name("SurfaceFlingerAIDL");
sp<IServiceManager> sm = defaultServiceManager();
- mComposer = interface_cast<ISurfaceComposer>(sm->checkService(name));
+ mComposer = interface_cast<gui::ISurfaceComposer>(sm->checkService(name));
}
if (mComposer != NULL) {
- DisplayStatInfo stats;
- status_t res = mComposer->getDisplayStats(NULL /* display */, &stats);
- if (res == OK) {
+ gui::DisplayStatInfo stats;
+ binder::Status status = mComposer->getDisplayStats(nullptr/* display */, &stats);
+ if (status.isOk()) {
ALOGV("vsync time:%lld period:%lld",
(long long)stats.vsyncTime, (long long)stats.vsyncPeriod);
mVsyncTime = stats.vsyncTime;
mVsyncPeriod = stats.vsyncPeriod;
} else {
- ALOGW("getDisplayStats returned %d", res);
+ ALOGW("getDisplayStats returned %d", status.transactionError());
}
} else {
ALOGW("could not get surface mComposer service");
diff --git a/media/libstagefright/httplive/fuzzer/Android.bp b/media/libstagefright/httplive/fuzzer/Android.bp
index 14097b0..85fd8b7 100644
--- a/media/libstagefright/httplive/fuzzer/Android.bp
+++ b/media/libstagefright/httplive/fuzzer/Android.bp
@@ -40,7 +40,6 @@
"libdatasource",
"libmedia",
"libstagefright",
- "libutils",
],
header_libs: [
"libbase_headers",
@@ -53,6 +52,7 @@
"libstagefright_foundation",
"libhidlbase",
"libhidlmemory",
+ "libutils",
"android.hidl.allocator@1.0",
],
corpus: ["corpus/*"],
diff --git a/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h b/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h
index fcfcbec..a4f512a 100644
--- a/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h
+++ b/media/libstagefright/include/media/stagefright/VideoFrameScheduler.h
@@ -21,7 +21,9 @@
namespace android {
-class ISurfaceComposer;
+namespace gui {
+ class ISurfaceComposer;
+}
struct VideoFrameScheduler : public VideoFrameSchedulerBase {
VideoFrameScheduler();
@@ -32,7 +34,7 @@
private:
void updateVsync() override;
- sp<ISurfaceComposer> mComposer;
+ sp<gui::ISurfaceComposer> mComposer;
};
} // namespace android
diff --git a/media/libstagefright/timedtext/test/Android.bp b/media/libstagefright/timedtext/test/Android.bp
index 58c68ef..ae97c50 100644
--- a/media/libstagefright/timedtext/test/Android.bp
+++ b/media/libstagefright/timedtext/test/Android.bp
@@ -36,7 +36,6 @@
static_libs: [
"libstagefright_timedtext",
- "libstagefright_foundation",
],
header_libs: [
@@ -47,6 +46,7 @@
"liblog",
"libmedia",
"libbinder",
+ "libstagefright_foundation",
],
cflags: [
diff --git a/media/libstagefright/writer_fuzzers/WriterFuzzerBase.cpp b/media/libstagefright/writer_fuzzers/WriterFuzzerBase.cpp
index ee7af70..b97f347 100644
--- a/media/libstagefright/writer_fuzzers/WriterFuzzerBase.cpp
+++ b/media/libstagefright/writer_fuzzers/WriterFuzzerBase.cpp
@@ -174,10 +174,13 @@
params.sampleRate = 16000;
} else {
params.sampleRate = max(1, params.sampleRate);
+ params.channelCount = max(0, params.channelCount);
}
format->setInt32("channel-count", params.channelCount);
format->setInt32("sample-rate", params.sampleRate);
} else if (!strncmp(params.mime, "video/", 6)) {
+ params.width = max(1, params.width);
+ params.height = max(1, params.height);
format->setInt32("width", params.width);
format->setInt32("height", params.height);
}
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 569ea2a..d76602b 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -28,8 +28,12 @@
"AImageReaderUtils.cpp",
"BatteryNotifier.cpp",
"ISchedulingPolicyService.cpp",
+ "Library.cpp",
"LimitProcessMemory.cpp",
+ "MediaUtilsDelayed.cpp",
"MemoryLeakTrackUtil.cpp",
+ "MethodStatistics.cpp",
+ "Process.cpp",
"ProcessInfo.cpp",
"SchedulingPolicyService.cpp",
"ServiceUtilities.cpp",
@@ -75,6 +79,10 @@
"libpermission",
],
+ required: [
+ "libmediautils_delayed", // lazy loaded
+ ],
+
include_dirs: [
// For DEBUGGER_SIGNAL
"system/core/debuggerd/include",
@@ -84,6 +92,23 @@
}
cc_library {
+ name: "libmediautils_delayed", // match with MEDIAUTILS_DELAYED_LIBRARY_NAME
+ srcs: [
+ "MediaUtilsDelayedLibrary.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ shared_libs: [
+ "liblog",
+ "libutils",
+ "libutilscallstack",
+ ],
+}
+
+cc_library {
name: "libmediautils_vendor",
vendor_available: true, // required for platform/hardware/interfaces
srcs: [
diff --git a/media/utils/Library.cpp b/media/utils/Library.cpp
new file mode 100644
index 0000000..c1e22bf
--- /dev/null
+++ b/media/utils/Library.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Library"
+#include <utils/Log.h>
+#include <mediautils/Library.h>
+
+namespace {
+
+std::string dlerrorIfPresent() {
+ const char *dlerr = dlerror();
+ if (dlerr == nullptr) return "dlerror: none";
+ return std::string("dlerror: '").append(dlerr).append("'");
+}
+
+}
+namespace android::mediautils {
+
+std::shared_ptr<void> loadLibrary(const char *libraryName, int flags) {
+ std::shared_ptr<void> library{
+ dlopen(libraryName, flags),
+ [](void *lib) {
+ if (lib != nullptr) {
+ const int ret = dlclose(lib);
+ ALOGW_IF(ret !=0, "%s: dlclose(%p) == %d, %s",
+ __func__, lib, ret, dlerrorIfPresent().c_str());
+ }
+ }
+ };
+
+ if (!library) {
+ ALOGW("%s: cannot load libraryName %s, %s",
+ __func__, libraryName, dlerrorIfPresent().c_str());
+ return {};
+ }
+ return library;
+}
+
+std::shared_ptr<void> getUntypedObjectFromLibrary(
+ const char *objectName, const std::shared_ptr<void>& library) {
+ if (!library) {
+ ALOGW("%s: null library, cannot load objectName %s", __func__, objectName);
+ return {};
+ }
+ void *ptr = dlsym(library.get(), objectName);
+ if (ptr == nullptr) {
+ ALOGW("%s: cannot load objectName %s, %s",
+ __func__, objectName, dlerrorIfPresent().c_str());
+ return {};
+ }
+
+ // Note: we use the "aliasing" constructor of the std:shared_ptr.
+ //
+ // https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
+ //
+ return { library, ptr }; // returns shared_ptr to ptr, but ref counted on library.
+}
+
+} // namespace android::mediautils
diff --git a/media/utils/MediaUtilsDelayed.cpp b/media/utils/MediaUtilsDelayed.cpp
new file mode 100644
index 0000000..c6c092d
--- /dev/null
+++ b/media/utils/MediaUtilsDelayed.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mediautils/Library.h>
+#include <mediautils/MediaUtilsDelayed.h>
+#include "MediaUtilsDelayedLibrary.h"
+
+#define LOG_TAG "MediaUtilsDelayed"
+#include <utils/Log.h>
+#include <memory>
+
+namespace android::mediautils {
+
+namespace {
+// Specific implementation details for MediaUtils Delayed Library.
+
+// The following use static Meyer's singleton caches instead of letting
+// refcounted management as provided above. This is for speed.
+std::shared_ptr<void> getDelayedLibrary() {
+ static std::shared_ptr<void> library = loadLibrary(MEDIAUTILS_DELAYED_LIBRARY_NAME);
+ return library;
+}
+
+// Get the delayed dispatch table. This is refcounted and keeps the underlying library alive.
+std::shared_ptr<delayed_library::DelayedDispatchTable> getDelayedDispatchTable() {
+ static auto delayedDispatchTable =
+ getObjectFromLibrary<delayed_library::DelayedDispatchTable>(
+ MEDIAUTILS_DELAYED_DISPATCH_TABLE_SYMBOL_NAME, getDelayedLibrary());
+ return delayedDispatchTable;
+}
+
+} // namespace
+
+// Public implementations of methods here.
+
+std::string getCallStackStringForTid(pid_t tid) {
+ auto delayedDispatchTable = getDelayedDispatchTable();
+ if (!delayedDispatchTable) return {}; // on failure, return empty string
+ return delayedDispatchTable->getCallStackStringForTid(tid);
+}
+
+} // android::mediautils
diff --git a/media/utils/MediaUtilsDelayedLibrary.cpp b/media/utils/MediaUtilsDelayedLibrary.cpp
new file mode 100644
index 0000000..9054c1a
--- /dev/null
+++ b/media/utils/MediaUtilsDelayedLibrary.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MediaUtilsDelayedLibrary.h"
+#include <utils/CallStack.h>
+
+// Methods that are dynamically linked.
+namespace {
+
+std::string getCallStackStringForTid(pid_t tid) {
+ android::CallStack cs{};
+ cs.update(0 /* ignoreDepth */, tid);
+ return cs.toString().c_str();
+}
+
+} // namespace
+
+// leave global, this is picked up from dynamic linking
+android::mediautils::delayed_library::DelayedDispatchTable gDelayedDispatchTable {
+ getCallStackStringForTid,
+};
diff --git a/media/utils/MediaUtilsDelayedLibrary.h b/media/utils/MediaUtilsDelayedLibrary.h
new file mode 100644
index 0000000..3d72a3a
--- /dev/null
+++ b/media/utils/MediaUtilsDelayedLibrary.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unistd.h>
+
+// This should not be directly included by clients.
+// Use MediaUtilsDelayed.h instead.
+
+namespace android::mediautils::delayed_library {
+
+// Use a dispatch table to return methods from the delayed library
+struct DelayedDispatchTable {
+ std::string (*getCallStackStringForTid)(pid_t tid);
+};
+
+// Match with Android.bp and MediaUtilsDelayed.cpp.
+#define MEDIAUTILS_DELAYED_LIBRARY_NAME "libmediautils_delayed.so"
+
+// Match with MediaUtilsDelayed.cpp and MediaUtilsDelayedLibrary.cpp
+#define MEDIAUTILS_DELAYED_DISPATCH_TABLE_SYMBOL_NAME "gDelayedDispatchTable"
+
+} // namespace android::mediautils::delayed_library
diff --git a/media/utils/MethodStatistics.cpp b/media/utils/MethodStatistics.cpp
new file mode 100644
index 0000000..b179b20
--- /dev/null
+++ b/media/utils/MethodStatistics.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mediautils/MethodStatistics.h>
+
+namespace android::mediautils {
+
+// Repository for MethodStatistics Objects
+
+std::shared_ptr<std::vector<std::string>>
+getStatisticsClassesForModule(std::string_view moduleName) {
+ static const std::map<std::string, std::shared_ptr<std::vector<std::string>>> m {
+ {
+ METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL,
+ std::shared_ptr<std::vector<std::string>>(
+ new std::vector<std::string>{
+ "DeviceHalHidl",
+ "EffectHalHidl",
+ "StreamInHalHidl",
+ "StreamOutHalHidl",
+ })
+ },
+ };
+ auto it = m.find({moduleName.begin(), moduleName.end()});
+ if (it == m.end()) return {};
+ return it->second;
+}
+
+static void addClassesToMap(const std::shared_ptr<std::vector<std::string>> &classNames,
+ std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> &map) {
+ if (classNames) {
+ for (const auto& className : *classNames) {
+ map.emplace(className, std::make_shared<MethodStatistics<std::string>>());
+ }
+ }
+}
+
+// singleton statistics for DeviceHalHidl StreamOutHalHidl StreamInHalHidl
+std::shared_ptr<MethodStatistics<std::string>>
+getStatisticsForClass(std::string_view className) {
+ static const std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m =
+ // copy elided initialization of map m.
+ [](){
+ std::map<std::string, std::shared_ptr<MethodStatistics<std::string>>> m;
+ addClassesToMap(
+ getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL),
+ m);
+ return m;
+ }();
+
+ auto it = m.find({className.begin(), className.end()});
+ if (it == m.end()) return {};
+ return it->second;
+}
+
+} // android::mediautils
diff --git a/media/utils/Process.cpp b/media/utils/Process.cpp
new file mode 100644
index 0000000..8fe8003
--- /dev/null
+++ b/media/utils/Process.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Process"
+#include <utils/Log.h>
+#include <mediautils/Process.h>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <cstdlib>
+
+namespace {
+
+void processLine(std::string_view s, std::map<std::string, double>& m) {
+ if (s.empty()) return;
+
+ const size_t colon_pos = s.find(':');
+ if (colon_pos == std::string_view::npos) return;
+
+ const size_t space_pos = s.find(' ');
+ if (space_pos == 0 || space_pos == std::string_view::npos || space_pos > colon_pos) return;
+ std::string key(s.data(), s.data() + space_pos);
+
+ const size_t value_pos = s.find_first_not_of(' ', colon_pos + 1);
+ if (value_pos == std::string_view::npos) return;
+
+ const double value = strtod(s.data() + value_pos, nullptr /* end */);
+ m[std::move(key)] = value;
+}
+
+} // namespace
+
+namespace android::mediautils {
+
+std::string getThreadSchedAsString(pid_t tid) {
+ const pid_t pid = getpid();
+ const std::string path = std::string("/proc/").append(std::to_string(pid))
+ .append("/task/").append(std::to_string(tid)).append("/sched");
+ std::string sched;
+ (void)android::base::ReadFileToString(path.c_str(), &sched);
+ return sched;
+}
+
+std::map<std::string, double> parseThreadSchedString(const std::string& schedString) {
+ std::map<std::string, double> m;
+ if (schedString.empty()) return m;
+ std::vector<std::string> stringlist = android::base::Split(schedString, "\n");
+
+ // OK we use values not strings... m["summary"] = stringlist[0];
+ for (size_t i = 2; i < stringlist.size(); ++i) {
+ processLine(stringlist[i], m);
+ }
+ return m;
+}
+
+} // namespace android::mediautils
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 5f269af..75a1b22 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -17,23 +17,79 @@
#define LOG_TAG "TimeCheck"
#include <optional>
-#include <sstream>
+#include <android-base/logging.h>
+#include <audio_utils/clock.h>
#include <mediautils/EventLog.h>
+#include <mediautils/MethodStatistics.h>
#include <mediautils/TimeCheck.h>
#include <utils/Log.h>
#include "debuggerd/handler.h"
namespace android::mediautils {
-namespace {
-
+/**
+ * Returns the std::string "HH:MM:SS.MSc" from a system_clock time_point.
+ */
std::string formatTime(std::chrono::system_clock::time_point t) {
- auto msSinceEpoch = std::chrono::round<std::chrono::milliseconds>(t.time_since_epoch());
- return (std::ostringstream() << msSinceEpoch.count()).str();
+ auto time_string = audio_utils_time_string_from_ns(
+ std::chrono::nanoseconds(t.time_since_epoch()).count());
+
+ // The time string is 19 characters (including null termination).
+ // Example: "03-27 16:47:06.187"
+ // MM DD HH MM SS MS
+ // We offset by 6 to get HH:MM:SS.MSc
+ //
+ return time_string.time + 6; // offset to remove month/day.
}
-} // namespace
+/**
+ * Finds the end of the common time prefix.
+ *
+ * This is as an option to remove the common time prefix to avoid
+ * unnecessary duplicated strings.
+ *
+ * \param time1 a time string
+ * \param time2 a time string
+ * \return the position where the common time prefix ends. For abbreviated
+ * printing of time2, offset the character pointer by this position.
+ */
+static size_t commonTimePrefixPosition(std::string_view time1, std::string_view time2) {
+ const size_t endPos = std::min(time1.size(), time2.size());
+ size_t i;
+
+ // Find location of the first mismatch between strings
+ for (i = 0; ; ++i) {
+ if (i == endPos) {
+ return i; // strings match completely to the length of one of the strings.
+ }
+ if (time1[i] != time2[i]) {
+ break;
+ }
+ if (time1[i] == '\0') {
+ return i; // "printed" strings match completely. No need to check further.
+ }
+ }
+
+ // Go backwards until we find a delimeter or space.
+ for (; i > 0
+ && isdigit(time1[i]) // still a number
+ && time1[i - 1] != ' '
+ ; --i) {
+ }
+ return i;
+}
+
+/**
+ * Returns the unique suffix of time2 that isn't present in time1.
+ *
+ * If time2 is identical to time1, then an empty string_view is returned.
+ * This method is used to elide the common prefix when printing times.
+ */
+std::string_view timeSuffix(std::string_view time1, std::string_view time2) {
+ const size_t pos = commonTimePrefixPosition(time1, time2);
+ return time2.substr(pos);
+}
// Audio HAL server pids vector used to generate audio HAL processes tombstone
// when audioserver watchdog triggers.
@@ -75,23 +131,34 @@
return sTimeCheckThread;
}
+/* static */
+std::string TimeCheck::toString() {
+ // note pending and retired are individually locked for maximum concurrency,
+ // snapshot is not instantaneous at a single time.
+ return getTimeCheckThread().toString();
+}
+
TimeCheck::TimeCheck(std::string tag, OnTimerFunc&& onTimer, uint32_t timeoutMs,
bool crashOnTimeout)
: mTimeCheckHandler(new TimeCheckHandler{
std::move(tag), std::move(onTimer), crashOnTimeout,
std::chrono::system_clock::now(), gettid()})
- , mTimerHandle(getTimeCheckThread().scheduleTask(
- // Pass in all the arguments by value to this task for safety.
- // The thread could call the callback before the constructor is finished.
- // The destructor will be blocked on the callback, but that is implementation
- // dependent.
- [ timeCheckHandler = mTimeCheckHandler ] {
- timeCheckHandler->onTimeout();
- },
- std::chrono::milliseconds(timeoutMs))) {}
+ , mTimerHandle(timeoutMs == 0
+ ? getTimeCheckThread().trackTask(mTimeCheckHandler->tag)
+ : getTimeCheckThread().scheduleTask(
+ mTimeCheckHandler->tag,
+ // Pass in all the arguments by value to this task for safety.
+ // The thread could call the callback before the constructor is finished.
+ // The destructor is not blocked on callback.
+ [ timeCheckHandler = mTimeCheckHandler ] {
+ timeCheckHandler->onTimeout();
+ },
+ std::chrono::milliseconds(timeoutMs))) {}
TimeCheck::~TimeCheck() {
- mTimeCheckHandler->onCancel(mTimerHandle);
+ if (mTimeCheckHandler) {
+ mTimeCheckHandler->onCancel(mTimerHandle);
+ }
}
void TimeCheck::TimeCheckHandler::onCancel(TimerThread::Handle timerHandle) const
@@ -115,6 +182,10 @@
if (!crashOnTimeout) return;
+ // Generate the TimerThread summary string early before sending signals to the
+ // HAL processes which can affect thread behavior.
+ const std::string summary = getTimeCheckThread().toString(4 /* retiredCount */);
+
// Generate audio HAL processes tombstones and allow time to complete
// before forcing restart
std::vector<pid_t> pids = TimeCheck::getAudioHalPids();
@@ -127,9 +198,45 @@
} else {
ALOGI("No HAL process pid available, skipping tombstones");
}
+
LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag.c_str());
- LOG_ALWAYS_FATAL("TimeCheck timeout for %s on thread %d (start=%s, end=%s)",
- tag.c_str(), tid, formatTime(startTime).c_str(), formatTime(endTime).c_str());
+
+ // Create abort message string - caution: this can be very large.
+ const std::string abortMessage = std::string("TimeCheck timeout for ")
+ .append(tag)
+ .append(" scheduled ").append(formatTime(startTime))
+ .append(" on thread ").append(std::to_string(tid)).append("\n")
+ .append(summary);
+
+ // Note: LOG_ALWAYS_FATAL limits the size of the string - per log/log.h:
+ // Log message text may be truncated to less than an
+ // implementation-specific limit (1023 bytes).
+ //
+ // Here, we send the string through android-base/logging.h LOG()
+ // to avoid the size limitation. LOG(FATAL) does an abort whereas
+ // LOG(FATAL_WITHOUT_ABORT) does not abort.
+
+ LOG(FATAL) << abortMessage;
+}
+
+// Automatically create a TimeCheck class for a class and method.
+// This is used for Audio HIDL support.
+mediautils::TimeCheck makeTimeCheckStatsForClassMethod(
+ std::string_view className, std::string_view methodName) {
+ std::shared_ptr<MethodStatistics<std::string>> statistics =
+ mediautils::getStatisticsForClass(className);
+ if (!statistics) return {}; // empty TimeCheck.
+ return mediautils::TimeCheck(
+ std::string(className).append("::").append(methodName),
+ [ clazz = std::string(className), method = std::string(methodName),
+ stats = std::move(statistics) ]
+ (bool timeout, float elapsedMs) {
+ if (timeout) {
+ ; // ignored, there is no timeout value.
+ } else {
+ stats->event(method, elapsedMs);
+ }
+ }, 0 /* timeoutMs */);
}
} // namespace android::mediautils
diff --git a/media/utils/TimerThread-test.cpp b/media/utils/TimerThread-test.cpp
index ee8a811..93cd64c 100644
--- a/media/utils/TimerThread-test.cpp
+++ b/media/utils/TimerThread-test.cpp
@@ -20,54 +20,71 @@
#include <mediautils/TimerThread.h>
using namespace std::chrono_literals;
+using namespace android::mediautils;
-namespace android {
namespace {
constexpr auto kJitter = 10ms;
+// Each task written by *ToString() will start with a left brace.
+constexpr char REQUEST_START = '{';
+
+inline size_t countChars(std::string_view s, char c) {
+ return std::count(s.begin(), s.end(), c);
+}
+
TEST(TimerThread, Basic) {
std::atomic<bool> taskRan = false;
TimerThread thread;
- thread.scheduleTask([&taskRan] { taskRan = true; }, 100ms);
+ thread.scheduleTask("Basic", [&taskRan] { taskRan = true; }, 100ms);
std::this_thread::sleep_for(100ms - kJitter);
ASSERT_FALSE(taskRan);
std::this_thread::sleep_for(2 * kJitter);
ASSERT_TRUE(taskRan);
+ ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, Cancel) {
std::atomic<bool> taskRan = false;
TimerThread thread;
- TimerThread::Handle handle = thread.scheduleTask([&taskRan] { taskRan = true; }, 100ms);
+ TimerThread::Handle handle =
+ thread.scheduleTask("Cancel", [&taskRan] { taskRan = true; }, 100ms);
std::this_thread::sleep_for(100ms - kJitter);
ASSERT_FALSE(taskRan);
- thread.cancelTask(handle);
+ ASSERT_TRUE(thread.cancelTask(handle));
std::this_thread::sleep_for(2 * kJitter);
ASSERT_FALSE(taskRan);
+ ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, CancelAfterRun) {
std::atomic<bool> taskRan = false;
TimerThread thread;
- TimerThread::Handle handle = thread.scheduleTask([&taskRan] { taskRan = true; }, 100ms);
+ TimerThread::Handle handle =
+ thread.scheduleTask("CancelAfterRun", [&taskRan] { taskRan = true; }, 100ms);
std::this_thread::sleep_for(100ms + kJitter);
ASSERT_TRUE(taskRan);
- thread.cancelTask(handle);
+ ASSERT_FALSE(thread.cancelTask(handle));
+ ASSERT_EQ(1, countChars(thread.retiredToString(), REQUEST_START));
}
TEST(TimerThread, MultipleTasks) {
- std::array<std::atomic<bool>, 6> taskRan;
+ std::array<std::atomic<bool>, 6> taskRan{};
TimerThread thread;
auto startTime = std::chrono::steady_clock::now();
- thread.scheduleTask([&taskRan] { taskRan[0] = true; }, 300ms);
- thread.scheduleTask([&taskRan] { taskRan[1] = true; }, 100ms);
- thread.scheduleTask([&taskRan] { taskRan[2] = true; }, 200ms);
- thread.scheduleTask([&taskRan] { taskRan[3] = true; }, 400ms);
- auto handle4 = thread.scheduleTask([&taskRan] { taskRan[4] = true; }, 200ms);
- thread.scheduleTask([&taskRan] { taskRan[5] = true; }, 200ms);
+ thread.scheduleTask("0", [&taskRan] { taskRan[0] = true; }, 300ms);
+ thread.scheduleTask("1", [&taskRan] { taskRan[1] = true; }, 100ms);
+ thread.scheduleTask("2", [&taskRan] { taskRan[2] = true; }, 200ms);
+ thread.scheduleTask("3", [&taskRan] { taskRan[3] = true; }, 400ms);
+ auto handle4 = thread.scheduleTask("4", [&taskRan] { taskRan[4] = true; }, 200ms);
+ thread.scheduleTask("5", [&taskRan] { taskRan[5] = true; }, 200ms);
+
+ // 6 tasks pending
+ ASSERT_EQ(6, countChars(thread.pendingToString(), REQUEST_START));
+ // 0 tasks completed
+ ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
// Task 1 should trigger around 100ms.
std::this_thread::sleep_until(startTime + 100ms - kJitter);
@@ -123,6 +140,11 @@
ASSERT_FALSE(taskRan[4]);
ASSERT_TRUE(taskRan[5]);
+ // 1 task pending
+ ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ // 4 tasks ran and 1 cancelled
+ ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
+
// Task 3 should trigger around 400ms.
std::this_thread::sleep_until(startTime + 400ms - kJitter);
ASSERT_TRUE(taskRan[0]);
@@ -132,6 +154,9 @@
ASSERT_FALSE(taskRan[4]);
ASSERT_TRUE(taskRan[5]);
+ // 4 tasks ran and 1 cancelled
+ ASSERT_EQ(4 + 1, countChars(thread.retiredToString(), REQUEST_START));
+
std::this_thread::sleep_until(startTime + 400ms + kJitter);
ASSERT_TRUE(taskRan[0]);
ASSERT_TRUE(taskRan[1]);
@@ -139,8 +164,62 @@
ASSERT_TRUE(taskRan[3]);
ASSERT_FALSE(taskRan[4]);
ASSERT_TRUE(taskRan[5]);
+
+ // 0 tasks pending
+ ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
+ // 5 tasks ran and 1 cancelled
+ ASSERT_EQ(5 + 1, countChars(thread.retiredToString(), REQUEST_START));
}
+TEST(TimerThread, TrackedTasks) {
+ TimerThread thread;
+
+ auto handle0 = thread.trackTask("0");
+ auto handle1 = thread.trackTask("1");
+ auto handle2 = thread.trackTask("2");
+
+ // 3 tasks pending
+ ASSERT_EQ(3, countChars(thread.pendingToString(), REQUEST_START));
+ // 0 tasks retired
+ ASSERT_EQ(0, countChars(thread.retiredToString(), REQUEST_START));
+
+ ASSERT_TRUE(thread.cancelTask(handle0));
+ ASSERT_TRUE(thread.cancelTask(handle1));
+
+ // 1 task pending
+ ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ // 2 tasks retired
+ ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+
+ // handle1 is stale, cancel returns false.
+ ASSERT_FALSE(thread.cancelTask(handle1));
+
+ // 1 task pending
+ ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ // 2 tasks retired
+ ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+
+ // Add another tracked task.
+ auto handle3 = thread.trackTask("3");
+
+ // 2 tasks pending
+ ASSERT_EQ(2, countChars(thread.pendingToString(), REQUEST_START));
+ // 2 tasks retired
+ ASSERT_EQ(2, countChars(thread.retiredToString(), REQUEST_START));
+
+ ASSERT_TRUE(thread.cancelTask(handle2));
+
+ // 1 tasks pending
+ ASSERT_EQ(1, countChars(thread.pendingToString(), REQUEST_START));
+ // 3 tasks retired
+ ASSERT_EQ(3, countChars(thread.retiredToString(), REQUEST_START));
+
+ ASSERT_TRUE(thread.cancelTask(handle3));
+
+ // 0 tasks pending
+ ASSERT_EQ(0, countChars(thread.pendingToString(), REQUEST_START));
+ // 4 tasks retired
+ ASSERT_EQ(4, countChars(thread.retiredToString(), REQUEST_START));
+}
} // namespace
-} // namespace android
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
index dfc84e1..6de6b13 100644
--- a/media/utils/TimerThread.cpp
+++ b/media/utils/TimerThread.cpp
@@ -17,18 +17,278 @@
#define LOG_TAG "TimerThread"
#include <optional>
+#include <sstream>
+#include <unistd.h>
+#include <vector>
+#include <mediautils/MediaUtilsDelayed.h>
#include <mediautils/TimerThread.h>
#include <utils/ThreadDefs.h>
-namespace android {
+namespace android::mediautils {
-TimerThread::TimerThread() : mThread([this] { threadFunc(); }) {
- pthread_setname_np(mThread.native_handle(), "TimeCheckThread");
- pthread_setschedprio(mThread.native_handle(), PRIORITY_URGENT_AUDIO);
+extern std::string formatTime(std::chrono::system_clock::time_point t);
+extern std::string_view timeSuffix(std::string_view time1, std::string_view time2);
+
+TimerThread::Handle TimerThread::scheduleTask(
+ std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout) {
+ const auto now = std::chrono::system_clock::now();
+ std::shared_ptr<const Request> request{
+ new Request{ now, now + timeout, gettid(), std::move(tag) }};
+ return mMonitorThread.add(std::move(request), std::move(func), timeout);
}
-TimerThread::~TimerThread() {
+TimerThread::Handle TimerThread::trackTask(std::string tag) {
+ const auto now = std::chrono::system_clock::now();
+ std::shared_ptr<const Request> request{
+ new Request{ now, now, gettid(), std::move(tag) }};
+ return mNoTimeoutMap.add(std::move(request));
+}
+
+bool TimerThread::cancelTask(Handle handle) {
+ std::shared_ptr<const Request> request = mNoTimeoutMap.isValidHandle(handle) ?
+ mNoTimeoutMap.remove(handle) : mMonitorThread.remove(handle);
+ if (!request) return false;
+ mRetiredQueue.add(std::move(request));
+ return true;
+}
+
+std::string TimerThread::toString(size_t retiredCount) const {
+ // Note: These request queues are snapshot very close together but
+ // not at "identical" times as we don't use a class-wide lock.
+
+ std::vector<std::shared_ptr<const Request>> timeoutRequests;
+ std::vector<std::shared_ptr<const Request>> retiredRequests;
+ mTimeoutQueue.copyRequests(timeoutRequests);
+ mRetiredQueue.copyRequests(retiredRequests, retiredCount);
+ std::vector<std::shared_ptr<const Request>> pendingRequests =
+ getPendingRequests();
+
+ struct Analysis analysis = analyzeTimeout(timeoutRequests, pendingRequests);
+ std::string analysisSummary;
+ if (!analysis.summary.empty()) {
+ analysisSummary = std::string("\nanalysis [ ").append(analysis.summary).append(" ]");
+ }
+ std::string timeoutStack;
+ if (analysis.timeoutTid != -1) {
+ timeoutStack = std::string("\ntimeout(")
+ .append(std::to_string(analysis.timeoutTid)).append(") callstack [\n")
+ .append(getCallStackStringForTid(analysis.timeoutTid)).append("]");
+ }
+ std::string blockedStack;
+ if (analysis.HALBlockedTid != -1) {
+ blockedStack = std::string("\nblocked(")
+ .append(std::to_string(analysis.HALBlockedTid)).append(") callstack [\n")
+ .append(getCallStackStringForTid(analysis.HALBlockedTid)).append("]");
+ }
+
+ return std::string("now ")
+ .append(formatTime(std::chrono::system_clock::now()))
+ .append(analysisSummary)
+ .append("\ntimeout [ ")
+ .append(requestsToString(timeoutRequests))
+ .append(" ]\npending [ ")
+ .append(requestsToString(pendingRequests))
+ .append(" ]\nretired [ ")
+ .append(requestsToString(retiredRequests))
+ .append(" ]")
+ .append(timeoutStack)
+ .append(blockedStack);
+}
+
+// A HAL method is where the substring "Hidl" is in the class name.
+// The tag should look like: ... Hidl ... :: ...
+// When the audio HAL is updated to AIDL perhaps we will use instead
+// a global directory of HAL classes.
+//
+// See MethodStatistics.cpp:
+// mediautils::getStatisticsClassesForModule(METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL)
+//
+/* static */
+bool TimerThread::isRequestFromHal(const std::shared_ptr<const Request>& request) {
+ const size_t hidlPos = request->tag.find("Hidl");
+ if (hidlPos == std::string::npos) return false;
+ // should be a separator afterwards Hidl which indicates the string was in the class.
+ const size_t separatorPos = request->tag.find("::", hidlPos);
+ return separatorPos != std::string::npos;
+}
+
+/* static */
+struct TimerThread::Analysis TimerThread::analyzeTimeout(
+ const std::vector<std::shared_ptr<const Request>>& timeoutRequests,
+ const std::vector<std::shared_ptr<const Request>>& pendingRequests) {
+
+ if (timeoutRequests.empty() || pendingRequests.empty()) return {}; // nothing to say.
+
+ // for now look at last timeout (in our case, the only timeout)
+ const std::shared_ptr<const Request> timeout = timeoutRequests.back();
+
+ // pending Requests that are problematic.
+ std::vector<std::shared_ptr<const Request>> pendingExact;
+ std::vector<std::shared_ptr<const Request>> pendingPossible;
+
+ // We look at pending requests that were scheduled no later than kDuration
+ // after the timeout request. This prevents false matches with calls
+ // that naturally block for a short period of time
+ // such as HAL write() and read().
+ //
+ auto constexpr kDuration = std::chrono::milliseconds(1000);
+ for (const auto& pending : pendingRequests) {
+ // If the pending tid is the same as timeout tid, problem identified.
+ if (pending->tid == timeout->tid) {
+ pendingExact.emplace_back(pending);
+ continue;
+ }
+
+ // if the pending tid is scheduled within time limit
+ if (pending->scheduled - timeout->scheduled < kDuration) {
+ pendingPossible.emplace_back(pending);
+ }
+ }
+
+ struct Analysis analysis{};
+
+ analysis.timeoutTid = timeout->tid;
+ std::string& summary = analysis.summary;
+ if (!pendingExact.empty()) {
+ const auto& request = pendingExact.front();
+ const bool hal = isRequestFromHal(request);
+
+ if (hal) {
+ summary = std::string("Blocked directly due to HAL call: ")
+ .append(request->toString());
+ }
+ }
+ if (summary.empty() && !pendingPossible.empty()) {
+ for (const auto& request : pendingPossible) {
+ const bool hal = isRequestFromHal(request);
+ if (hal) {
+ // The first blocked call is the most likely one.
+ // Recent calls might be temporarily blocked
+ // calls such as write() or read() depending on kDuration.
+ summary = std::string("Blocked possibly due to HAL call: ")
+ .append(request->toString());
+ analysis.HALBlockedTid = request->tid;
+ }
+ }
+ }
+ return analysis;
+}
+
+std::vector<std::shared_ptr<const TimerThread::Request>> TimerThread::getPendingRequests() const {
+ constexpr size_t kEstimatedPendingRequests = 8; // approx 128 byte alloc.
+ std::vector<std::shared_ptr<const Request>> pendingRequests;
+ pendingRequests.reserve(kEstimatedPendingRequests); // preallocate vector out of lock.
+
+ // following are internally locked calls, which add to our local pendingRequests.
+ mMonitorThread.copyRequests(pendingRequests);
+ mNoTimeoutMap.copyRequests(pendingRequests);
+
+ // Sort in order of scheduled time.
+ std::sort(pendingRequests.begin(), pendingRequests.end(),
+ [](const std::shared_ptr<const Request>& r1,
+ const std::shared_ptr<const Request>& r2) {
+ return r1->scheduled < r2->scheduled;
+ });
+ return pendingRequests;
+}
+
+std::string TimerThread::pendingToString() const {
+ return requestsToString(getPendingRequests());
+}
+
+std::string TimerThread::retiredToString(size_t n) const {
+ std::vector<std::shared_ptr<const Request>> retiredRequests;
+ mRetiredQueue.copyRequests(retiredRequests, n);
+
+ // Dump to string
+ return requestsToString(retiredRequests);
+}
+
+std::string TimerThread::timeoutToString(size_t n) const {
+ std::vector<std::shared_ptr<const Request>> timeoutRequests;
+ mTimeoutQueue.copyRequests(timeoutRequests, n);
+
+ // Dump to string
+ return requestsToString(timeoutRequests);
+}
+
+std::string TimerThread::Request::toString() const {
+ const auto scheduledString = formatTime(scheduled);
+ const auto deadlineString = formatTime(deadline);
+ return std::string(tag)
+ .append(" scheduled ").append(scheduledString)
+ .append(" deadline ").append(timeSuffix(scheduledString, deadlineString))
+ .append(" tid ").append(std::to_string(tid));
+}
+
+void TimerThread::RequestQueue::add(std::shared_ptr<const Request> request) {
+ std::lock_guard lg(mRQMutex);
+ mRequestQueue.emplace_back(std::chrono::system_clock::now(), std::move(request));
+ if (mRequestQueue.size() > mRequestQueueMax) {
+ mRequestQueue.pop_front();
+ }
+}
+
+void TimerThread::RequestQueue::copyRequests(
+ std::vector<std::shared_ptr<const Request>>& requests, size_t n) const {
+ std::lock_guard lg(mRQMutex);
+ const size_t size = mRequestQueue.size();
+ size_t i = n >= size ? 0 : size - n;
+ for (; i < size; ++i) {
+ const auto &[time, request] = mRequestQueue[i];
+ requests.emplace_back(request);
+ }
+}
+
+bool TimerThread::NoTimeoutMap::isValidHandle(Handle handle) const {
+ return handle > getIndexedHandle(mNoTimeoutRequests);
+}
+
+TimerThread::Handle TimerThread::NoTimeoutMap::add(std::shared_ptr<const Request> request) {
+ std::lock_guard lg(mNTMutex);
+ // A unique handle is obtained by mNoTimeoutRequests.fetch_add(1),
+ // This need not be under a lock, but we do so anyhow.
+ const Handle handle = getIndexedHandle(mNoTimeoutRequests++);
+ mMap[handle] = request;
+ return handle;
+}
+
+std::shared_ptr<const TimerThread::Request> TimerThread::NoTimeoutMap::remove(Handle handle) {
+ std::lock_guard lg(mNTMutex);
+ auto it = mMap.find(handle);
+ if (it == mMap.end()) return {};
+ auto request = it->second;
+ mMap.erase(it);
+ return request;
+}
+
+void TimerThread::NoTimeoutMap::copyRequests(
+ std::vector<std::shared_ptr<const Request>>& requests) const {
+ std::lock_guard lg(mNTMutex);
+ for (const auto &[handle, request] : mMap) {
+ requests.emplace_back(request);
+ }
+}
+
+TimerThread::Handle TimerThread::MonitorThread::getUniqueHandle_l(
+ std::chrono::milliseconds timeout) {
+ // To avoid key collisions, advance by 1 tick until the key is unique.
+ auto deadline = std::chrono::steady_clock::now() + timeout;
+ for (; mMonitorRequests.find(deadline) != mMonitorRequests.end();
+ deadline += std::chrono::steady_clock::duration(1))
+ ;
+ return deadline;
+}
+
+TimerThread::MonitorThread::MonitorThread(RequestQueue& timeoutQueue)
+ : mTimeoutQueue(timeoutQueue)
+ , mThread([this] { threadFunc(); }) {
+ pthread_setname_np(mThread.native_handle(), "TimerThread");
+ pthread_setschedprio(mThread.native_handle(), PRIORITY_URGENT_AUDIO);
+}
+
+TimerThread::MonitorThread::~MonitorThread() {
{
std::lock_guard _l(mMutex);
mShouldExit = true;
@@ -37,37 +297,26 @@
mThread.join();
}
-TimerThread::Handle TimerThread::scheduleTaskAtDeadline(std::function<void()>&& func,
- TimePoint deadline) {
- std::lock_guard _l(mMutex);
-
- // To avoid key collisions, advance by 1 tick until the key is unique.
- for (; mMonitorRequests.find(deadline) != mMonitorRequests.end();
- deadline += TimePoint::duration(1))
- ;
- mMonitorRequests.emplace(deadline, std::move(func));
- mCond.notify_all();
- return deadline;
-}
-
-// Returns true if cancelled, false if handle doesn't exist.
-// Beware of lock inversion with cancelTask() as the callback
-// is called while holding mMutex.
-bool TimerThread::cancelTask(Handle handle) {
- std::lock_guard _l(mMutex);
- return mMonitorRequests.erase(handle) != 0;
-}
-
-void TimerThread::threadFunc() {
+void TimerThread::MonitorThread::threadFunc() {
std::unique_lock _l(mMutex);
-
while (!mShouldExit) {
if (!mMonitorRequests.empty()) {
- TimePoint nextDeadline = mMonitorRequests.begin()->first;
+ Handle nextDeadline = mMonitorRequests.begin()->first;
if (nextDeadline < std::chrono::steady_clock::now()) {
- // Deadline expired.
- mMonitorRequests.begin()->second();
- mMonitorRequests.erase(mMonitorRequests.begin());
+ // Deadline has expired, handle the request.
+ {
+ auto node = mMonitorRequests.extract(mMonitorRequests.begin());
+ _l.unlock();
+ // We add Request to retired queue early so that it can be dumped out.
+ mTimeoutQueue.add(std::move(node.mapped().first));
+ node.mapped().second(); // Caution: we don't hold lock here - but do we care?
+ // this is the timeout case! We will crash soon,
+ // maybe before returning.
+ // anything left over is released here outside lock.
+ }
+ // reacquire the lock - if something was added, we loop immediately to check.
+ _l.lock();
+ continue;
}
mCond.wait_until(_l, nextDeadline);
} else {
@@ -76,4 +325,35 @@
}
}
-} // namespace android
+TimerThread::Handle TimerThread::MonitorThread::add(
+ std::shared_ptr<const Request> request, std::function<void()>&& func,
+ std::chrono::milliseconds timeout) {
+ std::lock_guard _l(mMutex);
+ const Handle handle = getUniqueHandle_l(timeout);
+ mMonitorRequests.emplace(handle, std::make_pair(std::move(request), std::move(func)));
+ mCond.notify_all();
+ return handle;
+}
+
+std::shared_ptr<const TimerThread::Request> TimerThread::MonitorThread::remove(Handle handle) {
+ std::unique_lock ul(mMutex);
+ const auto it = mMonitorRequests.find(handle);
+ if (it == mMonitorRequests.end()) {
+ return {};
+ }
+ std::shared_ptr<const TimerThread::Request> request = std::move(it->second.first);
+ std::function<void()> func = std::move(it->second.second);
+ mMonitorRequests.erase(it);
+ ul.unlock(); // manually release lock here so func is released outside of lock.
+ return request;
+}
+
+void TimerThread::MonitorThread::copyRequests(
+ std::vector<std::shared_ptr<const Request>>& requests) const {
+ std::lock_guard lg(mMutex);
+ for (const auto &[deadline, monitorpair] : mMonitorRequests) {
+ requests.emplace_back(monitorpair.first);
+ }
+}
+
+} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/Library.h b/media/utils/include/mediautils/Library.h
new file mode 100644
index 0000000..19cfc11
--- /dev/null
+++ b/media/utils/include/mediautils/Library.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <dlfcn.h>
+#include <string>
+#include <unistd.h>
+
+namespace android::mediautils {
+
+/**
+ * Returns a shared pointer to the library instance.
+ *
+ * When the last reference to the library is removed, the library will be dlclose().
+ *
+ * Notes:
+ * 1) The Android bionic linker always uses RTLD_GLOBAL for executable linking
+ * which provides the symbols for other subsequent libraries.
+ *
+ * 2) RTLD_GLOBAL like RTLD_NODELETE disables unloading of the library
+ * when the reference count drops to zero.
+ *
+ * 3) RTLD_LOCAL is the default in the absence of RTLD_GLOBAL.
+ * RTLD_LOCAL may be ignored in some situations, for example:
+ * https://stackoverflow.com/questions/56808889/static-objects-destructed-before-dlclose
+ *
+ * 4) We default to use RTLD_LAZY to delay symbol relocations until needed.
+ * This flag may be ignored by Android. RTLD_LAZY may allow
+ * unresolved symbols if not accessed, or symbols added later with another library
+ * loaded with RTLD_GLOBAL. See RTLD_NOW for comparison.
+ *
+ * 5) Avoid both staticly loading and dynamically loading the same library.
+ * This is known to cause double free issues as library symbols may map to
+ * the same location. RTLD_DEEPBIND does not appear supported as of T.
+ * https://stackoverflow.com/questions/34073051/when-we-are-supposed-to-use-rtld-deepbind
+ * https://stackoverflow.com/questions/31209693/static-library-linked-two-times
+ *
+ * Details on Android linker and debugging here:
+ * See: adb shell setprop debug.ld.all dlerror,dlopen,dlsym
+ * See: https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md
+ *
+ * Some other relevant info:
+ * See: Soong double_loadable:true go/double_loadable
+ * See: https://en.wikipedia.org/wiki/One_Definition_Rule#Summary
+ *
+ * TODO(b/228093151): Consider moving to platform/system.
+ *
+ * \param libraryName
+ * \param flags one of the dlopen RTLD_* flags. https://linux.die.net/man/3/dlopen
+ * \return shared_ptr to the library. This will be nullptr if it isn't found.
+ */
+std::shared_ptr<void> loadLibrary(const char *libraryName, int flags = RTLD_LAZY);
+
+/**
+ * Returns a shared pointer to an object in the library
+ *
+ * The object will be a global variable or method in the library.
+ * The object reference counting is aliased to the library shared ptr.
+ *
+ * Note: If any internals of the shared library are exposed, for example by
+ * a method returning a pointer to library globals,
+ * or returning an object whose class definition is from the library,
+ * then the shared_ptr must be kept alive while such references to
+ * library internals exist to prevent library unloading.
+ *
+ * See usage of RTLD_NODELETE as a flag to prevent unloading.
+ *
+ * \param objectName of the library object.
+ * \param library a shared pointer to the library returned by loadLibrary().
+ * \return shared_ptr to the object, but whose refcount is
+ * aliased to the library shared ptr.
+ */
+std::shared_ptr<void> getUntypedObjectFromLibrary(
+ const char *objectName, const std::shared_ptr<void>& library);
+
+/**
+ * Returns a shared pointer to an object in the library
+ *
+ * This is the template typed version of getUntypedObjectFromLibrary().
+ *
+ * \param objectName of the library object.
+ * \param library a shared pointer to the library
+ * \return shared_ptr to the object, but whose refcount is
+ * aliased to the library shared ptr.
+ */
+template <typename T>
+std::shared_ptr<T> getObjectFromLibrary(
+ const char *objectName, const std::shared_ptr<void>& library) {
+ return std::static_pointer_cast<T>(getUntypedObjectFromLibrary(objectName, library));
+}
+
+} // android::mediautils
diff --git a/media/utils/include/mediautils/MediaUtilsDelayed.h b/media/utils/include/mediautils/MediaUtilsDelayed.h
new file mode 100644
index 0000000..1583e60
--- /dev/null
+++ b/media/utils/include/mediautils/MediaUtilsDelayed.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <unistd.h>
+
+namespace android::mediautils {
+
+// These methods use lazy library loading.
+
+/**
+ * Returns a string callstack from the thread id tid.
+ */
+std::string getCallStackStringForTid(pid_t tid);
+
+} // android::mediautils
diff --git a/media/utils/include/mediautils/MethodStatistics.h b/media/utils/include/mediautils/MethodStatistics.h
index 7d8061d..700fbaa 100644
--- a/media/utils/include/mediautils/MethodStatistics.h
+++ b/media/utils/include/mediautils/MethodStatistics.h
@@ -19,6 +19,7 @@
#include <map>
#include <mutex>
#include <string>
+#include <vector>
#include <android-base/thread_annotations.h>
#include <audio_utils/Statistics.h>
@@ -91,9 +92,16 @@
std::string dump() const {
std::stringstream ss;
std::lock_guard lg(mLock);
- for (const auto &[code, stats] : mStatisticsMap) {
- ss << int(code) << " " << getMethodForCode(code) <<
- " n=" << stats.getN() << " " << stats.toString() << "\n";
+ if constexpr (std::is_same_v<Code, std::string>) {
+ for (const auto &[code, stats] : mStatisticsMap) {
+ ss << code <<
+ " n=" << stats.getN() << " " << stats.toString() << "\n";
+ }
+ } else /* constexpr */ {
+ for (const auto &[code, stats] : mStatisticsMap) {
+ ss << int(code) << " " << getMethodForCode(code) <<
+ " n=" << stats.getN() << " " << stats.toString() << "\n";
+ }
}
return ss.str();
}
@@ -104,6 +112,18 @@
std::map<Code, StatsType> mStatisticsMap GUARDED_BY(mLock);
};
+// Managed Statistics support.
+// Supported Modules
+#define METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL "AudioHidl"
+
+// Returns a vector of class names for the module, or a nullptr if module not found.
+std::shared_ptr<std::vector<std::string>>
+getStatisticsClassesForModule(std::string_view moduleName);
+
+// Returns a statistics object for that class, or a nullptr if class not found.
+std::shared_ptr<MethodStatistics<std::string>>
+getStatisticsForClass(std::string_view className);
+
// Only if used, requires IBinder.h to be included at the location of invocation.
#define METHOD_STATISTICS_BINDER_CODE_NAMES(CODE_TYPE) \
{(CODE_TYPE)IBinder::PING_TRANSACTION , "ping"}, \
diff --git a/media/utils/include/mediautils/Process.h b/media/utils/include/mediautils/Process.h
new file mode 100644
index 0000000..d249c3a
--- /dev/null
+++ b/media/utils/include/mediautils/Process.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+#include <unistd.h>
+
+/*
+ * This header contains utilities to read the linux system /proc/pid files
+ *
+ * The format of this is not guaranteed to be stable, so use for diagnostic purposes only.
+ *
+ * The linux "proc" directory documentation:
+ * https://kernel.org/doc/Documentation/filesystems/proc.txt
+ * https://www.kernel.org/doc/html/latest/filesystems/proc.html?highlight=proc%20pid#chapter-3-per-process-parameters
+ */
+
+namespace android::mediautils {
+
+/**
+ * Return the thread schedule information for tid.
+ *
+ * String will be empty if the process does not have permission to
+ * access the /proc/pid tables, or if not on a Linux device.
+ *
+ * Linux scheduler documentation:
+ * https://www.kernel.org/doc/html/latest/scheduler/index.html
+ * https://man7.org/linux/man-pages/man7/sched.7.html
+ *
+ * Sample as follows:
+
+AudioOut_8D (10800, #threads: 36)
+-------------------------------------------------------------------
+se.exec_start : 8132077.598026
+se.vruntime : 798689.872087
+se.sum_exec_runtime : 136466.957838
+se.nr_migrations : 132487
+se.statistics.sum_sleep_runtime : 5629794.565945
+se.statistics.wait_start : 0.000000
+se.statistics.sleep_start : 8195727.586392
+se.statistics.block_start : 0.000000
+se.statistics.sleep_max : 1995665.869808
+se.statistics.block_max : 0.591675
+se.statistics.exec_max : 2.477580
+se.statistics.slice_max : 0.000000
+se.statistics.wait_max : 8.608642
+se.statistics.wait_sum : 4683.266835
+se.statistics.wait_count : 300964
+se.statistics.iowait_sum : 0.000000
+se.statistics.iowait_count : 0
+se.statistics.nr_migrations_cold : 0
+se.statistics.nr_failed_migrations_affine : 297
+se.statistics.nr_failed_migrations_running : 1412
+se.statistics.nr_failed_migrations_hot : 96
+se.statistics.nr_forced_migrations : 26
+se.statistics.nr_wakeups : 281263
+se.statistics.nr_wakeups_sync : 84
+se.statistics.nr_wakeups_migrate : 132322
+se.statistics.nr_wakeups_local : 2165
+se.statistics.nr_wakeups_remote : 279098
+se.statistics.nr_wakeups_affine : 0
+se.statistics.nr_wakeups_affine_attempts : 0
+se.statistics.nr_wakeups_passive : 0
+se.statistics.nr_wakeups_idle : 0
+avg_atom : 0.453434
+avg_per_cpu : 1.030040
+nr_switches : 300963
+nr_voluntary_switches : 281252
+nr_involuntary_switches : 19711
+se.load.weight : 73477120
+se.avg.load_sum : 58
+se.avg.runnable_sum : 27648
+se.avg.util_sum : 21504
+se.avg.load_avg : 48
+se.avg.runnable_avg : 0
+se.avg.util_avg : 0
+se.avg.last_update_time : 8132075824128
+se.avg.util_est.ewma : 8
+se.avg.util_est.enqueued : 1
+uclamp.min : 0
+uclamp.max : 1024
+effective uclamp.min : 0
+effective uclamp.max : 1024
+policy : 0
+prio : 101
+clock-delta : 163
+*/
+std::string getThreadSchedAsString(pid_t tid);
+
+/**
+ * Returns map for the raw thread schedule string.
+ */
+std::map<std::string, double> parseThreadSchedString(const std::string& schedString);
+
+/**
+ * Returns map for /proc/pid/task/tid/sched
+ */
+inline std::map<std::string, double> getThreadSchedAsMap(pid_t tid) {
+ return parseThreadSchedString(getThreadSchedAsString(tid));
+}
+
+// TODO: Extend to other /proc/pid file information.
+//
+// See "ps" command get_ps().
+// https://cs.android.com/android/platform/superproject/+/master:external/toybox/toys/posix/ps.c;l=707
+
+} // android::mediautils
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index 991a921..ef03aef 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -50,27 +50,31 @@
* destroyed or leaves scope before the timer expires.)
* float elapsedMs (the elapsed time to this event).
* The callback when timeout is true will be called on a different thread.
- * Currently this is guaranteed to block the destructor
- * (potential lock inversion warning here) nevertheless
- * it would be safer not to depend on stack contents.
+ * This will cancel the callback on the destructor but is not guaranteed
+ * to block for callback completion if it is already in progress
+ * (for maximum concurrency and reduced deadlock potential), so use proper
+ * lifetime analysis (e.g. shared or weak pointers).
* \param timeoutMs timeout in milliseconds.
+ * A zero timeout means no timeout is set -
+ * the callback is called only when
+ * the TimeCheck object is destroyed or leaves scope.
* \param crashOnTimeout true if the object issues an abort on timeout.
*/
explicit TimeCheck(std::string tag, OnTimerFunc&& onTimer = {},
uint32_t timeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true);
+
+ TimeCheck() = default;
// Remove copy constructors as there should only be one call to the destructor.
// Move is kept implicitly disabled, but would be logically consistent if enabled.
TimeCheck(const TimeCheck& other) = delete;
TimeCheck& operator=(const TimeCheck&) = delete;
~TimeCheck();
+ static std::string toString();
static void setAudioHalPids(const std::vector<pid_t>& pids);
static std::vector<pid_t> getAudioHalPids();
private:
- static TimerThread& getTimeCheckThread();
- static void accessAudioHalPids(std::vector<pid_t>* pids, bool update);
-
// Helper class for handling events.
// The usage here is const safe.
class TimeCheckHandler {
@@ -85,11 +89,19 @@
void onTimeout() const;
};
+ static TimerThread& getTimeCheckThread();
+ static void accessAudioHalPids(std::vector<pid_t>* pids, bool update);
+
// mTimeCheckHandler is immutable, prefer to be first initialized, last destroyed.
// Technically speaking, we do not need a shared_ptr here because TimerThread::cancelTask()
// is mutually exclusive of the callback, but the price paid for lifetime safety is minimal.
const std::shared_ptr<const TimeCheckHandler> mTimeCheckHandler;
- const TimerThread::Handle mTimerHandle;
+ const TimerThread::Handle mTimerHandle = TimerThread::INVALID_HANDLE;
};
+// Returns a TimeCheck object that sends info to MethodStatistics
+// obtained from getStatisticsForClass(className).
+TimeCheck makeTimeCheckStatsForClassMethod(
+ std::string_view className, std::string_view methodName);
+
} // namespace android::mediautils
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
index acf0b16..ffee602 100644
--- a/media/utils/include/mediautils/TimerThread.h
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -16,53 +16,255 @@
#pragma once
+#include <atomic>
#include <condition_variable>
+#include <deque>
#include <functional>
#include <map>
#include <mutex>
+#include <string>
#include <thread>
#include <android-base/thread_annotations.h>
-namespace android {
+namespace android::mediautils {
/**
* A thread for deferred execution of tasks, with cancellation.
*/
class TimerThread {
public:
+ // A Handle is a time_point that serves as a unique key. It is ordered.
using Handle = std::chrono::steady_clock::time_point;
- TimerThread();
- ~TimerThread();
+ static inline constexpr Handle INVALID_HANDLE =
+ std::chrono::steady_clock::time_point::min();
/**
- * Schedule a task to be executed in the future (`timeout` duration from now).
- * Returns a handle that can be used for cancellation.
+ * Schedules a task to be executed in the future (`timeout` duration from now).
+ *
+ * \param tag string associated with the task. This need not be unique,
+ * as the Handle returned is used for cancelling.
+ * \param func callback function that is invoked at the timeout.
+ * \param timeout timeout duration which is converted to milliseconds with at
+ * least 45 integer bits.
+ * A timeout of 0 (or negative) means the timer never expires
+ * so func() is never called. These tasks are stored internally
+ * and reported in the toString() until manually cancelled.
+ * \returns a handle that can be used for cancellation.
*/
- template <typename R, typename P>
- Handle scheduleTask(std::function<void()>&& func, std::chrono::duration<R, P> timeout) {
- auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout);
- return scheduleTaskAtDeadline(std::move(func), deadline);
- }
+ Handle scheduleTask(
+ std::string tag, std::function<void()>&& func, std::chrono::milliseconds timeout);
/**
- * Cancel a task, previously scheduled with scheduleTask().
- * If the task has already executed, this is a no-op and returns false.
+ * Tracks a task that shows up on toString() until cancelled.
+ *
+ * \param tag string associated with the task.
+ * \returns a handle that can be used for cancellation.
+ */
+ Handle trackTask(std::string tag);
+
+ /**
+ * Cancels a task previously scheduled with scheduleTask()
+ * or trackTask().
+ *
+ * \returns true if cancelled. If the task has already executed
+ * or if the handle doesn't exist, this is a no-op
+ * and returns false.
*/
bool cancelTask(Handle handle);
+ std::string toString(size_t retiredCount = SIZE_MAX) const;
+
+ /**
+ * Returns a string representation of the TimerThread queue.
+ *
+ * The queue is dumped in order of scheduling (not deadline).
+ */
+ std::string pendingToString() const;
+
+ /**
+ * Returns a string representation of the last retired tasks.
+ *
+ * These tasks from trackTask() or scheduleTask() are
+ * cancelled.
+ *
+ * These are ordered when the task was retired.
+ *
+ * \param n is maximum number of tasks to dump.
+ */
+ std::string retiredToString(size_t n = SIZE_MAX) const;
+
+
+ /**
+ * Returns a string representation of the last timeout tasks.
+ *
+ * These tasks from scheduleTask() which have timed-out.
+ *
+ * These are ordered when the task had timed-out.
+ *
+ * \param n is maximum number of tasks to dump.
+ */
+ std::string timeoutToString(size_t n = SIZE_MAX) const;
+
+ /**
+ * Dumps a container with SmartPointer<Request> to a string.
+ *
+ * "{ Request1 } { Request2} ...{ RequestN }"
+ */
+ template <typename T>
+ static std::string requestsToString(const T& containerRequests) {
+ std::string s;
+ // append seems to be faster than stringstream.
+ // https://stackoverflow.com/questions/18892281/most-optimized-way-of-concatenation-in-strings
+ for (const auto& request : containerRequests) {
+ s.append("{ ").append(request->toString()).append(" } ");
+ }
+ // If not empty, there's an extra space at the end, so we trim it off.
+ if (!s.empty()) s.pop_back();
+ return s;
+ }
+
private:
- using TimePoint = std::chrono::steady_clock::time_point;
+ // To minimize movement of data, we pass around shared_ptrs to Requests.
+ // These are allocated and deallocated outside of the lock.
+ struct Request {
+ const std::chrono::system_clock::time_point scheduled;
+ const std::chrono::system_clock::time_point deadline; // deadline := scheduled + timeout
+ // if deadline == scheduled, no
+ // timeout, task not executed.
+ const pid_t tid;
+ const std::string tag;
- std::condition_variable mCond;
- std::mutex mMutex;
- std::thread mThread;
- std::map<TimePoint, std::function<void()>> mMonitorRequests GUARDED_BY(mMutex);
- bool mShouldExit GUARDED_BY(mMutex) = false;
+ std::string toString() const;
+ };
- void threadFunc();
- Handle scheduleTaskAtDeadline(std::function<void()>&& func, TimePoint deadline);
+ // Deque of requests, in order of add().
+ // This class is thread-safe.
+ class RequestQueue {
+ public:
+ explicit RequestQueue(size_t maxSize)
+ : mRequestQueueMax(maxSize) {}
+
+ void add(std::shared_ptr<const Request>);
+
+ // return up to the last "n" requests retired.
+ void copyRequests(std::vector<std::shared_ptr<const Request>>& requests,
+ size_t n = SIZE_MAX) const;
+
+ private:
+ const size_t mRequestQueueMax;
+ mutable std::mutex mRQMutex;
+ std::deque<std::pair<std::chrono::system_clock::time_point,
+ std::shared_ptr<const Request>>>
+ mRequestQueue GUARDED_BY(mRQMutex);
+ };
+
+ // A storage map of tasks without timeouts. There is no std::function<void()>
+ // required, it just tracks the tasks with the tag, scheduled time and the tid.
+ // These tasks show up on a pendingToString() until manually cancelled.
+ class NoTimeoutMap {
+ // This a counter of the requests that have no timeout (timeout == 0).
+ std::atomic<size_t> mNoTimeoutRequests{};
+
+ mutable std::mutex mNTMutex;
+ std::map<Handle, std::shared_ptr<const Request>> mMap GUARDED_BY(mNTMutex);
+
+ public:
+ bool isValidHandle(Handle handle) const; // lock free
+ Handle add(std::shared_ptr<const Request> request);
+ std::shared_ptr<const Request> remove(Handle handle);
+ void copyRequests(std::vector<std::shared_ptr<const Request>>& requests) const;
+ };
+
+ // Monitor thread.
+ // This thread manages shared pointers to Requests and a function to
+ // call on timeout.
+ // This class is thread-safe.
+ class MonitorThread {
+ mutable std::mutex mMutex;
+ mutable std::condition_variable mCond;
+
+ // Ordered map of requests based on time of deadline.
+ //
+ std::map<Handle, std::pair<std::shared_ptr<const Request>, std::function<void()>>>
+ mMonitorRequests GUARDED_BY(mMutex);
+
+ RequestQueue& mTimeoutQueue; // locked internally, added to when request times out.
+
+ // Worker thread variables
+ bool mShouldExit GUARDED_BY(mMutex) = false;
+
+ // To avoid race with initialization,
+ // mThread should be initialized last as the thread is launched immediately.
+ std::thread mThread;
+
+ void threadFunc();
+ Handle getUniqueHandle_l(std::chrono::milliseconds timeout) REQUIRES(mMutex);
+
+ public:
+ MonitorThread(RequestQueue &timeoutQueue);
+ ~MonitorThread();
+
+ Handle add(std::shared_ptr<const Request> request, std::function<void()>&& func,
+ std::chrono::milliseconds timeout);
+ std::shared_ptr<const Request> remove(Handle handle);
+ void copyRequests(std::vector<std::shared_ptr<const Request>>& requests) const;
+ };
+
+ // Analysis contains info deduced by analysisTimeout().
+ //
+ // Summary is the result string from checking timeoutRequests to see if
+ // any might be caused by blocked calls in pendingRequests.
+ //
+ // Summary string is empty if there is no automatic actionable info.
+ //
+ // timeoutTid is the tid selected from timeoutRequests (if any).
+ //
+ // HALBlockedTid is the tid that is blocked from pendingRequests believed
+ // to cause the timeout.
+ // HALBlockedTid may be INVALID_PID if no suspected tid is found,
+ // and if HALBlockedTid is valid, it will not be the same as timeoutTid.
+ //
+ static constexpr pid_t INVALID_PID = -1;
+ struct Analysis {
+ std::string summary;
+ pid_t timeoutTid = INVALID_PID;
+ pid_t HALBlockedTid = INVALID_PID;
+ };
+
+ // A HAL method is where the substring "Hidl" is in the class name.
+ // The tag should look like: ... Hidl ... :: ...
+ static bool isRequestFromHal(const std::shared_ptr<const Request>& request);
+
+ // Returns analysis from the requests.
+ static Analysis analyzeTimeout(
+ const std::vector<std::shared_ptr<const Request>>& timeoutRequests,
+ const std::vector<std::shared_ptr<const Request>>& pendingRequests);
+
+ std::vector<std::shared_ptr<const Request>> getPendingRequests() const;
+
+ // A no-timeout request is represented by a handles at the end of steady_clock time,
+ // counting down by the number of no timeout requests previously requested.
+ // We manage them on the NoTimeoutMap, but conceptually they could be scheduled
+ // on the MonitorThread because those time handles won't expire in
+ // the lifetime of the device.
+ static inline Handle getIndexedHandle(size_t index) {
+ return std::chrono::time_point<std::chrono::steady_clock>::max() -
+ std::chrono::time_point<std::chrono::steady_clock>::duration(index);
+ }
+
+ static constexpr size_t kRetiredQueueMax = 16;
+ RequestQueue mRetiredQueue{kRetiredQueueMax}; // locked internally
+
+ static constexpr size_t kTimeoutQueueMax = 16;
+ RequestQueue mTimeoutQueue{kTimeoutQueueMax}; // locked internally
+
+ NoTimeoutMap mNoTimeoutMap; // locked internally
+
+ MonitorThread mMonitorThread{mTimeoutQueue}; // This should be initialized last because
+ // the thread is launched immediately.
+ // Locked internally.
};
-} // namespace android
+} // namespace android::mediautils
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 5498ac5..d9c2b21 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -7,6 +7,82 @@
default_applicable_licenses: ["frameworks_av_license"],
}
+cc_test_library {
+ name: "libsharedtest",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ sanitize:{
+ address: true,
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
+ },
+
+ shared_libs: [
+ "liblog",
+ ],
+
+ srcs: [
+ "sharedtest.cpp",
+ ]
+}
+
+cc_test {
+ name: "library_tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ sanitize:{
+ address: true,
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
+ },
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ data_libs: [
+ "libsharedtest",
+ ],
+
+ srcs: [
+ "library_tests.cpp",
+ ],
+}
+
+cc_test {
+ name: "media_process_tests",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libmediautils",
+ "libutils",
+ ],
+
+ srcs: [
+ "media_process_tests.cpp",
+ ],
+}
+
cc_test {
name: "media_synchronization_tests",
diff --git a/media/utils/tests/library_tests.cpp b/media/utils/tests/library_tests.cpp
new file mode 100644
index 0000000..c5c500c
--- /dev/null
+++ b/media/utils/tests/library_tests.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "library_tests"
+#include <utils/Log.h>
+
+#include <mediautils/Library.h>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+using namespace android::mediautils;
+
+namespace {
+
+static int32_t here = 0; // accessed on same thread.
+
+TEST(library_tests, basic) {
+ std::string path = android::base::GetExecutableDirectory() + "/libsharedtest.so";
+ // The flags to loadLibrary should not include RTLD_GLOBAL or RTLD_NODELETE
+ // which prevent unloading.
+ std::shared_ptr<void> library = loadLibrary(path.c_str(), RTLD_LAZY);
+ ASSERT_TRUE(library);
+ ASSERT_EQ(1, library.use_count());
+
+ std::shared_ptr<int32_t*> ptr = getObjectFromLibrary<int32_t*>("gPtr", library);
+ ASSERT_TRUE(ptr);
+ ASSERT_EQ(2, library.use_count());
+
+ ASSERT_EQ(nullptr, *ptr); // original contents are nullptr.
+
+ // There is a static object destructor in libsharedtest.so that will set the
+ // contents of the integer pointer (if non-null) to 1 when called.
+ // This is used to detect that the library is unloaded.
+ *ptr = &here;
+
+ ptr.reset(); // Note: this shared pointer uses library's refcount.
+ ASSERT_EQ(1, library.use_count()); // Verify library's refcount goes down by 1.
+ ASSERT_EQ(0, here); // the shared library's object destructor hasn't been called.
+
+ // use weak_ptr to investigate whether the library is gone.
+ std::weak_ptr<void> wlibrary = library;
+ ASSERT_EQ(1, wlibrary.use_count());
+ library.reset();
+
+ // we should have released the last reference.
+ ASSERT_EQ(0, wlibrary.use_count());
+
+ // The library should unload and the global object destroyed.
+ // Note on Android, specifying RTLD_GLOBAL or RTLD_NODELETE in the flags
+ // will prevent unloading libraries.
+ ASSERT_EQ(1, here);
+}
+
+TEST(library_tests, sad_library) {
+ std::string path = android::base::GetExecutableDirectory()
+ + "/something_random_library_that_doesn't_exit.so";
+
+ std::shared_ptr<void> library = loadLibrary(path.c_str(), RTLD_LAZY);
+ // We shouldn't crash on an invalid library path, just return an empty shared pointer.
+ // Check the logcat for any error details.
+ ASSERT_FALSE(library);
+}
+
+} // namespace
diff --git a/media/utils/tests/media_process_tests.cpp b/media/utils/tests/media_process_tests.cpp
new file mode 100644
index 0000000..2ae3f70
--- /dev/null
+++ b/media/utils/tests/media_process_tests.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mediautils/Process.h>
+
+#define LOG_TAG "media_process_tests"
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+using namespace android;
+using namespace android::mediautils;
+
+TEST(media_process_tests, basic) {
+ const std::string schedString = getThreadSchedAsString(gettid());
+
+ (void)schedString;
+ // We don't test schedString, only that we haven't crashed.
+ // ASSERT_FALSE(schedString.empty());
+
+ // schedString is not normative. So we conjure up our own string
+ const std::string fakeString = "\
+AudioOut_8D (10800, #threads: 36)\n\
+-------------------------------------------------------------------\n\
+se.exec_start : 8132077.598026\n\
+se.vruntime : 798689.872087\n\
+se.sum_exec_runtime : 136466.957838\n\
+se.nr_migrations : 132487\n\
+se.statistics.sum_sleep_runtime : 5629794.565945\n\
+se.statistics.wait_start : 0.000000\n\
+se.statistics.sleep_start : 8195727.586392\n\
+se.statistics.block_start : 0.000000\n\
+se.statistics.sleep_max : 1995665.869808\n\
+se.statistics.block_max : 0.591675\n\
+se.statistics.exec_max : 2.477580\n\
+se.statistics.slice_max : 0.000000\n\
+se.statistics.wait_max : 8.608642\n\
+se.statistics.wait_sum : 4683.266835\n\
+se.statistics.wait_count : 300964\n\
+se.statistics.iowait_sum : 0.000000\n\
+se.statistics.iowait_count : 0\n\
+se.statistics.nr_migrations_cold : 0\n\
+se.statistics.nr_failed_migrations_affine : 297\n\
+se.statistics.nr_failed_migrations_running : 1412\n\
+se.statistics.nr_failed_migrations_hot : 96\n\
+se.statistics.nr_forced_migrations : 26\n\
+se.statistics.nr_wakeups : 281263\n\
+se.statistics.nr_wakeups_sync : 84\n\
+se.statistics.nr_wakeups_migrate : 132322\n\
+se.statistics.nr_wakeups_local : 2165\n\
+se.statistics.nr_wakeups_remote : 279098\n\
+se.statistics.nr_wakeups_affine : 0\n\
+se.statistics.nr_wakeups_affine_attempts : 0\n\
+se.statistics.nr_wakeups_passive : 0\n\
+se.statistics.nr_wakeups_idle : 0\n\
+avg_atom : 0.453434\n\
+avg_per_cpu : 1.030040\n\
+nr_switches : 300963\n\
+nr_voluntary_switches : 281252\n\
+nr_involuntary_switches : 19711\n\
+se.load.weight : 73477120\n\
+se.avg.load_sum : 58\n\
+se.avg.runnable_sum : 27648\n\
+se.avg.util_sum : 21504\n\
+se.avg.load_avg : 48\n\
+se.avg.runnable_avg : 0\n\
+se.avg.util_avg : 0\n\
+se.avg.last_update_time : 8132075824128\n\
+se.avg.util_est.ewma : 8\n\
+se.avg.util_est.enqueued : 1\n\
+uclamp.min : 0\n\
+uclamp.max : 1024\n\
+effective uclamp.min : 0\n\
+effective uclamp.max : 1024\n\
+policy : 0\n\
+prio : 101\n\
+clock-delta : 163";
+
+ std::map<std::string, double> m = parseThreadSchedString(fakeString);
+
+ auto it = m.find("clock-delta");
+ ASSERT_NE(it, m.end());
+ ASSERT_EQ(it->second, 163);
+
+ it = m.find("se.avg.load_avg");
+ ASSERT_NE(it, m.end());
+ ASSERT_EQ(it->second, 48);
+}
diff --git a/media/utils/tests/sharedtest.cpp b/media/utils/tests/sharedtest.cpp
new file mode 100644
index 0000000..3888874
--- /dev/null
+++ b/media/utils/tests/sharedtest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdint>
+#define LOG_TAG "sharedtest"
+#include <utils/Log.h>
+
+// Test library which is dynamicly loaded by library_tests.
+
+// Static variable construction.
+// Calls A constructor on library load, A destructor on library unload.
+
+int32_t *gPtr = nullptr; // this pointer is filled with the location to set memory
+ // when ~A() is called.
+ // we cannot use anything internal to this file as the
+ // data segment may no longer exist after unloading the library.
+struct A {
+ A() {
+ ALOGD("%s: gPtr:%p", __func__, gPtr);
+ }
+
+ ~A() {
+ ALOGD("%s: gPtr:%p", __func__, gPtr);
+ if (gPtr != nullptr) {
+ *gPtr = 1;
+ }
+ }
+} gA;
+
+// __attribute__((constructor)) methods occur before any static variable construction.
+// Libraries that use __attribute__((constructor)) should not rely on global constructors
+// before method call because they will not be initialized before use.
+// See heapprofd_client_api.
+// NOTE: is this right? Shouldn't it occur after construction?
+ __attribute__((constructor))
+void onConstruction() {
+ ALOGD("%s: in progress", __func__); // for logcat analysis
+}
+
+// __attribute__((destructor)) methods occur before any static variable destruction.
+ __attribute__((destructor))
+void onDestruction() {
+ ALOGD("%s: in progress", __func__); // for logcat analysis
+}
diff --git a/media/utils/tests/timecheck_tests.cpp b/media/utils/tests/timecheck_tests.cpp
index 9833dc9..6ebf44d 100644
--- a/media/utils/tests/timecheck_tests.cpp
+++ b/media/utils/tests/timecheck_tests.cpp
@@ -23,6 +23,9 @@
#include <utils/Log.h>
using namespace android::mediautils;
+using namespace std::chrono_literals;
+
+namespace {
TEST(timecheck_tests, success) {
bool timeoutRegistered = false;
@@ -56,7 +59,7 @@
elapsedMsRegistered = elapsedMs;
event = true; // store-release, must be last.
}, 1 /* msec */, false /* crash */);
- usleep(100 * 1000 /* usec */); // extra time as callback called by different thread.
+ std::this_thread::sleep_for(100ms);
}
ASSERT_TRUE(event); // load-acquire, must be first.
ASSERT_TRUE(timeoutRegistered); // only called once on failure, not on dealloc.
@@ -65,3 +68,5 @@
// Note: We do not test TimeCheck crash because TimeCheck is multithreaded and the
// EXPECT_EXIT() signal catching is imperfect due to the gtest fork.
+
+} // namespace
\ No newline at end of file
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8cafdfd..894b31c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -585,14 +585,14 @@
fullConfig.channel_mask = config->channel_mask;
fullConfig.format = config->format;
std::vector<audio_io_handle_t> secondaryOutputs;
-
+ bool isSpatialized;
ret = AudioSystem::getOutputForAttr(&localAttr, &io,
actualSessionId,
&streamType, client.attributionSource,
&fullConfig,
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
- deviceId, &portId, &secondaryOutputs);
+ deviceId, &portId, &secondaryOutputs, &isSpatialized);
ALOGW_IF(!secondaryOutputs.empty(),
"%s does not support secondary outputs, ignoring them", __func__);
} else {
@@ -917,13 +917,33 @@
}
{
std::string timeCheckStats = getIAudioFlingerStatistics().dump();
- dprintf(fd, "\nIAudioFlinger binder call profile\n");
+ dprintf(fd, "\nIAudioFlinger binder call profile:\n");
write(fd, timeCheckStats.c_str(), timeCheckStats.size());
extern mediautils::MethodStatistics<int>& getIEffectStatistics();
timeCheckStats = getIEffectStatistics().dump();
- dprintf(fd, "\nIEffect binder call profile\n");
+ dprintf(fd, "\nIEffect binder call profile:\n");
write(fd, timeCheckStats.c_str(), timeCheckStats.size());
+
+ // Automatically fetch HIDL statistics.
+ std::shared_ptr<std::vector<std::string>> hidlClassNames =
+ mediautils::getStatisticsClassesForModule(
+ METHOD_STATISTICS_MODULE_NAME_AUDIO_HIDL);
+ if (hidlClassNames) {
+ for (const auto& className : *hidlClassNames) {
+ auto stats = mediautils::getStatisticsForClass(className);
+ if (stats) {
+ timeCheckStats = stats->dump();
+ dprintf(fd, "\n%s binder call profile:\n", className.c_str());
+ write(fd, timeCheckStats.c_str(), timeCheckStats.size());
+ }
+ }
+ }
+
+ timeCheckStats = mediautils::TimeCheck::toString();
+ dprintf(fd, "\nTimeCheck:\n");
+ write(fd, timeCheckStats.c_str(), timeCheckStats.size());
+ dprintf(fd, "\n");
}
}
return NO_ERROR;
@@ -1014,6 +1034,7 @@
audio_stream_type_t streamType;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
std::vector<audio_io_handle_t> secondaryOutputs;
+ bool isSpatialized = false;;
// TODO b/182392553: refactor or make clearer
pid_t clientPid =
@@ -1057,7 +1078,8 @@
output.selectedDeviceId = input.selectedDeviceId;
lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
adjAttributionSource, &input.config, input.flags,
- &output.selectedDeviceId, &portId, &secondaryOutputs);
+ &output.selectedDeviceId, &portId, &secondaryOutputs,
+ &isSpatialized);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -1123,7 +1145,7 @@
input.notificationsPerBuffer, input.speed,
input.sharedBuffer, sessionId, &output.flags,
callingPid, adjAttributionSource, input.clientInfo.clientTid,
- &lStatus, portId, input.audioTrackCallback);
+ &lStatus, portId, input.audioTrackCallback, isSpatialized);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index efd2dbd..e6d7cf7 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -41,6 +41,7 @@
#include <media/audiohal/EffectsFactoryHalInterface.h>
#include <mediautils/MethodStatistics.h>
#include <mediautils/ServiceUtilities.h>
+#include <mediautils/TimeCheck.h>
#include "AudioFlinger.h"
@@ -1780,13 +1781,16 @@
status_t AudioFlinger::EffectHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
- std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now();
- mediametrics::Defer defer([startTime, code] {
- std::chrono::system_clock::time_point endTime = std::chrono::system_clock::now();
- getIEffectStatistics().event(code,
- std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
- endTime - startTime).count());
- });
+ const std::string methodName = getIEffectStatistics().getMethodForCode(code);
+ mediautils::TimeCheck check(
+ std::string("IEffect::").append(methodName),
+ [code](bool timeout, float elapsedMs) {
+ if (timeout) {
+ ; // we don't timeout right now on the effect interface.
+ } else {
+ getIEffectStatistics().event(code, elapsedMs);
+ }
+ }, 0 /* timeoutMs */);
return BnEffect::onTransact(code, data, reply, flags);
}
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index aecd4d3..6a138bb 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -82,7 +82,8 @@
/** default behaviour is to start when there are as many frames
* ready as possible (aka. Buffer is full). */
size_t frameCountToBeReady = SIZE_MAX,
- float speed = 1.0f);
+ float speed = 1.0f,
+ bool isSpatialized = false);
virtual ~Track();
virtual status_t initCheck() const;
@@ -201,6 +202,7 @@
audio_output_flags_t getOutputFlags() const { return mFlags; }
float getSpeed() const { return mSpeed; }
+ bool isSpatialized() const override { return mIsSpatialized; }
protected:
// for numerous
@@ -351,6 +353,7 @@
audio_output_flags_t mFlags;
TeePatches mTeePatches;
const float mSpeed;
+ const bool mIsSpatialized;
}; // end of Track
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ae5772d..f8c3ae4 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -65,6 +65,7 @@
#include <media/nbaio/PipeReader.h>
#include <media/nbaio/SourceAudioBufferProvider.h>
#include <mediautils/BatteryNotifier.h>
+#include <mediautils/Process.h>
#include <audiomanager/AudioManager.h>
#include <powermanager/PowerManager.h>
@@ -923,6 +924,20 @@
dprintf(fd, " Local log:\n");
mLocalLog.dump(fd, " " /* prefix */, 40 /* lines */);
+
+ // --all does the statistics
+ bool dumpAll = false;
+ for (const auto &arg : args) {
+ if (arg == String16("--all")) {
+ dumpAll = true;
+ }
+ }
+ if (dumpAll || type() == SPATIALIZER) {
+ const std::string sched = mediautils::getThreadSchedAsString(getTid());
+ if (!sched.empty()) {
+ (void)write(fd, sched.c_str(), sched.size());
+ }
+ }
}
void AudioFlinger::ThreadBase::dumpBase_l(int fd, const Vector<String16>& args __unused)
@@ -2228,7 +2243,8 @@
pid_t tid,
status_t *status,
audio_port_handle_t portId,
- const sp<media::IAudioTrackCallback>& callback)
+ const sp<media::IAudioTrackCallback>& callback,
+ bool isSpatialized)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
@@ -2520,7 +2536,8 @@
channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, creatorPid, attributionSource, trackFlags,
- TrackBase::TYPE_DEFAULT, portId, SIZE_MAX /*frameCountToBeReady*/, speed);
+ TrackBase::TYPE_DEFAULT, portId, SIZE_MAX /*frameCountToBeReady*/,
+ speed, isSpatialized);
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
@@ -3882,14 +3899,14 @@
&& effectChain->containsHapticGeneratingEffect_l()) {
activeHapticSessionId = track->sessionId();
isHapticSessionSpatialized =
- mType == SPATIALIZER && track->canBeSpatialized();
+ mType == SPATIALIZER && track->isSpatialized();
break;
}
if (activeHapticSessionId == AUDIO_SESSION_NONE
&& track->getHapticPlaybackEnabled()) {
activeHapticSessionId = track->sessionId();
isHapticSessionSpatialized =
- mType == SPATIALIZER && track->canBeSpatialized();
+ mType == SPATIALIZER && track->isSpatialized();
}
}
}
@@ -5569,7 +5586,7 @@
AudioMixer::TRACK,
AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
- if (mType == SPATIALIZER && !track->canBeSpatialized()) {
+ if (mType == SPATIALIZER && !track->isSpatialized()) {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
@@ -5619,7 +5636,7 @@
if (mMixerBufferEnabled
&& (track->mainBuffer() == mSinkBuffer
|| track->mainBuffer() == mMixerBuffer)) {
- if (mType == SPATIALIZER && !track->canBeSpatialized()) {
+ if (mType == SPATIALIZER && !track->isSpatialized()) {
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
@@ -9494,6 +9511,7 @@
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
audio_port_handle_t deviceId = mDeviceId;
std::vector<audio_io_handle_t> secondaryOutputs;
+ bool isSpatialized;
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
@@ -9502,7 +9520,8 @@
flags,
&deviceId,
&portId,
- &secondaryOutputs);
+ &secondaryOutputs,
+ &isSpatialized);
ALOGD_IF(!secondaryOutputs.empty(),
"MmapThread::start does not support secondary outputs, ignoring them");
} else {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 982893d..1fccdc5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -483,7 +483,7 @@
if (track->isFastTrack()) {
result |= FAST_SESSION; // caution, only represents first track.
}
- if (track->canBeSpatialized()) {
+ if (track->isSpatialized()) {
result |= SPATIALIZED_SESSION; // caution, only first track.
}
break;
@@ -960,7 +960,8 @@
pid_t tid,
status_t *status /*non-NULL*/,
audio_port_handle_t portId,
- const sp<media::IAudioTrackCallback>& callback);
+ const sp<media::IAudioTrackCallback>& callback,
+ bool isSpatialized);
AudioStreamOut* getOutput() const;
AudioStreamOut* clearOutput();
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 98a1bd9..2677ab3 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -107,9 +107,7 @@
audio_attributes_t attributes() const { return mAttr; }
- bool canBeSpatialized() const { return mIsOut && (mAttr.flags
- & (AUDIO_FLAG_CONTENT_SPATIALIZED | AUDIO_FLAG_NEVER_SPATIALIZE)) == 0
- && mChannelCount > 2; }
+ virtual bool isSpatialized() const { return false; }
#ifdef TEE_SINK
void dumpTee(int fd, const std::string &reason) const {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 279ff3d..6135020 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -633,7 +633,8 @@
track_type type,
audio_port_handle_t portId,
size_t frameCountToBeReady,
- float speed)
+ float speed,
+ bool isSpatialized)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
@@ -667,7 +668,8 @@
mResumeToStopping(false),
mFlushHwPending(false),
mFlags(flags),
- mSpeed(speed)
+ mSpeed(speed),
+ mIsSpatialized(isSpatialized)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 09f947c..20d0523 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -143,7 +143,8 @@
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs,
- output_type_t *outputType) = 0;
+ output_type_t *outputType,
+ bool *isSpatialized) = 0;
// indicates to the audio policy manager that the output starts being used by corresponding
// stream.
virtual status_t startOutput(audio_port_handle_t portId) = 0;
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
index 7c6907d..48f7410 100644
--- a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -258,13 +258,14 @@
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
AudioPolicyInterface::output_type_t outputType;
+ bool isSpatialized;
// TODO b/182392769: use attribution source util
AttributionSourceState attributionSource;
attributionSource.uid = 0;
attributionSource.token = sp<BBinder>::make();
if (mManager->getOutputForAttr(&attr, output, AUDIO_SESSION_NONE, &stream, attributionSource,
- &config, &flags, selectedDeviceId, portId, {}, &outputType) != OK) {
+ &config, &flags, selectedDeviceId, portId, {}, &outputType, &isSpatialized) != OK) {
return false;
}
if (*output == AUDIO_IO_HANDLE_NONE || *portId == AUDIO_PORT_HANDLE_NONE) {
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e45de32..f08d4ad 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1122,7 +1122,8 @@
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
std::vector<sp<AudioPolicyMix>> *secondaryMixes,
- output_type_t *outputType)
+ output_type_t *outputType,
+ bool *isSpatialized)
{
DeviceVector outputDevices;
const audio_port_handle_t requestedPortId = *selectedDeviceId;
@@ -1131,6 +1132,8 @@
mAvailableOutputDevices.getDeviceFromId(requestedPortId);
*outputType = API_OUTPUT_INVALID;
+ *isSpatialized = false;
+
status_t status = getAudioAttributes(resultAttr, attr, *stream);
if (status != NO_ERROR) {
return status;
@@ -1230,7 +1233,7 @@
*output = AUDIO_IO_HANDLE_NONE;
if (!msdDevices.isEmpty()) {
- *output = getOutputForDevices(msdDevices, session, resultAttr, config, flags);
+ *output = getOutputForDevices(msdDevices, session, resultAttr, config, flags, isSpatialized);
if (*output != AUDIO_IO_HANDLE_NONE && setMsdOutputPatches(&outputDevices) == NO_ERROR) {
ALOGV("%s() Using MSD devices %s instead of devices %s",
__func__, msdDevices.toString().c_str(), outputDevices.toString().c_str());
@@ -1240,7 +1243,7 @@
}
if (*output == AUDIO_IO_HANDLE_NONE) {
*output = getOutputForDevices(outputDevices, session, resultAttr, config,
- flags, resultAttr->flags & AUDIO_FLAG_MUTE_HAPTIC);
+ flags, isSpatialized, resultAttr->flags & AUDIO_FLAG_MUTE_HAPTIC);
}
if (*output == AUDIO_IO_HANDLE_NONE) {
return INVALID_OPERATION;
@@ -1275,7 +1278,8 @@
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs,
- output_type_t *outputType)
+ output_type_t *outputType,
+ bool *isSpatialized)
{
// The supplied portId must be AUDIO_PORT_HANDLE_NONE
if (*portId != AUDIO_PORT_HANDLE_NONE) {
@@ -1297,7 +1301,7 @@
status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
- secondaryOutputs != nullptr ? &secondaryMixes : nullptr, outputType);
+ secondaryOutputs != nullptr ? &secondaryMixes : nullptr, outputType, isSpatialized);
if (status != NO_ERROR) {
return status;
}
@@ -1440,6 +1444,7 @@
const audio_attributes_t *attr,
const audio_config_t *config,
audio_output_flags_t *flags,
+ bool *isSpatialized,
bool forceMutingHaptic)
{
audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
@@ -1486,9 +1491,11 @@
*flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_ULTRASOUND);
}
+ *isSpatialized = false;
if (mSpatializerOutput != nullptr
&& canBeSpatializedInt(attr, config,
devices.toTypeAddrVector(), false /* allowCurrentOutputReconfig */)) {
+ *isSpatialized = true;
return mSpatializerOutput->mIoHandle;
}
@@ -3192,9 +3199,10 @@
}
}
}
- return mEffects.registerEffect(desc, io, session, id,
- (strategy == streamToStrategy(AUDIO_STREAM_MUSIC) ||
- strategy == PRODUCT_STRATEGY_NONE));
+ bool isMusicEffect = (session != AUDIO_SESSION_OUTPUT_STAGE)
+ && ((strategy == streamToStrategy(AUDIO_STREAM_MUSIC)
+ || strategy == PRODUCT_STRATEGY_NONE));
+ return mEffects.registerEffect(desc, io, session, id, isMusicEffect);
}
status_t AudioPolicyManager::unregisterEffect(int id)
@@ -4462,10 +4470,11 @@
audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
bool isRequestedDeviceForExclusiveUse = false;
output_type_t outputType;
+ bool isSpatialized;
getOutputForAttrInt(&resultAttr, &output, AUDIO_SESSION_NONE, &attributes,
&stream, sourceDesc->uid(), &config, &flags,
&selectedDeviceId, &isRequestedDeviceForExclusiveUse,
- nullptr, &outputType);
+ nullptr, &outputType, &isSpatialized);
if (output == AUDIO_IO_HANDLE_NONE) {
ALOGV("%s no output for device %s",
__FUNCTION__, sinkDevice->toString().c_str());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 68ae8cb..b12b71a 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -122,7 +122,8 @@
audio_port_handle_t *selectedDeviceId,
audio_port_handle_t *portId,
std::vector<audio_io_handle_t> *secondaryOutputs,
- output_type_t *outputType) override;
+ output_type_t *outputType,
+ bool *isSpatialized) override;
virtual status_t startOutput(audio_port_handle_t portId);
virtual status_t stopOutput(audio_port_handle_t portId);
virtual bool releaseOutput(audio_port_handle_t portId);
@@ -1041,7 +1042,8 @@
audio_port_handle_t *selectedDeviceId,
bool *isRequestedDeviceForExclusiveUse,
std::vector<sp<AudioPolicyMix>> *secondaryMixes,
- output_type_t *outputType);
+ output_type_t *outputType,
+ bool *isSpatialized);
// internal method to return the output handle for the given device and format
audio_io_handle_t getOutputForDevices(
const DeviceVector &devices,
@@ -1049,6 +1051,7 @@
const audio_attributes_t *attr,
const audio_config_t *config,
audio_output_flags_t *flags,
+ bool *isSpatialized,
bool forceMutingHaptic = false);
// Internal method checking if a direct output can be opened matching the requested
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index ae4d174..df49bba 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -90,13 +90,13 @@
if (attr.usage == AUDIO_USAGE_CALL_ASSISTANT
&& ((attr.flags & AUDIO_FLAG_CALL_REDIRECTION) != 0)) {
if (!callAudioInterceptionAllowed(attributionSource)) {
- ALOGE(("permission denied: modify audio routing not allowed "
- "for attributionSource %s"), attributionSource.toString().c_str());
+ ALOGE("%s: call audio interception not allowed for attribution source: %s",
+ __func__, attributionSource.toString().c_str());
return PERMISSION_DENIED;
}
} else if (!modifyAudioRoutingAllowed(attributionSource)) {
- ALOGE(("permission denied: modify audio routing not allowed "
- "for attributionSource %s"), attributionSource.toString().c_str());
+ ALOGE("%s: modify audio routing not allowed for attribution source: %s",
+ __func__, attributionSource.toString().c_str());
return PERMISSION_DENIED;
}
} else {
@@ -383,13 +383,15 @@
AutoCallerClear acc;
AudioPolicyInterface::output_type_t outputType;
+ bool isSpatialized = false;
status_t result = mAudioPolicyManager->getOutputForAttr(&attr, &output, session,
&stream,
adjAttributionSource,
&config,
&flags, &selectedDeviceId, &portId,
&secondaryOutputs,
- &outputType);
+ &outputType,
+ &isSpatialized);
// FIXME: Introduce a way to check for the the telephony device before opening the output
if (result == NO_ERROR) {
@@ -426,7 +428,7 @@
if (result == NO_ERROR) {
sp<AudioPlaybackClient> client =
new AudioPlaybackClient(attr, output, adjAttributionSource, session,
- portId, selectedDeviceId, stream);
+ portId, selectedDeviceId, stream, isSpatialized);
mAudioPlaybackClients.add(portId, client);
_aidl_return->output = VALUE_OR_RETURN_BINDER_STATUS(
@@ -440,6 +442,7 @@
_aidl_return->secondaryOutputs = VALUE_OR_RETURN_BINDER_STATUS(
convertContainer<std::vector<int32_t>>(secondaryOutputs,
legacy2aidl_audio_io_handle_t_int32_t));
+ _aidl_return->isSpatialized = isSpatialized;
}
return binderStatusFromStatusT(result);
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index bdd86b1..da0a4a9 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -505,6 +505,8 @@
{
Mutex::Autolock _l(mLock);
+ ALOGI("%s mSpatializer %p level %d", __func__, mSpatializer.get(), (int)mSpatializer->getLevel());
+
if (mSpatializer != nullptr) {
// Note: mSpatializer != nullptr => mAudioPolicyManager != nullptr
if (mSpatializer->getLevel() != media::SpatializationLevel::NONE) {
@@ -544,11 +546,13 @@
}
}
-size_t AudioPolicyService::countActiveClientsOnOutput_l(audio_io_handle_t output) REQUIRES(mLock) {
+size_t AudioPolicyService::countActiveClientsOnOutput_l(
+ audio_io_handle_t output, bool spatializedOnly) {
size_t count = 0;
for (size_t i = 0; i < mAudioPlaybackClients.size(); i++) {
auto client = mAudioPlaybackClients.valueAt(i);
- if (client->io == output && client->active) {
+ if (client->io == output && client->active
+ && (!spatializedOnly || client->isSpatialized)) {
count++;
}
}
@@ -564,14 +568,21 @@
void AudioPolicyService::doOnUpdateActiveSpatializerTracks()
{
- Mutex::Autolock _l(mLock);
- if (mSpatializer == nullptr) {
- return;
+ sp<Spatializer> spatializer;
+ size_t activeClients;
+ {
+ Mutex::Autolock _l(mLock);
+ if (mSpatializer == nullptr) {
+ return;
+ }
+ spatializer = mSpatializer;
+ activeClients = countActiveClientsOnOutput_l(mSpatializer->getOutput());
}
- mSpatializer->updateActiveTracks(countActiveClientsOnOutput_l(mSpatializer->getOutput()));
+ if (spatializer != nullptr) {
+ spatializer->updateActiveTracks(activeClients);
+ }
}
-
status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle,
int delayMs)
@@ -1629,6 +1640,9 @@
}
}
+void AudioPolicyService::UidPolicy::onUidProcAdjChanged(uid_t uid __unused) {
+}
+
void AudioPolicyService::UidPolicy::updateOverrideUid(uid_t uid, bool active, bool insert) {
updateUid(&mOverrideUids, uid, active, ActivityManager::PROCESS_STATE_UNKNOWN, insert);
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index 43b579f..d863ff1 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -450,7 +450,8 @@
void onUidGone(uid_t uid, bool disabled) override;
void onUidIdle(uid_t uid, bool disabled) override;
void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
- int32_t capability);
+ int32_t capability) override;
+ void onUidProcAdjChanged(uid_t uid) override;
void addOverrideUid(uid_t uid, bool active) { updateOverrideUid(uid, active, true); }
void removeOverrideUid(uid_t uid) { updateOverrideUid(uid, false, false); }
@@ -970,12 +971,14 @@
AudioPlaybackClient(const audio_attributes_t attributes,
const audio_io_handle_t io, AttributionSourceState attributionSource,
const audio_session_t session, audio_port_handle_t portId,
- audio_port_handle_t deviceId, audio_stream_type_t stream) :
+ audio_port_handle_t deviceId, audio_stream_type_t stream,
+ bool isSpatialized) :
AudioClient(attributes, io, attributionSource, session, portId,
- deviceId), stream(stream) {}
+ deviceId), stream(stream), isSpatialized(isSpatialized) {}
~AudioPlaybackClient() override = default;
const audio_stream_type_t stream;
+ const bool isSpatialized;
};
void getPlaybackClientAndEffects(audio_port_handle_t portId,
@@ -1005,7 +1008,16 @@
void loadAudioPolicyManager();
void unloadAudioPolicyManager();
- size_t countActiveClientsOnOutput_l(audio_io_handle_t output) REQUIRES(mLock);
+ /**
+ * Returns the number of active audio tracks on the specified output mixer.
+ * The query can be specified to only include spatialized audio tracks or consider
+ * all tracks.
+ * @param output the I/O handle of the output mixer to consider
+ * @param spatializedOnly true if only spatialized tracks should be considered
+ * @return the number of active tracks.
+ */
+ size_t countActiveClientsOnOutput_l(
+ audio_io_handle_t output, bool spatializedOnly = true) REQUIRES(mLock);
mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing
// device connection state or routing
diff --git a/services/audiopolicy/service/Spatializer.cpp b/services/audiopolicy/service/Spatializer.cpp
index 579b852..3b466d7 100644
--- a/services/audiopolicy/service/Spatializer.cpp
+++ b/services/audiopolicy/service/Spatializer.cpp
@@ -298,7 +298,7 @@
callback = mSpatializerCallback;
if (levelChanged && mEngine != nullptr) {
- setEffectParameter_l(SPATIALIZER_PARAM_LEVEL, std::vector<SpatializationLevel>{level});
+ checkEngineState_l();
}
checkSensorsState_l();
}
@@ -373,10 +373,8 @@
break;
}
- if (mPoseController != nullptr) {
- mPoseController->setDesiredMode(mDesiredHeadTrackingMode);
- checkSensorsState_l();
- }
+ checkPoseController_l();
+ checkSensorsState_l();
return Status::ok();
}
@@ -449,6 +447,7 @@
}
std::lock_guard lock(mLock);
mHeadSensor = sensorHandle;
+ checkPoseController_l();
checkSensorsState_l();
return Status::ok();
}
@@ -460,7 +459,9 @@
}
std::lock_guard lock(mLock);
mScreenSensor = sensorHandle;
- checkSensorsState_l();
+ if (mPoseController != nullptr) {
+ mPoseController->setScreenSensor(mScreenSensor);
+ }
return Status::ok();
}
@@ -594,6 +595,7 @@
ALOGV("%s(%d)", __func__, (int) mode);
sp<media::ISpatializerHeadTrackingCallback> callback;
SpatializerHeadTrackingMode spatializerMode;
+ bool modeChanged = false;
{
std::lock_guard lock(mLock);
if (!mSupportsHeadTracking) {
@@ -613,10 +615,15 @@
LOG_ALWAYS_FATAL("Unknown mode: %d", mode);
}
}
+ modeChanged = mActualHeadTrackingMode != spatializerMode;
mActualHeadTrackingMode = spatializerMode;
+ if (modeChanged && mEngine != nullptr) {
+ setEffectParameter_l(SPATIALIZER_PARAM_HEADTRACKING_MODE,
+ std::vector<SpatializerHeadTrackingMode>{spatializerMode});
+ }
callback = mHeadTrackingCallback;
}
- if (callback != nullptr) {
+ if (callback != nullptr && modeChanged) {
callback->onHeadTrackingModeChanged(spatializerMode);
}
}
@@ -648,30 +655,20 @@
return status;
}
- setEffectParameter_l(SPATIALIZER_PARAM_LEVEL,
- std::vector<SpatializationLevel>{mLevel});
- setEffectParameter_l(SPATIALIZER_PARAM_HEADTRACKING_MODE,
- std::vector<SpatializerHeadTrackingMode>{mActualHeadTrackingMode});
-
- mEngine->setEnabled(true);
outputChanged = mOutput != output;
mOutput = output;
+ mNumActiveTracks = numActiveTracks;
+ checkEngineState_l();
if (mSupportsHeadTracking) {
- mPoseController = std::make_shared<SpatializerPoseController>(
- static_cast<SpatializerPoseController::Listener*>(this), 10ms, 50ms);
- LOG_ALWAYS_FATAL_IF(mPoseController == nullptr,
- "%s could not allocate pose controller", __func__);
-
- mPoseController->setDesiredMode(mDesiredHeadTrackingMode);
- mNumActiveTracks = numActiveTracks;
+ checkPoseController_l();
checkSensorsState_l();
- mPoseController->setDisplayOrientation(mDisplayOrientation);
poseController = mPoseController;
}
callback = mSpatializerCallback;
}
if (poseController != nullptr) {
+ poseController->calculateAsync();
poseController->waitUntilCalculated();
}
@@ -709,8 +706,11 @@
void Spatializer::updateActiveTracks(size_t numActiveTracks) {
std::lock_guard lock(mLock);
- mNumActiveTracks = numActiveTracks;
- checkSensorsState_l();
+ if (mNumActiveTracks != numActiveTracks) {
+ mNumActiveTracks = numActiveTracks;
+ checkEngineState_l();
+ checkSensorsState_l();
+ }
}
void Spatializer::checkSensorsState_l() {
@@ -727,6 +727,41 @@
}
}
+void Spatializer::checkEngineState_l() {
+ if (mEngine != nullptr) {
+ if (mLevel != SpatializationLevel::NONE && mNumActiveTracks > 0) {
+ mEngine->setEnabled(true);
+ setEffectParameter_l(SPATIALIZER_PARAM_LEVEL,
+ std::vector<SpatializationLevel>{mLevel});
+ setEffectParameter_l(SPATIALIZER_PARAM_HEADTRACKING_MODE,
+ std::vector<SpatializerHeadTrackingMode>{mActualHeadTrackingMode});
+ } else {
+ setEffectParameter_l(SPATIALIZER_PARAM_LEVEL,
+ std::vector<SpatializationLevel>{SpatializationLevel::NONE});
+ mEngine->setEnabled(false);
+ }
+ }
+}
+
+void Spatializer::checkPoseController_l() {
+ bool isControllerNeeded = mDesiredHeadTrackingMode != HeadTrackingMode::STATIC
+ && mHeadSensor != SpatializerPoseController::INVALID_SENSOR;
+
+ if (isControllerNeeded && mPoseController == nullptr) {
+ mPoseController = std::make_shared<SpatializerPoseController>(
+ static_cast<SpatializerPoseController::Listener*>(this),
+ 10ms, std::chrono::microseconds::max());
+ LOG_ALWAYS_FATAL_IF(mPoseController == nullptr,
+ "%s could not allocate pose controller", __func__);
+ mPoseController->setDisplayOrientation(mDisplayOrientation);
+ } else if (!isControllerNeeded && mPoseController != nullptr) {
+ mPoseController.reset();
+ }
+ if (mPoseController != nullptr) {
+ mPoseController->setDesiredMode(mDesiredHeadTrackingMode);
+ }
+}
+
void Spatializer::calculateHeadPose() {
ALOGV("%s", __func__);
std::lock_guard lock(mLock);
diff --git a/services/audiopolicy/service/Spatializer.h b/services/audiopolicy/service/Spatializer.h
index 1382124..a36ba61 100644
--- a/services/audiopolicy/service/Spatializer.h
+++ b/services/audiopolicy/service/Spatializer.h
@@ -283,6 +283,18 @@
*/
void checkSensorsState_l() REQUIRES(mLock);
+ /**
+ * Checks if the head pose controller should be created or destroyed according
+ * to desired head tracking mode.
+ */
+ void checkPoseController_l() REQUIRES(mLock);
+
+ /**
+ * Checks if the spatializer effect should be enabled based on
+ * playback activity and requested level.
+ */
+ void checkEngineState_l() REQUIRES(mLock);
+
/** Effect engine descriptor */
const effect_descriptor_t mEngineDescriptor;
/** Callback interface to parent audio policy service */
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index da42ab4..5429176 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -218,13 +218,14 @@
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
AudioPolicyInterface::output_type_t outputType;
+ bool isSpatialized;
// TODO b/182392769: use attribution source util
AttributionSourceState attributionSource = AttributionSourceState();
attributionSource.uid = 0;
attributionSource.token = sp<BBinder>::make();
ASSERT_EQ(OK, mManager->getOutputForAttr(
&attr, output, AUDIO_SESSION_NONE, &stream, attributionSource, &config, &flags,
- selectedDeviceId, portId, {}, &outputType));
+ selectedDeviceId, portId, {}, &outputType, &isSpatialized));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
ASSERT_NE(AUDIO_IO_HANDLE_NONE, *output);
}
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index 69300be..1e2dccb 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -81,7 +81,6 @@
"device3/Camera3OutputUtils.cpp",
"device3/Camera3DeviceInjectionMethods.cpp",
"device3/UHRCropAndMeteringRegionMapper.cpp",
- "device3/PreviewFrameScheduler.cpp",
"device3/hidl/HidlCamera3Device.cpp",
"device3/hidl/HidlCamera3OfflineSession.cpp",
"device3/hidl/HidlCamera3OutputUtils.cpp",
@@ -112,7 +111,6 @@
],
shared_libs: [
- "libandroid",
"libbase",
"libdl",
"libexif",
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index c576162..a965080 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -3670,7 +3670,8 @@
status_t res = mAm.linkToDeath(this);
mAm.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE
| ActivityManager::UID_OBSERVER_IDLE
- | ActivityManager::UID_OBSERVER_ACTIVE | ActivityManager::UID_OBSERVER_PROCSTATE,
+ | ActivityManager::UID_OBSERVER_ACTIVE | ActivityManager::UID_OBSERVER_PROCSTATE
+ | ActivityManager::UID_OBSERVER_PROC_OOM_ADJ,
ActivityManager::PROCESS_STATE_UNKNOWN,
String16("cameraserver"));
if (res == OK) {
@@ -3719,9 +3720,9 @@
bool procStateChange = false;
{
Mutex::Autolock _l(mUidLock);
- if ((mMonitoredUids.find(uid) != mMonitoredUids.end()) &&
- (mMonitoredUids[uid].first != procState)) {
- mMonitoredUids[uid].first = procState;
+ if (mMonitoredUids.find(uid) != mMonitoredUids.end() &&
+ mMonitoredUids[uid].procState != procState) {
+ mMonitoredUids[uid].procState = procState;
procStateChange = true;
}
}
@@ -3734,15 +3735,33 @@
}
}
+void CameraService::UidPolicy::onUidProcAdjChanged(uid_t uid) {
+ bool procAdjChange = false;
+ {
+ Mutex::Autolock _l(mUidLock);
+ if (mMonitoredUids.find(uid) != mMonitoredUids.end()) {
+ procAdjChange = true;
+ }
+ }
+
+ if (procAdjChange) {
+ sp<CameraService> service = mService.promote();
+ if (service != nullptr) {
+ service->notifyMonitoredUids();
+ }
+ }
+}
+
void CameraService::UidPolicy::registerMonitorUid(uid_t uid) {
Mutex::Autolock _l(mUidLock);
auto it = mMonitoredUids.find(uid);
if (it != mMonitoredUids.end()) {
- it->second.second++;
+ it->second.refCount++;
} else {
- mMonitoredUids.emplace(
- std::pair<uid_t, std::pair<int32_t, size_t>> (uid,
- std::pair<int32_t, size_t> (ActivityManager::PROCESS_STATE_NONEXISTENT, 1)));
+ MonitoredUid monitoredUid;
+ monitoredUid.procState = ActivityManager::PROCESS_STATE_NONEXISTENT;
+ monitoredUid.refCount = 1;
+ mMonitoredUids.emplace(std::pair<uid_t, MonitoredUid>(uid, monitoredUid));
}
}
@@ -3750,8 +3769,8 @@
Mutex::Autolock _l(mUidLock);
auto it = mMonitoredUids.find(uid);
if (it != mMonitoredUids.end()) {
- it->second.second--;
- if (it->second.second == 0) {
+ it->second.refCount--;
+ if (it->second.refCount == 0) {
mMonitoredUids.erase(it);
}
} else {
@@ -3829,7 +3848,7 @@
int32_t CameraService::UidPolicy::getProcStateLocked(uid_t uid) {
int32_t procState = ActivityManager::PROCESS_STATE_UNKNOWN;
if (mMonitoredUids.find(uid) != mMonitoredUids.end()) {
- procState = mMonitoredUids[uid].first;
+ procState = mMonitoredUids[uid].procState;
}
return procState;
}
@@ -4431,8 +4450,9 @@
void CameraService::dumpOpenSessionClientLogs(int fd,
const Vector<String16>& args, const String8& cameraId) {
auto clientDescriptor = mActiveClientManager.get(cameraId);
- dprintf(fd, " Device %s is open. Client instance dump:\n",
- cameraId.string());
+ dprintf(fd, " %s : Device %s is open. Client instance dump:\n",
+ getFormattedCurrentTime().string(),
+ cameraId.string());
dprintf(fd, " Client priority score: %d state: %d\n",
clientDescriptor->getPriority().getScore(),
clientDescriptor->getPriority().getState());
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 6346f50..89a537d 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -698,11 +698,13 @@
bool isUidActive(uid_t uid, String16 callingPackage);
int32_t getProcState(uid_t uid);
- void onUidGone(uid_t uid, bool disabled);
- void onUidActive(uid_t uid);
- void onUidIdle(uid_t uid, bool disabled);
+ // IUidObserver
+ void onUidGone(uid_t uid, bool disabled) override;
+ void onUidActive(uid_t uid) override;
+ void onUidIdle(uid_t uid, bool disabled) override;
void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq,
- int32_t capability);
+ int32_t capability) override;
+ void onUidProcAdjChanged(uid_t uid) override;
void addOverrideUid(uid_t uid, String16 callingPackage, bool active);
void removeOverrideUid(uid_t uid, String16 callingPackage);
@@ -717,13 +719,18 @@
int32_t getProcStateLocked(uid_t uid);
void updateOverrideUid(uid_t uid, String16 callingPackage, bool active, bool insert);
+ struct MonitoredUid {
+ int32_t procState;
+ size_t refCount;
+ };
+
Mutex mUidLock;
bool mRegistered;
ActivityManager mAm;
wp<CameraService> mService;
std::unordered_set<uid_t> mActiveUids;
- // Monitored uid map to cached procState and refCount pair
- std::unordered_map<uid_t, std::pair<int32_t, size_t>> mMonitoredUids;
+ // Monitored uid map
+ std::unordered_map<uid_t, MonitoredUid> mMonitoredUids;
std::unordered_map<uid_t, bool> mOverrideUids;
}; // class UidPolicy
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 701206a..1f0e095 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -518,6 +518,10 @@
metadataRequestList.push_back(physicalSettingsList);
surfaceMapList.push_back(surfaceMap);
+
+ if (!request.mUserTag.empty()) {
+ mUserTag = request.mUserTag;
+ }
}
mRequestIdCounter++;
@@ -1964,7 +1968,8 @@
if (remoteCb != 0) {
remoteCb->onDeviceIdle();
}
- Camera2ClientBase::notifyIdle(requestCount, resultErrorCount, deviceError, streamStats);
+ Camera2ClientBase::notifyIdleWithUserTag(requestCount, resultErrorCount, deviceError,
+ streamStats, mUserTag);
}
void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 9d1deb1..3af0b80 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -349,6 +349,9 @@
// Override the camera characteristics for performance class primary cameras.
bool mOverrideForPerfClass;
+
+ // The string representation of object passed into CaptureRequest.setTag.
+ std::string mUserTag;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 6ed3c02..0ac047a 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -332,9 +332,10 @@
}
template <typename TClientBase>
-void Camera2ClientBase<TClientBase>::notifyIdle(
+void Camera2ClientBase<TClientBase>::notifyIdleWithUserTag(
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
- const std::vector<hardware::CameraStreamStats>& streamStats) {
+ const std::vector<hardware::CameraStreamStats>& streamStats,
+ const std::string& userTag) {
if (mDeviceActive) {
status_t res = TClientBase::finishCameraStreamingOps();
if (res != OK) {
@@ -342,7 +343,7 @@
TClientBase::mCameraIdStr.string(), res);
}
CameraServiceProxyWrapper::logIdle(TClientBase::mCameraIdStr,
- requestCount, resultErrorCount, deviceError, streamStats);
+ requestCount, resultErrorCount, deviceError, userTag, streamStats);
}
mDeviceActive = false;
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 6b90f5e..9cba2f1 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -75,9 +75,9 @@
const CaptureResultExtras& resultExtras);
// Returns errors on app ops permission failures
virtual status_t notifyActive(float maxPreviewFps);
- virtual void notifyIdle(int64_t requestCount, int64_t resultErrorCount,
- bool deviceError,
- const std::vector<hardware::CameraStreamStats>& streamStats);
+ virtual void notifyIdle(int64_t /*requestCount*/, int64_t /*resultErrorCount*/,
+ bool /*deviceError*/,
+ const std::vector<hardware::CameraStreamStats>&) {}
virtual void notifyShutter(const CaptureResultExtras& resultExtras,
nsecs_t timestamp);
virtual void notifyAutoFocus(uint8_t newState, int triggerId);
@@ -88,6 +88,11 @@
virtual void notifyRequestQueueEmpty();
virtual void notifyRepeatingRequestError(long lastFrameNumber);
+ void notifyIdleWithUserTag(int64_t requestCount, int64_t resultErrorCount,
+ bool deviceError,
+ const std::vector<hardware::CameraStreamStats>& streamStats,
+ const std::string& userTag);
+
int getCameraId() const;
const sp<CameraDeviceBase>&
getCameraDevice();
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index aeffd24..04e65d4 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2649,7 +2649,7 @@
status_t Camera3Device::registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool hasAppCallback, nsecs_t maxExpectedDuration,
+ bool hasAppCallback, nsecs_t minExpectedDuration, nsecs_t maxExpectedDuration,
const std::set<std::set<String8>>& physicalCameraIds,
bool isStillCapture, bool isZslCapture, bool rotateAndCropAuto,
const std::set<std::string>& cameraIdsWithZoom,
@@ -2659,8 +2659,9 @@
ssize_t res;
res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
- hasAppCallback, maxExpectedDuration, physicalCameraIds, isStillCapture, isZslCapture,
- rotateAndCropAuto, cameraIdsWithZoom, requestTimeNs, outputSurfaces));
+ hasAppCallback, minExpectedDuration, maxExpectedDuration, physicalCameraIds,
+ isStillCapture, isZslCapture, rotateAndCropAuto, cameraIdsWithZoom, requestTimeNs,
+ outputSurfaces));
if (res < 0) return res;
if (mInFlightMap.size() == 1) {
@@ -2856,6 +2857,7 @@
mInterface(interface),
mListener(nullptr),
mId(getId(parent)),
+ mRequestClearing(false),
mFirstRepeating(false),
mReconfigured(false),
mDoPause(false),
@@ -3089,6 +3091,7 @@
*lastFrameNumber = mRepeatingLastFrameNumber;
}
mRepeatingLastFrameNumber = hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES;
+ mRequestClearing = true;
mRequestSignal.signal();
return OK;
}
@@ -3216,13 +3219,16 @@
return true;
}
-nsecs_t Camera3Device::RequestThread::calculateMaxExpectedDuration(const camera_metadata_t *request) {
- nsecs_t maxExpectedDuration = kDefaultExpectedDuration;
+std::pair<nsecs_t, nsecs_t> Camera3Device::RequestThread::calculateExpectedDurationRange(
+ const camera_metadata_t *request) {
+ std::pair<nsecs_t, nsecs_t> expectedRange(
+ InFlightRequest::kDefaultMinExpectedDuration,
+ InFlightRequest::kDefaultMaxExpectedDuration);
camera_metadata_ro_entry_t e = camera_metadata_ro_entry_t();
find_camera_metadata_ro_entry(request,
ANDROID_CONTROL_AE_MODE,
&e);
- if (e.count == 0) return maxExpectedDuration;
+ if (e.count == 0) return expectedRange;
switch (e.data.u8[0]) {
case ANDROID_CONTROL_AE_MODE_OFF:
@@ -3230,13 +3236,15 @@
ANDROID_SENSOR_EXPOSURE_TIME,
&e);
if (e.count > 0) {
- maxExpectedDuration = e.data.i64[0];
+ expectedRange.first = e.data.i64[0];
+ expectedRange.second = expectedRange.first;
}
find_camera_metadata_ro_entry(request,
ANDROID_SENSOR_FRAME_DURATION,
&e);
if (e.count > 0) {
- maxExpectedDuration = std::max(e.data.i64[0], maxExpectedDuration);
+ expectedRange.first = std::max(e.data.i64[0], expectedRange.first);
+ expectedRange.second = expectedRange.first;
}
break;
default:
@@ -3244,12 +3252,13 @@
ANDROID_CONTROL_AE_TARGET_FPS_RANGE,
&e);
if (e.count > 1) {
- maxExpectedDuration = 1e9 / e.data.u8[0];
+ expectedRange.first = 1e9 / e.data.i32[1];
+ expectedRange.second = 1e9 / e.data.i32[0];
}
break;
}
- return maxExpectedDuration;
+ return expectedRange;
}
bool Camera3Device::RequestThread::skipHFRTargetFPSUpdate(int32_t tag,
@@ -3864,11 +3873,13 @@
isZslCapture = true;
}
}
+ auto expectedDurationRange = calculateExpectedDurationRange(settings);
res = parent->registerInFlight(halRequest->frame_number,
totalNumBuffers, captureRequest->mResultExtras,
/*hasInput*/halRequest->input_buffer != NULL,
hasCallback,
- calculateMaxExpectedDuration(settings),
+ /*min*/expectedDurationRange.first,
+ /*max*/expectedDurationRange.second,
requestedPhysicalCameras, isStillCapture, isZslCapture,
captureRequest->mRotateAndCropAuto, mPrevCameraIdsWithZoom,
(mUseHalBufManager) ? uniqueSurfaceIdMap :
@@ -4209,7 +4220,9 @@
break;
}
- res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout);
+ if (!mRequestClearing) {
+ res = mRequestSignal.waitRelative(mRequestLock, kRequestTimeout);
+ }
if ((mRequestQueue.empty() && mRepeatingRequests.empty()) ||
exitPending()) {
@@ -4231,6 +4244,7 @@
if (parent != nullptr) {
parent->mRequestBufferSM.onRequestThreadPaused();
}
+ mRequestClearing = false;
}
// Stop waiting for now and let thread management happen
return NULL;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index c1ba88a..749b342 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -302,7 +302,6 @@
static const nsecs_t kMinWarnInflightDuration = 5000000000; // 5 s
static const size_t kInFlightWarnLimit = 30;
static const size_t kInFlightWarnLimitHighSpeed = 256; // batch size 32 * pipe depth 8
- static const nsecs_t kDefaultExpectedDuration = 100000000; // 100 ms
static const nsecs_t kMinInflightDuration = 5000000000; // 5 s
static const nsecs_t kBaseGetBufferWait = 3000000000; // 3 sec.
// SCHED_FIFO priority for request submission thread in HFR mode
@@ -956,8 +955,9 @@
// send request in mNextRequests to HAL in a batch. Return true = sucssess
bool sendRequestsBatch();
- // Calculate the expected maximum duration for a request
- nsecs_t calculateMaxExpectedDuration(const camera_metadata_t *request);
+ // Calculate the expected (minimum, maximum) duration range for a request
+ std::pair<nsecs_t, nsecs_t> calculateExpectedDurationRange(
+ const camera_metadata_t *request);
// Check and update latest session parameters based on the current request settings.
bool updateSessionParameters(const CameraMetadata& settings);
@@ -982,6 +982,8 @@
Mutex mRequestLock;
Condition mRequestSignal;
+ bool mRequestClearing;
+
Condition mRequestSubmittedSignal;
RequestList mRequestQueue;
RequestList mRepeatingRequests;
@@ -1072,7 +1074,7 @@
status_t registerInFlight(uint32_t frameNumber,
int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
- bool callback, nsecs_t maxExpectedDuration,
+ bool callback, nsecs_t minExpectedDuration, nsecs_t maxExpectedDuration,
const std::set<std::set<String8>>& physicalCameraIds,
bool isStillCapture, bool isZslCapture, bool rotateAndCropAuto,
const std::set<std::string>& cameraIdsWithZoom, const SurfaceMap& outputSurfaces,
@@ -1323,6 +1325,9 @@
// performance class.
bool mOverrideForPerfClass;
+ // The current minimum expected frame duration based on AE_TARGET_FPS_RANGE
+ nsecs_t mMinExpectedDuration = 0;
+
// Injection camera related methods.
class Camera3DeviceInjectionMethods : public virtual RefBase {
public:
diff --git a/services/camera/libcameraservice/device3/Camera3FakeStream.h b/services/camera/libcameraservice/device3/Camera3FakeStream.h
index 48e44dc..8cecabd 100644
--- a/services/camera/libcameraservice/device3/Camera3FakeStream.h
+++ b/services/camera/libcameraservice/device3/Camera3FakeStream.h
@@ -100,6 +100,7 @@
virtual status_t setBatchSize(size_t batchSize) override;
+ virtual void onMinDurationChanged(nsecs_t /*duration*/) {}
protected:
/**
diff --git a/services/camera/libcameraservice/device3/Camera3OfflineSession.h b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
index 0f7d145..a799719 100644
--- a/services/camera/libcameraservice/device3/Camera3OfflineSession.h
+++ b/services/camera/libcameraservice/device3/Camera3OfflineSession.h
@@ -246,6 +246,9 @@
// For client methods such as disconnect/dump
std::mutex mInterfaceLock;
+ // The current minimum expected frame duration based on AE_TARGET_FPS_RANGE
+ nsecs_t mMinExpectedDuration = 0;
+
// SetErrorInterface
void setErrorState(const char *fmt, ...) override;
void setErrorStateLocked(const char *fmt, ...) override;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 0f61065..37f9227 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -376,32 +376,24 @@
dumpImageToDisk(timestamp, anwBuffer, anwReleaseFence);
}
- nsecs_t t = mPreviewFrameScheduler != nullptr ? readoutTimestamp : timestamp;
- t -= mTimestampOffset;
- if (mPreviewFrameScheduler != nullptr) {
- res = mPreviewFrameScheduler->queuePreviewBuffer(t, transform,
- anwBuffer, anwReleaseFence);
- if (res != OK) {
- ALOGE("%s: Stream %d: Error queuing buffer to preview buffer scheduler: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
- } else {
- setTransform(transform, true/*mayChangeMirror*/);
- res = native_window_set_buffers_timestamp(mConsumer.get(), t);
- if (res != OK) {
- ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
+ nsecs_t captureTime = (mSyncToDisplay ? readoutTimestamp : timestamp) - mTimestampOffset;
+ nsecs_t presentTime = mSyncToDisplay ?
+ syncTimestampToDisplayLocked(captureTime) : captureTime;
- queueHDRMetadata(anwBuffer->handle, currentConsumer, dynamic_range_profile);
+ setTransform(transform, true/*mayChangeMirror*/);
+ res = native_window_set_buffers_timestamp(mConsumer.get(), presentTime);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
- res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
- if (shouldLogError(res, state)) {
- ALOGE("%s: Stream %d: Error queueing buffer to native window:"
- " %s (%d)", __FUNCTION__, mId, strerror(-res), res);
- }
+ queueHDRMetadata(anwBuffer->handle, currentConsumer, dynamic_range_profile);
+
+ res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
+ if (shouldLogError(res, state)) {
+ ALOGE("%s: Stream %d: Error queueing buffer to native window:"
+ " %s (%d)", __FUNCTION__, mId, strerror(-res), res);
}
}
mLock.lock();
@@ -476,7 +468,7 @@
return res;
}
- if ((res = configureConsumerQueueLocked(true /*allowPreviewScheduler*/)) != OK) {
+ if ((res = configureConsumerQueueLocked(true /*allowDisplaySync*/)) != OK) {
return res;
}
@@ -500,7 +492,7 @@
return OK;
}
-status_t Camera3OutputStream::configureConsumerQueueLocked(bool allowPreviewScheduler) {
+status_t Camera3OutputStream::configureConsumerQueueLocked(bool allowDisplaySync) {
status_t res;
mTraceFirstBuffer = true;
@@ -590,16 +582,17 @@
int timestampBase = getTimestampBase();
bool isDefaultTimeBase = (timestampBase ==
OutputConfiguration::TIMESTAMP_BASE_DEFAULT);
- if (allowPreviewScheduler) {
+ if (allowDisplaySync) {
// We cannot distinguish between a SurfaceView and an ImageReader of
- // preview buffer format. The PreviewFrameScheduler needs to handle both.
+ // preview buffer format. Frames are synchronized to display in both
+ // cases.
bool forceChoreographer = (timestampBase ==
OutputConfiguration::TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED);
bool defaultToChoreographer = (isDefaultTimeBase && isConsumedByHWComposer() &&
!property_get_bool("camera.disable_preview_scheduler", false));
if (forceChoreographer || defaultToChoreographer) {
- mPreviewFrameScheduler = std::make_unique<PreviewFrameScheduler>(*this, mConsumer);
- mTotalBufferCount += PreviewFrameScheduler::kQueueDepthWatermark;
+ mSyncToDisplay = true;
+ mTotalBufferCount += kDisplaySyncExtraBuffer;
}
}
@@ -1244,6 +1237,11 @@
return OK;
}
+void Camera3OutputStream::onMinDurationChanged(nsecs_t duration) {
+ Mutex::Autolock l(mLock);
+ mMinExpectedDuration = duration;
+}
+
void Camera3OutputStream::returnPrefetchedBuffersLocked() {
std::vector<Surface::BatchBuffer> batchedBuffers;
@@ -1261,9 +1259,61 @@
}
}
-bool Camera3OutputStream::shouldLogError(status_t res) {
- Mutex::Autolock l(mLock);
- return shouldLogError(res, mState);
+nsecs_t Camera3OutputStream::syncTimestampToDisplayLocked(nsecs_t t) {
+ ParcelableVsyncEventData parcelableVsyncEventData;
+ auto res = mDisplayEventReceiver.getLatestVsyncEventData(&parcelableVsyncEventData);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Error getting latest vsync event data: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ mLastCaptureTime = t;
+ mLastPresentTime = t;
+ return t;
+ }
+
+ const VsyncEventData& vsyncEventData = parcelableVsyncEventData.vsync;
+ nsecs_t currentTime = systemTime();
+
+ // Reset capture to present time offset if more than 1 second
+ // between frames.
+ if (t - mLastCaptureTime > kSpacingResetIntervalNs) {
+ for (size_t i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ if (vsyncEventData.frameTimelines[i].deadlineTimestamp >= currentTime) {
+ mCaptureToPresentOffset =
+ vsyncEventData.frameTimelines[i].expectedPresentationTime - t;
+ break;
+ }
+ }
+ }
+
+ nsecs_t idealPresentT = t + mCaptureToPresentOffset;
+ nsecs_t expectedPresentT = mLastPresentTime;
+ nsecs_t minDiff = INT64_MAX;
+ // Derive minimum intervals between presentation times based on minimal
+ // expected duration.
+ size_t minVsyncs = (mMinExpectedDuration + vsyncEventData.frameInterval - 1) /
+ vsyncEventData.frameInterval - 1;
+ nsecs_t minInterval = minVsyncs * vsyncEventData.frameInterval + kTimelineThresholdNs;
+ // Find best timestamp in the vsync timeline:
+ // - closest to the ideal present time,
+ // - deadline timestamp is greater than the current time, and
+ // - the candidate present time is at least minInterval in the future
+ // compared to last present time.
+ for (const auto& vsyncTime : vsyncEventData.frameTimelines) {
+ if (std::abs(vsyncTime.expectedPresentationTime - idealPresentT) < minDiff &&
+ vsyncTime.deadlineTimestamp >= currentTime &&
+ vsyncTime.expectedPresentationTime > mLastPresentTime + minInterval) {
+ expectedPresentT = vsyncTime.expectedPresentationTime;
+ minDiff = std::abs(vsyncTime.expectedPresentationTime - idealPresentT);
+ }
+ }
+ mLastCaptureTime = t;
+ mLastPresentTime = expectedPresentT;
+
+ // Move the expected presentation time back by 1/3 of frame interval to
+ // mitigate the time drift. Due to time drift, if we directly use the
+ // expected presentation time, often times 2 expected presentation time
+ // falls into the same VSYNC interval.
+ return expectedPresentT - vsyncEventData.frameInterval/3;
}
}; // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 7b12efc..6ea7ef7 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -21,13 +21,13 @@
#include <utils/RefBase.h>
#include <gui/IProducerListener.h>
#include <gui/Surface.h>
+#include <gui/DisplayEventReceiver.h>
#include "utils/LatencyHistogram.h"
#include "Camera3Stream.h"
#include "Camera3IOStreamBase.h"
#include "Camera3OutputStreamInterface.h"
#include "Camera3BufferManager.h"
-#include "PreviewFrameScheduler.h"
namespace android {
@@ -240,12 +240,16 @@
virtual status_t setBatchSize(size_t batchSize = 1) override;
/**
+ * Notify the stream on change of min frame durations.
+ */
+ virtual void onMinDurationChanged(nsecs_t duration) override;
+
+ /**
* Apply ZSL related consumer usage quirk.
*/
static void applyZSLUsageQuirk(int format, uint64_t *consumerUsage /*inout*/);
void setImageDumpMask(int mask) { mImageDumpMask = mask; }
- bool shouldLogError(status_t res);
protected:
Camera3OutputStream(int id, camera_stream_type_t type,
@@ -278,7 +282,7 @@
status_t getEndpointUsageForSurface(uint64_t *usage,
const sp<Surface>& surface) const;
- status_t configureConsumerQueueLocked(bool allowPreviewScheduler);
+ status_t configureConsumerQueueLocked(bool allowDisplaySync);
// Consumer as the output of camera HAL
sp<Surface> mConsumer;
@@ -392,13 +396,24 @@
void returnPrefetchedBuffersLocked();
+ // Synchronize camera timestamp to display, and the return value
+ // can be used as presentation timestamp
+ nsecs_t syncTimestampToDisplayLocked(nsecs_t t);
+
static const int32_t kDequeueLatencyBinSize = 5; // in ms
CameraLatencyHistogram mDequeueBufferLatency;
int mImageDumpMask = 0;
- // The preview stream scheduler for re-timing frames
- std::unique_ptr<PreviewFrameScheduler> mPreviewFrameScheduler;
+ nsecs_t mMinExpectedDuration = 0;
+ bool mSyncToDisplay = false;
+ DisplayEventReceiver mDisplayEventReceiver;
+ nsecs_t mLastCaptureTime = 0;
+ nsecs_t mLastPresentTime = 0;
+ nsecs_t mCaptureToPresentOffset = 0;
+ static constexpr size_t kDisplaySyncExtraBuffer = 2;
+ static constexpr nsecs_t kSpacingResetIntervalNs = 1000000000LL; // 1 second
+ static constexpr nsecs_t kTimelineThresholdNs = 1000000LL; // 1 millisecond
}; // class Camera3OutputStream
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
index e44e795..a6d4b96 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStreamInterface.h
@@ -108,6 +108,14 @@
* instead.
*/
virtual status_t setBatchSize(size_t batchSize = 1) = 0;
+
+ /**
+ * Notify the output stream that the minimum frame duration has changed.
+ *
+ * The minimum frame duration is calculated based on the upper bound of
+ * AE_TARGET_FPS_RANGE in the capture request.
+ */
+ virtual void onMinDurationChanged(nsecs_t duration) = 0;
};
// Helper class to organize a synchronized mapping of stream IDs to stream instances
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index ab25322..ed66df0 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -853,6 +853,13 @@
r.shutterTimestamp = msg.timestamp;
r.shutterReadoutTimestamp = msg.readout_timestamp;
+ if (r.minExpectedDuration != states.minFrameDuration) {
+ for (size_t i = 0; i < states.outputStreams.size(); i++) {
+ auto outputStream = states.outputStreams[i];
+ outputStream->onMinDurationChanged(r.minExpectedDuration);
+ }
+ states.minFrameDuration = r.minExpectedDuration;
+ }
if (r.hasCallback) {
ALOGVV("Camera %s: %s: Shutter fired for frame %d (id %d) at %" PRId64,
states.cameraId.string(), __FUNCTION__,
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.h b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
index 4d1eb75..dd01408 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.h
@@ -107,6 +107,7 @@
InflightRequestUpdateInterface& inflightIntf;
BufferRecordsInterface& bufferRecordsIntf;
bool legacyClient;
+ nsecs_t& minFrameDuration;
};
void processCaptureResult(CaptureOutputStates& states, const camera_capture_result *result);
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index c09a0b2..d24b527 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -251,7 +251,7 @@
return res;
}
- res = configureConsumerQueueLocked(false/*allowPreviewScheduler*/);
+ res = configureConsumerQueueLocked(false/*allowDisplaySync*/);
if (res != OK) {
ALOGE("Failed to configureConsumerQueueLocked: %s(%d)", strerror(-res), res);
return res;
diff --git a/services/camera/libcameraservice/device3/InFlightRequest.h b/services/camera/libcameraservice/device3/InFlightRequest.h
index 0c97f3e..493a9e2 100644
--- a/services/camera/libcameraservice/device3/InFlightRequest.h
+++ b/services/camera/libcameraservice/device3/InFlightRequest.h
@@ -143,6 +143,11 @@
// is not for constrained high speed recording, this flag will also be true.
bool hasCallback;
+ // Minimum expected frame duration for this request
+ // For manual captures, equal to the max of requested exposure time and frame duration
+ // For auto-exposure modes, equal to 1/(higher end of target FPS range)
+ nsecs_t minExpectedDuration;
+
// Maximum expected frame duration for this request.
// For manual captures, equal to the max of requested exposure time and frame duration
// For auto-exposure modes, equal to 1/(lower end of target FPS range)
@@ -187,8 +192,8 @@
// Current output transformation
int32_t transform;
- // TODO: dedupe
- static const nsecs_t kDefaultExpectedDuration = 100000000; // 100 ms
+ static const nsecs_t kDefaultMinExpectedDuration = 33333333; // 33 ms
+ static const nsecs_t kDefaultMaxExpectedDuration = 100000000; // 100 ms
// Default constructor needed by KeyedVector
InFlightRequest() :
@@ -199,7 +204,8 @@
numBuffersLeft(0),
hasInputBuffer(false),
hasCallback(true),
- maxExpectedDuration(kDefaultExpectedDuration),
+ minExpectedDuration(kDefaultMinExpectedDuration),
+ maxExpectedDuration(kDefaultMaxExpectedDuration),
skipResultMetadata(false),
errorBufStrategy(ERROR_BUF_CACHE),
stillCapture(false),
@@ -210,7 +216,7 @@
}
InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
- bool hasAppCallback, nsecs_t maxDuration,
+ bool hasAppCallback, nsecs_t minDuration, nsecs_t maxDuration,
const std::set<std::set<String8>>& physicalCameraIdSet, bool isStillCapture,
bool isZslCapture, bool rotateAndCropAuto, const std::set<std::string>& idsWithZoom,
nsecs_t requestNs, const SurfaceMap& outSurfaces = SurfaceMap{}) :
@@ -222,6 +228,7 @@
resultExtras(extras),
hasInputBuffer(hasInput),
hasCallback(hasAppCallback),
+ minExpectedDuration(minDuration),
maxExpectedDuration(maxDuration),
skipResultMetadata(false),
errorBufStrategy(ERROR_BUF_CACHE),
diff --git a/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp b/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
deleted file mode 100644
index 80f27ed..0000000
--- a/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Camera3-PreviewFrameScheduler"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
-
-#include <utils/Log.h>
-#include <utils/Trace.h>
-
-#include <android/looper.h>
-#include "PreviewFrameScheduler.h"
-#include "Camera3OutputStream.h"
-
-namespace android {
-
-namespace camera3 {
-
-/**
- * Internal Choreographer thread implementation for polling and handling callbacks
- */
-
-// Callback function for Choreographer
-static void frameCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
- PreviewFrameScheduler* parent = static_cast<PreviewFrameScheduler*>(data);
- if (parent == nullptr) {
- ALOGE("%s: Invalid data for Choreographer callback!", __FUNCTION__);
- return;
- }
-
- size_t length = AChoreographerFrameCallbackData_getFrameTimelinesLength(callbackData);
- std::vector<nsecs_t> timeline(length);
- for (size_t i = 0; i < length; i++) {
- nsecs_t timestamp = AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
- callbackData, i);
- timeline[i] = timestamp;
- }
-
- parent->onNewPresentationTime(timeline);
-
- AChoreographer_postVsyncCallback(AChoreographer_getInstance(), frameCallback, data);
-}
-
-struct ChoreographerThread : public Thread {
- ChoreographerThread();
- status_t start(PreviewFrameScheduler* parent);
- virtual status_t readyToRun() override;
- virtual bool threadLoop() override;
-
-protected:
- virtual ~ChoreographerThread() {}
-
-private:
- ChoreographerThread &operator=(const ChoreographerThread &);
-
- // This only impacts the shutdown time. It won't impact the choreographer
- // callback frequency.
- static constexpr nsecs_t kPollingTimeoutMs = 5;
- PreviewFrameScheduler* mParent = nullptr;
-};
-
-ChoreographerThread::ChoreographerThread() : Thread(false /*canCallJava*/) {
-}
-
-status_t ChoreographerThread::start(PreviewFrameScheduler* parent) {
- mParent = parent;
- return run("PreviewChoreographer");
-}
-
-status_t ChoreographerThread::readyToRun() {
- ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
- if (AChoreographer_getInstance() == NULL) {
- return NO_INIT;
- }
-
- AChoreographer_postVsyncCallback(
- AChoreographer_getInstance(), frameCallback, mParent);
- return OK;
-}
-
-bool ChoreographerThread::threadLoop() {
- if (exitPending()) {
- return false;
- }
- ALooper_pollOnce(kPollingTimeoutMs, nullptr, nullptr, nullptr);
- return true;
-}
-
-/**
- * PreviewFrameScheduler implementation
- */
-
-PreviewFrameScheduler::PreviewFrameScheduler(Camera3OutputStream& parent, sp<Surface> consumer) :
- mParent(parent),
- mConsumer(consumer),
- mChoreographerThread(new ChoreographerThread()) {
-}
-
-PreviewFrameScheduler::~PreviewFrameScheduler() {
- {
- Mutex::Autolock l(mLock);
- mChoreographerThread->requestExit();
- }
- mChoreographerThread->join();
-}
-
-status_t PreviewFrameScheduler::queuePreviewBuffer(nsecs_t timestamp, int32_t transform,
- ANativeWindowBuffer* anwBuffer, int releaseFence) {
- // Start choreographer thread if it's not already running.
- if (!mChoreographerThread->isRunning()) {
- status_t res = mChoreographerThread->start(this);
- if (res != OK) {
- ALOGE("%s: Failed to init choreographer thread!", __FUNCTION__);
- return res;
- }
- }
-
- {
- Mutex::Autolock l(mLock);
- mPendingBuffers.emplace(timestamp, transform, anwBuffer, releaseFence);
-
- // Queue buffer to client right away if pending buffers are more than
- // the queue depth watermark.
- if (mPendingBuffers.size() > kQueueDepthWatermark) {
- auto oldBuffer = mPendingBuffers.front();
- mPendingBuffers.pop();
-
- status_t res = queueBufferToClientLocked(oldBuffer, oldBuffer.timestamp);
- if (res != OK) {
- return res;
- }
-
- // Reset the last capture and presentation time
- mLastCameraCaptureTime = 0;
- mLastCameraPresentTime = 0;
- } else {
- ATRACE_INT(kPendingBufferTraceName, mPendingBuffers.size());
- }
- }
- return OK;
-}
-
-void PreviewFrameScheduler::onNewPresentationTime(const std::vector<nsecs_t>& timeline) {
- ATRACE_CALL();
- Mutex::Autolock l(mLock);
- if (mPendingBuffers.size() > 0) {
- auto nextBuffer = mPendingBuffers.front();
- mPendingBuffers.pop();
-
- // Find the best presentation time by finding the element in the
- // choreographer timeline that's closest to the ideal presentation time.
- // The ideal presentation time is the last presentation time + frame
- // interval.
- nsecs_t cameraInterval = nextBuffer.timestamp - mLastCameraCaptureTime;
- nsecs_t idealPresentTime = (cameraInterval < kSpacingResetIntervalNs) ?
- (mLastCameraPresentTime + cameraInterval) : nextBuffer.timestamp;
- nsecs_t presentTime = *std::min_element(timeline.begin(), timeline.end(),
- [idealPresentTime](nsecs_t p1, nsecs_t p2) {
- return std::abs(p1 - idealPresentTime) < std::abs(p2 - idealPresentTime);
- });
-
- status_t res = queueBufferToClientLocked(nextBuffer, presentTime);
- ATRACE_INT(kPendingBufferTraceName, mPendingBuffers.size());
-
- if (mParent.shouldLogError(res)) {
- ALOGE("%s: Preview Stream: Error queueing buffer to native window:"
- " %s (%d)", __FUNCTION__, strerror(-res), res);
- }
-
- mLastCameraCaptureTime = nextBuffer.timestamp;
- mLastCameraPresentTime = presentTime;
- }
-}
-
-status_t PreviewFrameScheduler::queueBufferToClientLocked(
- const BufferHolder& bufferHolder, nsecs_t timestamp) {
- mParent.setTransform(bufferHolder.transform, true/*mayChangeMirror*/);
-
- status_t res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp);
- if (res != OK) {
- ALOGE("%s: Preview Stream: Error setting timestamp: %s (%d)",
- __FUNCTION__, strerror(-res), res);
- return res;
- }
-
- Camera3Stream::queueHDRMetadata(bufferHolder.anwBuffer.get()->handle, mConsumer,
- mParent.getDynamicRangeProfile());
-
- res = mConsumer->queueBuffer(mConsumer.get(), bufferHolder.anwBuffer.get(),
- bufferHolder.releaseFence);
- if (res != OK) {
- close(bufferHolder.releaseFence);
- }
-
- return res;
-}
-
-}; // namespace camera3
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/device3/PreviewFrameScheduler.h b/services/camera/libcameraservice/device3/PreviewFrameScheduler.h
deleted file mode 100644
index c0574fd..0000000
--- a/services/camera/libcameraservice/device3/PreviewFrameScheduler.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SERVERS_CAMERA_CAMERA3_PREVIEWFRAMESCHEDULER_H
-#define ANDROID_SERVERS_CAMERA_CAMERA3_PREVIEWFRAMESCHEDULER_H
-
-#include <queue>
-
-#include <android/choreographer.h>
-#include <gui/Surface.h>
-#include <gui/ISurfaceComposer.h>
-#include <utils/Condition.h>
-#include <utils/Mutex.h>
-#include <utils/Looper.h>
-#include <utils/Thread.h>
-#include <utils/Timers.h>
-
-namespace android {
-
-namespace camera3 {
-
-class Camera3OutputStream;
-struct ChoreographerThread;
-
-/***
- * Preview stream scheduler for better preview display synchronization
- *
- * The ideal viewfinder user experience is that frames are presented to the
- * user in the same cadence as outputed by the camera sensor. However, the
- * processing latency between frames could vary, due to factors such
- * as CPU load, differences in request settings, etc. This frame processing
- * latency results in variation in presentation of frames to the user.
- *
- * The PreviewFrameScheduler improves the viewfinder user experience by:
- * 1. Cache preview buffers in the scheduler
- * 2. For each choreographer callback, queue the oldest cached buffer with
- * the best matching presentation timestamp. Frame N's presentation timestamp
- * is the choreographer timeline timestamp closest to (Frame N-1's
- * presentation time + camera capture interval between frame N-1 and frame N).
- * 3. Maintain at most 2 queue-able buffers. If the 3rd preview buffer becomes
- * available, queue the oldest cached buffer to the buffer queue.
- */
-class PreviewFrameScheduler {
- public:
- explicit PreviewFrameScheduler(Camera3OutputStream& parent, sp<Surface> consumer);
- virtual ~PreviewFrameScheduler();
-
- // Queue preview buffer locally
- status_t queuePreviewBuffer(nsecs_t timestamp, int32_t transform,
- ANativeWindowBuffer* anwBuffer, int releaseFence);
-
- // Callback function with a new presentation timeline from choreographer. This
- // will trigger a locally queued buffer be sent to the buffer queue.
- void onNewPresentationTime(const std::vector<nsecs_t>& presentationTimeline);
-
- // Maintain at most 2 queue-able buffers
- static constexpr int32_t kQueueDepthWatermark = 2;
-
- private:
- // structure holding cached preview buffer info
- struct BufferHolder {
- nsecs_t timestamp;
- int32_t transform;
- sp<ANativeWindowBuffer> anwBuffer;
- int releaseFence;
-
- BufferHolder(nsecs_t t, int32_t tr, ANativeWindowBuffer* anwb, int rf) :
- timestamp(t), transform(tr), anwBuffer(anwb), releaseFence(rf) {}
- };
-
- status_t queueBufferToClientLocked(const BufferHolder& bufferHolder,
- nsecs_t presentTime);
-
- static constexpr char kPendingBufferTraceName[] = "pending_preview_buffers";
-
- // Camera capture interval for resetting frame spacing between preview sessions
- static constexpr nsecs_t kSpacingResetIntervalNs = 1000000000L; // 1 second
-
- Camera3OutputStream& mParent;
- sp<ANativeWindow> mConsumer;
- mutable Mutex mLock;
-
- std::queue<BufferHolder> mPendingBuffers;
- nsecs_t mLastCameraCaptureTime = 0;
- nsecs_t mLastCameraPresentTime = 0;
-
- // Choreographer related
- sp<Looper> mLooper;
- sp<ChoreographerThread> mChoreographerThread;
-};
-
-}; //namespace camera3
-}; //namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
index 1add032..f05520f 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3Device.cpp
@@ -371,7 +371,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this,
- *this, *(mInterface), mLegacyClient}, mResultMetadataQueue
+ *this, *(mInterface), mLegacyClient, mMinExpectedDuration}, mResultMetadataQueue
};
for (const auto& result : results) {
@@ -412,7 +412,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this,
- *this, *(mInterface), mLegacyClient}, mResultMetadataQueue
+ *this, *(mInterface), mLegacyClient, mMinExpectedDuration}, mResultMetadataQueue
};
for (const auto& msg : msgs) {
camera3::notify(states, msg);
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.cpp b/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.cpp
index 895ce56..336719d 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.cpp
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.cpp
@@ -123,7 +123,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this,
- *this, mBufferRecords, /*legacyClient*/ false}, mResultMetadataQueue
+ *this, mBufferRecords, /*legacyClient*/ false, mMinExpectedDuration}, mResultMetadataQueue
};
std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
@@ -168,7 +168,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this,
- *this, mBufferRecords, /*legacyClient*/ false}, mResultMetadataQueue
+ *this, mBufferRecords, /*legacyClient*/ false, mMinExpectedDuration}, mResultMetadataQueue
};
for (const auto& msg : msgs) {
camera3::notify(states, msg);
diff --git a/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.h b/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.h
index ad4a480..33de2c5 100644
--- a/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.h
+++ b/services/camera/libcameraservice/device3/aidl/AidlCamera3OfflineSession.h
@@ -75,7 +75,7 @@
// See explanation for why we need a separate class for this in
// AidlCamera3Device::AidlCameraDeviceCallbacks in AidlCamera3Device.h
class AidlCameraDeviceCallbacks :
- virtual public aidl::android::hardware::camera::device::BnCameraDeviceCallback {
+ public aidl::android::hardware::camera::device::BnCameraDeviceCallback {
public:
AidlCameraDeviceCallbacks(wp<AidlCamera3OfflineSession> parent) : mParent(parent) { }
@@ -112,7 +112,9 @@
offlineSession) :
Camera3OfflineSession(id, inputStream, offlineStreamSet, std::move(bufferRecords),
offlineReqs, offlineStates),
- mSession(offlineSession) { mCallbacks = std::make_shared<AidlCameraDeviceCallbacks>(this);};
+ mSession(offlineSession) {
+ mCallbacks = ndk::SharedRefBase::make<AidlCameraDeviceCallbacks>(this);
+ };
/**
* End of CameraOfflineSessionBase interface
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp
index 87d3ee8..cf6d462 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3Device.cpp
@@ -369,7 +369,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
- *mInterface, mLegacyClient}, mResultMetadataQueue
+ *mInterface, mLegacyClient, mMinExpectedDuration}, mResultMetadataQueue
};
//HidlCaptureOutputStates hidlStates {
@@ -431,7 +431,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
- *mInterface, mLegacyClient}, mResultMetadataQueue
+ *mInterface, mLegacyClient, mMinExpectedDuration}, mResultMetadataQueue
};
for (const auto& result : results) {
@@ -483,7 +483,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
- *mInterface, mLegacyClient}, mResultMetadataQueue
+ *mInterface, mLegacyClient, mMinExpectedDuration}, mResultMetadataQueue
};
for (const auto& msg : msgs) {
camera3::notify(states, msg);
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3OfflineSession.cpp b/services/camera/libcameraservice/device3/hidl/HidlCamera3OfflineSession.cpp
index d517c8d..5c97f0e 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3OfflineSession.cpp
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3OfflineSession.cpp
@@ -105,7 +105,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
- mBufferRecords, /*legacyClient*/ false}, mResultMetadataQueue
+ mBufferRecords, /*legacyClient*/ false, mMinExpectedDuration}, mResultMetadataQueue
};
std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
@@ -145,7 +145,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
- mBufferRecords, /*legacyClient*/ false}, mResultMetadataQueue
+ mBufferRecords, /*legacyClient*/ false, mMinExpectedDuration}, mResultMetadataQueue
};
std::lock_guard<std::mutex> lock(mProcessCaptureResultLock);
@@ -180,7 +180,7 @@
mNumPartialResults, mVendorTagId, mDeviceInfo, mPhysicalDeviceInfoMap,
mDistortionMappers, mZoomRatioMappers, mRotateAndCropMappers,
mTagMonitor, mInputStream, mOutputStreams, mSessionStatsBuilder, listener, *this, *this,
- mBufferRecords, /*legacyClient*/ false}, mResultMetadataQueue
+ mBufferRecords, /*legacyClient*/ false, mMinExpectedDuration}, mResultMetadataQueue
};
for (const auto& msg : msgs) {
camera3::notify(states, msg);
diff --git a/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp b/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp
index 1563dcf..60e4e42 100644
--- a/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/hidl/HidlCamera3OutputUtils.cpp
@@ -219,6 +219,7 @@
tmpBuffers[i].buffer = convertToHidl(aBuf.buffer, handlesCreated);
tmpBuffers[i].acquireFence = convertToHidl(aBuf.acquireFence, handlesCreated);
tmpBuffers[i].releaseFence = convertToHidl(aBuf.releaseFence, handlesCreated);
+ i++;
}
hBuffersVal.buffers(std::move(tmpBuffers));
}
diff --git a/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp b/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp
deleted file mode 100644
index 025521a..0000000
--- a/services/camera/libcameraservice/tests/PreviewSchedulerTest.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "PreviewSchedulerTest"
-
-#include <chrono>
-#include <thread>
-#include <utility>
-
-#include <gtest/gtest.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/Mutex.h>
-
-#include <gui/BufferItemConsumer.h>
-#include <gui/BufferQueue.h>
-#include <gui/IGraphicBufferProducer.h>
-#include <gui/IGraphicBufferConsumer.h>
-#include <gui/Surface.h>
-
-#include "../device3/Camera3OutputStream.h"
-#include "../device3/PreviewFrameScheduler.h"
-
-using namespace android;
-using namespace android::camera3;
-
-// Consumer buffer available listener
-class SimpleListener : public BufferItemConsumer::FrameAvailableListener {
-public:
- SimpleListener(size_t frameCount): mFrameCount(frameCount) {}
-
- void waitForFrames() {
- Mutex::Autolock lock(mMutex);
- while (mFrameCount > 0) {
- mCondition.wait(mMutex);
- }
- }
-
- void onFrameAvailable(const BufferItem& /*item*/) override {
- Mutex::Autolock lock(mMutex);
- if (mFrameCount > 0) {
- mFrameCount--;
- mCondition.signal();
- }
- }
-
- void reset(size_t frameCount) {
- Mutex::Autolock lock(mMutex);
- mFrameCount = frameCount;
- }
-private:
- size_t mFrameCount;
- Mutex mMutex;
- Condition mCondition;
-};
-
-// Test the PreviewFrameScheduler functionatliy of re-timing buffers
-TEST(PreviewSchedulerTest, BasicPreviewSchedulerTest) {
- const int ID = 0;
- const int FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
- const uint32_t WIDTH = 640;
- const uint32_t HEIGHT = 480;
- const int32_t TRANSFORM = 0;
- const nsecs_t T_OFFSET = 0;
- const android_dataspace DATASPACE = HAL_DATASPACE_UNKNOWN;
- const camera_stream_rotation_t ROTATION = CAMERA_STREAM_ROTATION_0;
- const String8 PHY_ID;
- const std::unordered_set<int32_t> PIX_MODES;
- const int BUFFER_COUNT = 4;
- const int TOTAL_BUFFER_COUNT = BUFFER_COUNT * 2;
-
- // Create buffer queue
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferConsumer> consumer;
- BufferQueue::createBufferQueue(&producer, &consumer);
- ASSERT_NE(producer, nullptr);
- ASSERT_NE(consumer, nullptr);
- ASSERT_EQ(NO_ERROR, consumer->setDefaultBufferSize(WIDTH, HEIGHT));
-
- // Set up consumer
- sp<BufferItemConsumer> bufferConsumer = new BufferItemConsumer(consumer,
- GRALLOC_USAGE_HW_COMPOSER, BUFFER_COUNT);
- ASSERT_NE(bufferConsumer, nullptr);
- sp<SimpleListener> consumerListener = new SimpleListener(BUFFER_COUNT);
- bufferConsumer->setFrameAvailableListener(consumerListener);
-
- // Set up producer
- sp<Surface> surface = new Surface(producer);
- sp<StubProducerListener> listener = new StubProducerListener();
- ASSERT_EQ(NO_ERROR, surface->connect(NATIVE_WINDOW_API_CPU, listener));
- sp<ANativeWindow> anw(surface);
- ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), TOTAL_BUFFER_COUNT));
-
- // Create Camera3OutputStream and PreviewFrameScheduler
- sp<Camera3OutputStream> stream = new Camera3OutputStream(ID, surface, WIDTH, HEIGHT,
- FORMAT, DATASPACE, ROTATION, T_OFFSET, PHY_ID, PIX_MODES);
- ASSERT_NE(stream, nullptr);
- std::unique_ptr<PreviewFrameScheduler> scheduler =
- std::make_unique<PreviewFrameScheduler>(*stream, surface);
- ASSERT_NE(scheduler, nullptr);
-
- // The pair of nsecs_t: camera timestamp delta (negative means in the past) and frame interval
- const std::pair<nsecs_t, nsecs_t> inputTimestamps[][BUFFER_COUNT] = {
- // 30fps, 33ms interval
- {{-100000000LL, 33333333LL}, {-66666667LL, 33333333LL},
- {-33333333LL, 33333333LL}, {0, 0}},
- // 30fps, variable interval
- {{-100000000LL, 16666667LL}, {-66666667LL, 33333333LL},
- {-33333333LL, 50000000LL}, {0, 0}},
- // 60fps, 16.7ms interval
- {{-50000000LL, 16666667LL}, {-33333333LL, 16666667LL},
- {-16666667LL, 16666667LL}, {0, 0}},
- // 60fps, variable interval
- {{-50000000LL, 8666667LL}, {-33333333LL, 19666667LL},
- {-16666667LL, 20666667LL}, {0, 0}},
- };
-
- // Go through different use cases, and check the buffer timestamp
- size_t iterations = sizeof(inputTimestamps)/sizeof(inputTimestamps[0]);
- for (size_t i = 0; i < iterations; i++) {
- // Space out different test sets to reset the frame scheduler
- nsecs_t timeBase = systemTime() - s2ns(1) * (iterations - i);
- nsecs_t lastQueueTime = 0;
- nsecs_t duration = 0;
- for (size_t j = 0; j < BUFFER_COUNT; j++) {
- ANativeWindowBuffer* buffer = nullptr;
- int fenceFd;
- ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buffer, &fenceFd));
-
- // Sleep to space out queuePreviewBuffer
- nsecs_t currentTime = systemTime();
- if (duration > 0 && duration > currentTime - lastQueueTime) {
- std::this_thread::sleep_for(
- std::chrono::nanoseconds(duration + lastQueueTime - currentTime));
- }
- nsecs_t timestamp = timeBase + inputTimestamps[i][j].first;
- ASSERT_EQ(NO_ERROR,
- scheduler->queuePreviewBuffer(timestamp, TRANSFORM, buffer, fenceFd));
-
- lastQueueTime = systemTime();
- duration = inputTimestamps[i][j].second;
- }
-
- // Collect output timestamps, making sure they are either set by
- // producer, or set by the scheduler.
- consumerListener->waitForFrames();
- nsecs_t outputTimestamps[BUFFER_COUNT];
- for (size_t j = 0; j < BUFFER_COUNT; j++) {
- BufferItem bufferItem;
- ASSERT_EQ(NO_ERROR, bufferConsumer->acquireBuffer(&bufferItem, 0/*presentWhen*/));
-
- outputTimestamps[j] = bufferItem.mTimestamp;
- ALOGV("%s: [%zu][%zu]: input: %" PRId64 ", output: %" PRId64, __FUNCTION__,
- i, j, timeBase + inputTimestamps[i][j].first, bufferItem.mTimestamp);
- ASSERT_GT(bufferItem.mTimestamp, inputTimestamps[i][j].first);
-
- ASSERT_EQ(NO_ERROR, bufferConsumer->releaseBuffer(bufferItem));
- }
-
- // Check the output timestamp intervals are aligned with input intervals
- const nsecs_t SHIFT_THRESHOLD = ms2ns(2);
- for (size_t j = 0; j < BUFFER_COUNT - 1; j ++) {
- nsecs_t interval_shift = outputTimestamps[j+1] - outputTimestamps[j] -
- (inputTimestamps[i][j+1].first - inputTimestamps[i][j].first);
- ASSERT_LE(std::abs(interval_shift), SHIFT_THRESHOLD);
- }
-
- consumerListener->reset(BUFFER_COUNT);
- }
-
- // Disconnect the surface
- ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU));
-}
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
index 82d58e0..a00b221 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.cpp
@@ -80,6 +80,7 @@
void CameraServiceProxyWrapper::CameraSessionStatsWrapper::onIdle(
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::string& userTag,
const std::vector<hardware::CameraStreamStats>& streamStats) {
Mutex::Autolock l(mLock);
@@ -87,6 +88,7 @@
mSessionStats.mRequestCount = requestCount;
mSessionStats.mResultErrorCount = resultErrorCount;
mSessionStats.mDeviceError = deviceError;
+ mSessionStats.mUserTag = String16(userTag.c_str());
mSessionStats.mStreamStats = streamStats;
updateProxyDeviceState(mSessionStats);
@@ -177,6 +179,7 @@
void CameraServiceProxyWrapper::logIdle(const String8& id,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::string& userTag,
const std::vector<hardware::CameraStreamStats>& streamStats) {
std::shared_ptr<CameraSessionStatsWrapper> sessionStats;
{
@@ -190,8 +193,9 @@
return;
}
- ALOGV("%s: id %s, requestCount %" PRId64 ", resultErrorCount %" PRId64 ", deviceError %d",
- __FUNCTION__, id.c_str(), requestCount, resultErrorCount, deviceError);
+ ALOGV("%s: id %s, requestCount %" PRId64 ", resultErrorCount %" PRId64 ", deviceError %d"
+ ", userTag %s", __FUNCTION__, id.c_str(), requestCount, resultErrorCount,
+ deviceError, userTag.c_str());
for (size_t i = 0; i < streamStats.size(); i++) {
ALOGV("%s: streamStats[%zu]: w %d h %d, requestedCount %" PRId64 ", dropCount %"
PRId64 ", startTimeMs %d" ,
@@ -200,7 +204,7 @@
streamStats[i].mStartLatencyMs);
}
- sessionStats->onIdle(requestCount, resultErrorCount, deviceError, streamStats);
+ sessionStats->onIdle(requestCount, resultErrorCount, deviceError, userTag, streamStats);
}
void CameraServiceProxyWrapper::logOpen(const String8& id, int facing,
diff --git a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
index 037316d..6604aa1 100644
--- a/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
+++ b/services/camera/libcameraservice/utils/CameraServiceProxyWrapper.h
@@ -50,6 +50,7 @@
void onStreamConfigured(int operatingMode, bool internalReconfig, int32_t latencyMs);
void onActive(float maxPreviewFps);
void onIdle(int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::string& userTag,
const std::vector<hardware::CameraStreamStats>& streamStats);
};
@@ -86,6 +87,7 @@
// Session state becomes idle
static void logIdle(const String8& id,
int64_t requestCount, int64_t resultErrorCount, bool deviceError,
+ const std::string& userTag,
const std::vector<hardware::CameraStreamStats>& streamStats);
// Ping camera service proxy for user update
diff --git a/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp b/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp
index 7a7707c..c3aac72 100644
--- a/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp
+++ b/services/camera/libcameraservice/utils/SessionStatsBuilder.cpp
@@ -60,6 +60,7 @@
mErrorResultCount = 0;
mCounterStopped = false;
mDeviceError = false;
+ mUserTag.clear();
for (auto& streamStats : mStatsMap) {
StreamStats& streamStat = streamStats.second;
streamStat.mRequestedFrameCount = 0;
diff --git a/services/camera/libcameraservice/utils/SessionStatsBuilder.h b/services/camera/libcameraservice/utils/SessionStatsBuilder.h
index c23abb6..2936531 100644
--- a/services/camera/libcameraservice/utils/SessionStatsBuilder.h
+++ b/services/camera/libcameraservice/utils/SessionStatsBuilder.h
@@ -84,6 +84,7 @@
int64_t mErrorResultCount;
bool mCounterStopped;
bool mDeviceError;
+ std::string mUserTag;
// Map from stream id to stream statistics
std::map<int, StreamStats> mStatsMap;
};
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index aacc2be..99e3691 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -297,33 +297,35 @@
ALOGD("%s", __func__);
// Add action to save AnalyticsState if audioserver is restarted.
- // This triggers on an item of "audio.flinger"
- // with a property "event" set to "AudioFlinger" (the constructor).
+ // This triggers on AudioFlinger or AudioPolicy ctors and onFirstRef,
+ // as well as TimeCheck events.
mActions.addAction(
AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
- ALOGW("(key=%s) Audioflinger constructor event detected", item->getKey().c_str());
- mPreviousAnalyticsState.set(std::make_shared<AnalyticsState>(
- *mAnalyticsState.get()));
- // Note: get returns shared_ptr temp, whose lifetime is extended
- // to end of full expression.
- mAnalyticsState->clear(); // TODO: filter the analytics state.
- // Perhaps report this.
-
- // Set up a timer to expire the previous audio state to save space.
- // Use the transaction log size as a cookie to see if it is the
- // same as before. A benign race is possible where a state is cleared early.
- const size_t size = mPreviousAnalyticsState->transactionLog().size();
- mTimedAction.postIn(
- std::chrono::seconds(PREVIOUS_STATE_EXPIRE_SEC), [this, size](){
- if (mPreviousAnalyticsState->transactionLog().size() == size) {
- ALOGD("expiring previous audio state after %d seconds.",
- PREVIOUS_STATE_EXPIRE_SEC);
- mPreviousAnalyticsState->clear(); // removes data from the state.
- }
- });
+ mHealth.onAudioServerStart(Health::Module::AUDIOFLINGER, item);
+ }));
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_AUDIO_POLICY "." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mHealth.onAudioServerStart(Health::Module::AUDIOPOLICY, item);
+ }));
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_TIMEOUT),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mHealth.onAudioServerTimeout(Health::Module::AUDIOFLINGER, item);
+ }));
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_AUDIO_POLICY "." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_TIMEOUT),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mHealth.onAudioServerTimeout(Health::Module::AUDIOPOLICY, item);
}));
// Handle legacy aaudio playback stream statistics
@@ -1390,4 +1392,138 @@
}
}
+// Create new state, typically occurs after an AudioFlinger ctor event.
+void AudioAnalytics::newState()
+{
+ mPreviousAnalyticsState.set(std::make_shared<AnalyticsState>(
+ *mAnalyticsState.get()));
+ // Note: get returns shared_ptr temp, whose lifetime is extended
+ // to end of full expression.
+ mAnalyticsState->clear(); // TODO: filter the analytics state.
+ // Perhaps report this.
+
+ // Set up a timer to expire the previous audio state to save space.
+ // Use the transaction log size as a cookie to see if it is the
+ // same as before. A benign race is possible where a state is cleared early.
+ const size_t size = mPreviousAnalyticsState->transactionLog().size();
+ mTimedAction.postIn(
+ std::chrono::seconds(PREVIOUS_STATE_EXPIRE_SEC), [this, size](){
+ if (mPreviousAnalyticsState->transactionLog().size() == size) {
+ ALOGD("expiring previous audio state after %d seconds.",
+ PREVIOUS_STATE_EXPIRE_SEC);
+ mPreviousAnalyticsState->clear(); // removes data from the state.
+ }
+ });
+}
+
+void AudioAnalytics::Health::onAudioServerStart(Module module,
+ const std::shared_ptr<const android::mediametrics::Item> &item)
+{
+ const auto nowTime = std::chrono::system_clock::now();
+ if (module == Module::AUDIOFLINGER) {
+ {
+ std::lock_guard lg(mLock);
+ // reset state on AudioFlinger construction.
+ // AudioPolicy is created after AudioFlinger.
+ mAudioFlingerCtorTime = nowTime;
+ mSimpleLog.log("AudioFlinger ctor");
+ }
+ mAudioAnalytics.newState();
+ return;
+ }
+ if (module == Module::AUDIOPOLICY) {
+ // A start event occurs when audioserver
+ //
+ // (1) Starts the first time
+ // (2) Restarts because of the TimeCheck watchdog
+ // (3) Restarts not because of the TimeCheck watchdog.
+ int64_t executionTimeNs = 0;
+ (void)item->get(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, &executionTimeNs);
+ const float loadTimeMs = executionTimeNs * 1e-6f;
+ std::lock_guard lg(mLock);
+ const int64_t restarts = mStartCount;
+ if (mStopCount == mStartCount) {
+ mAudioPolicyCtorTime = nowTime;
+ ++mStartCount;
+ if (mStopCount == 0) {
+ // (1) First time initialization.
+ ALOGW("%s: (key=%s) AudioPolicy ctor, loadTimeMs:%f",
+ __func__, item->getKey().c_str(), loadTimeMs);
+ mSimpleLog.log("AudioPolicy ctor, loadTimeMs:%f", loadTimeMs);
+ } else {
+ // (2) Previous failure caught due to TimeCheck. We know how long restart takes.
+ const float restartMs =
+ std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
+ mAudioFlingerCtorTime - mStopTime).count();
+ ALOGW("%s: (key=%s) AudioPolicy ctor, "
+ "restarts:%lld restartMs:%f loadTimeMs:%f",
+ __func__, item->getKey().c_str(),
+ (long long)restarts, restartMs, loadTimeMs);
+ mSimpleLog.log("AudioPolicy ctor restarts:%lld restartMs:%f loadTimeMs:%f",
+ (long long)restarts, restartMs, loadTimeMs);
+ }
+ } else {
+ // (3) Previous failure is NOT due to TimeCheck, so we don't know the restart time.
+ // However we can estimate the uptime from the delta time from previous ctor.
+ const float uptimeMs =
+ std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
+ nowTime - mAudioFlingerCtorTime).count();
+ mStopCount = mStartCount;
+ mAudioPolicyCtorTime = nowTime;
+ ++mStartCount;
+
+ ALOGW("%s: (key=%s) AudioPolicy ctor after uncaught failure, "
+ "mStartCount:%lld mStopCount:%lld uptimeMs:%f loadTimeMs:%f",
+ __func__, item->getKey().c_str(),
+ (long long)mStartCount, (long long)mStopCount, uptimeMs, loadTimeMs);
+ mSimpleLog.log("AudioPolicy ctor after uncaught failure, "
+ "restarts:%lld uptimeMs:%f loadTimeMs:%f",
+ (long long)restarts, uptimeMs, loadTimeMs);
+ }
+ }
+}
+
+void AudioAnalytics::Health::onAudioServerTimeout(Module module,
+ const std::shared_ptr<const android::mediametrics::Item> &item)
+{
+ std::string moduleName = getModuleName(module);
+ int64_t methodCode{};
+ std::string methodName;
+ (void)item->get(AMEDIAMETRICS_PROP_METHODCODE, &methodCode);
+ (void)item->get(AMEDIAMETRICS_PROP_METHODNAME, &methodName);
+
+ std::lock_guard lg(mLock);
+ if (mStopCount >= mStartCount) {
+ ALOGD("%s: (key=%s) %s timeout %s(%lld) "
+ "unmatched mStopCount(%lld) >= mStartCount(%lld), ignoring",
+ __func__, item->getKey().c_str(), moduleName.c_str(),
+ methodName.c_str(), (long long)methodCode,
+ (long long)mStopCount, (long long)mStartCount);
+ return;
+ }
+
+ const int64_t restarts = mStartCount - 1;
+ ++mStopCount;
+ mStopTime = std::chrono::system_clock::now();
+ const float uptimeMs = std::chrono::duration_cast<std::chrono::duration<float, std::milli>>(
+ mStopTime - mAudioFlingerCtorTime).count();
+ ALOGW("%s: (key=%s) %s timeout %s(%lld) restarts:%lld uptimeMs:%f",
+ __func__, item->getKey().c_str(), moduleName.c_str(),
+ methodName.c_str(), (long long)methodCode,
+ (long long)restarts, uptimeMs);
+ mSimpleLog.log("%s timeout %s(%lld) restarts:%lld uptimeMs:%f",
+ moduleName.c_str(), methodName.c_str(), (long long)methodCode,
+ (long long)restarts, uptimeMs);
+}
+
+std::pair<std::string, int32_t> AudioAnalytics::Health::dump(
+ int32_t lines, const char *prefix) const
+{
+ std::lock_guard lg(mLock);
+ std::string s = mSimpleLog.dumpToString(prefix == nullptr ? "" : prefix, lines);
+ size_t n = std::count(s.begin(), s.end(), '\n');
+ return { s, n };
+}
+
+
} // namespace android::mediametrics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 636b343..ff16b9e 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -327,6 +327,15 @@
result << "-- some lines may be truncated --\n";
}
+ const int32_t healthLinesToDump = all ? INT32_MAX : 15;
+ result << "\nHealth Message Log:";
+ const auto [ healthDumpString, healthLines ] =
+ mAudioAnalytics.dumpHealth(healthLinesToDump);
+ result << "\n" << healthDumpString;
+ if (healthLines == healthLinesToDump) {
+ result << "-- some lines may be truncated --\n";
+ }
+
result << "\nLogSessionId:\n"
<< mediametrics::ValidateId::get()->dump();
diff --git a/services/mediametrics/include/mediametricsservice/AudioAnalytics.h b/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
index a44fcc1..5ee8c30 100644
--- a/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
+++ b/services/mediametrics/include/mediametricsservice/AudioAnalytics.h
@@ -83,6 +83,15 @@
return mHeatMap.dump(lines);
}
+ /**
+ * Returns a pair consisting of the dump string and the number of lines in the string.
+ *
+ * Health dump.
+ */
+ std::pair<std::string, int32_t> dumpHealth(int32_t lines = INT32_MAX) const {
+ return mHealth.dump(lines);
+ }
+
void clear() {
// underlying state is locked.
mPreviousAnalyticsState->clear();
@@ -247,6 +256,67 @@
AudioAnalytics &mAudioAnalytics;
} mAAudioStreamInfo{*this};
+ // Create new state, typically occurs after an AudioFlinger ctor event.
+ void newState();
+
+ // Health is a nested class that tracks audioserver health properties
+ class Health {
+ public:
+ explicit Health(AudioAnalytics &audioAnalytics)
+ : mAudioAnalytics(audioAnalytics) {}
+
+ enum class Module {
+ AUDIOFLINGER,
+ AUDIOPOLICY,
+ };
+
+ const char *getModuleName(Module module) {
+ switch (module) {
+ case Module::AUDIOFLINGER: return "AudioFlinger";
+ case Module::AUDIOPOLICY: return "AudioPolicy";
+ }
+ return "Unknown";
+ }
+
+ // Called when we believe audioserver starts (AudioFlinger ctor)
+ void onAudioServerStart(Module module,
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ // Called when we believe audioserver crashes (TimeCheck timeouts).
+ void onAudioServerTimeout(Module module,
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ std::pair<std::string, int32_t> dump(
+ int32_t lines = INT32_MAX, const char *prefix = nullptr) const;
+
+ private:
+ AudioAnalytics& mAudioAnalytics;
+
+ mutable std::mutex mLock;
+
+ // Life cycle of AudioServer
+ // mAudioFlingerCtorTime
+ // mAudioPolicyCtorTime
+ // mAudioPolicyCtorDoneTime
+ // ...
+ // possibly mStopTime (if TimeCheck thread)
+ //
+ // UpTime is measured from mStopTime - mAudioFlingerCtorTime.
+ //
+ // The stop events come from TimeCheck timeout aborts. There may be other
+ // uncaught signals, e.g. SIGSEGV, that cause missing stop events.
+ std::chrono::system_clock::time_point mAudioFlingerCtorTime GUARDED_BY(mLock);
+ std::chrono::system_clock::time_point mAudioPolicyCtorTime GUARDED_BY(mLock);
+ std::chrono::system_clock::time_point mAudioPolicyCtorDoneTime GUARDED_BY(mLock);
+ std::chrono::system_clock::time_point mStopTime GUARDED_BY(mLock);
+
+ // mStartCount and mStopCount track the audioserver start and stop events.
+ int64_t mStartCount GUARDED_BY(mLock) = 0;
+ int64_t mStopCount GUARDED_BY(mLock) = 0;
+
+ SimpleLog mSimpleLog GUARDED_BY(mLock) {64};
+ } mHealth{*this};
+
AudioPowerUsage mAudioPowerUsage;
};
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 17a3a5f..e322d62 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -231,7 +231,7 @@
sessionId = mediametrics::ValidateId::get()->validateId(sessionId);
metrics_proto.set_log_session_id(sessionId);
}
- AStatsEvent_writeString(event, codec.c_str());
+ AStatsEvent_writeString(event, sessionId.c_str());
int32_t channelCount = -1;
if (item->getInt32("android.media.mediacodec.channelCount", &channelCount)) {
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 57be435..e0584df 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -483,7 +483,7 @@
}
}
if (info.cookie == 0 && client != nullptr) {
- info.cookie = addCookieAndLink_l(client->asBinder(),
+ info.cookie = addCookieAndLink_l(client,
new DeathNotifier(ref<ResourceManagerService>(), pid, clientId));
}
if (mObserverService != nullptr && !resourceAdded.empty()) {
@@ -592,7 +592,7 @@
onLastRemoved(it->second, info);
}
- removeCookieAndUnlink_l(info.client->asBinder(), info.cookie);
+ removeCookieAndUnlink_l(info.client, info.cookie);
if (mObserverService != nullptr && !info.resources.empty()) {
mObserverService->onResourceRemoved(info.uid, pid, info.resources);
@@ -696,11 +696,11 @@
if (clients.size() == 0) {
// if we are here, run the fourth pass to free one codec with the different type.
if (secureCodec != NULL) {
- MediaResource temp(MediaResource::Type::kNonSecureCodec, 1);
+ MediaResource temp(MediaResource::Type::kNonSecureCodec, secureCodec->subType, 1);
getClientForResource_l(callingPid, &temp, &clients);
}
if (nonSecureCodec != NULL) {
- MediaResource temp(MediaResource::Type::kSecureCodec, 1);
+ MediaResource temp(MediaResource::Type::kSecureCodec, nonSecureCodec->subType, 1);
getClientForResource_l(callingPid, &temp, &clients);
}
}
@@ -812,7 +812,7 @@
return Status::fromServiceSpecificError(BAD_VALUE);
}
- uintptr_t cookie = addCookieAndLink_l(client->asBinder(),
+ uintptr_t cookie = addCookieAndLink_l(client,
new OverrideProcessInfoDeathNotifier(ref<ResourceManagerService>(), pid));
mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{cookie, client});
@@ -820,23 +820,29 @@
return Status::ok();
}
-uintptr_t ResourceManagerService::addCookieAndLink_l(::ndk::SpAIBinder binder,
- const sp<DeathNotifier>& notifier) {
+uintptr_t ResourceManagerService::addCookieAndLink_l(
+ const std::shared_ptr<IResourceManagerClient>& client, const sp<DeathNotifier>& notifier) {
+ if (client == nullptr) {
+ return 0;
+ }
std::scoped_lock lock{sCookieLock};
uintptr_t cookie;
// Need to skip cookie 0 (if it wraps around). ResourceInfo has cookie initialized to 0
// indicating the death notifier is not created yet.
while ((cookie = ++sCookieCounter) == 0);
- AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie);
+ AIBinder_linkToDeath(client->asBinder().get(), mDeathRecipient.get(), (void*)cookie);
sCookieToDeathNotifierMap.emplace(cookie, notifier);
return cookie;
}
-void ResourceManagerService::removeCookieAndUnlink_l(::ndk::SpAIBinder binder, uintptr_t cookie) {
+void ResourceManagerService::removeCookieAndUnlink_l(
+ const std::shared_ptr<IResourceManagerClient>& client, uintptr_t cookie) {
std::scoped_lock lock{sCookieLock};
- AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie);
+ if (client != nullptr) {
+ AIBinder_unlinkToDeath(client->asBinder().get(), mDeathRecipient.get(), (void*)cookie);
+ }
sCookieToDeathNotifierMap.erase(cookie);
}
@@ -854,7 +860,7 @@
mProcessInfo->removeProcessInfoOverride(pid);
- removeCookieAndUnlink_l(it->second.client->asBinder(), it->second.cookie);
+ removeCookieAndUnlink_l(it->second.client, it->second.cookie);
mProcessInfoOverrideMap.erase(pid);
}
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 6551371..c636a0f 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -166,8 +166,10 @@
void removeProcessInfoOverride(int pid);
void removeProcessInfoOverride_l(int pid);
- uintptr_t addCookieAndLink_l(::ndk::SpAIBinder binder, const sp<DeathNotifier>& notifier);
- void removeCookieAndUnlink_l(::ndk::SpAIBinder binder, uintptr_t cookie);
+ uintptr_t addCookieAndLink_l(const std::shared_ptr<IResourceManagerClient>& client,
+ const sp<DeathNotifier>& notifier);
+ void removeCookieAndUnlink_l(const std::shared_ptr<IResourceManagerClient>& client,
+ uintptr_t cookie);
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
diff --git a/services/tuner/Android.bp b/services/tuner/Android.bp
index ec62d4e..5c1dda1 100644
--- a/services/tuner/Android.bp
+++ b/services/tuner/Android.bp
@@ -92,11 +92,8 @@
"liblog",
"libtunerservice",
"libutils",
- "tv_tuner_resource_manager_aidl_interface-ndk",
- ],
-
- static_libs: [
"tv_tuner_aidl_interface-ndk",
+ "tv_tuner_resource_manager_aidl_interface-ndk",
],
init_rc: ["mediatuner.rc"],