Merge "Add tests for TreeHugger Presubmit." into rvc-dev
diff --git a/camera/CameraMetadata.cpp b/camera/CameraMetadata.cpp
index 4745ee8..135384a 100644
--- a/camera/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -17,9 +17,7 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "Camera2-Metadata"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
#include <utils/Log.h>
-#include <utils/Trace.h>
#include <utils/Errors.h>
#include <binder/Parcel.h>
@@ -40,13 +38,11 @@
CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) :
mLocked(false)
{
- ATRACE_CALL();
mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity);
}
CameraMetadata::CameraMetadata(const CameraMetadata &other) :
mLocked(false) {
- ATRACE_CALL();
mBuffer = clone_camera_metadata(other.mBuffer);
}
@@ -117,7 +113,6 @@
}
void CameraMetadata::clear() {
- ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -129,7 +124,6 @@
}
void CameraMetadata::acquire(camera_metadata_t *buffer) {
- ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -143,7 +137,6 @@
}
void CameraMetadata::acquire(CameraMetadata &other) {
- ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return;
@@ -152,12 +145,10 @@
}
status_t CameraMetadata::append(const CameraMetadata &other) {
- ATRACE_CALL();
return append(other.mBuffer);
}
status_t CameraMetadata::append(const camera_metadata_t* other) {
- ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
@@ -179,7 +170,6 @@
}
status_t CameraMetadata::sort() {
- ATRACE_CALL();
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
return INVALID_OPERATION;
@@ -310,7 +300,6 @@
status_t CameraMetadata::updateImpl(uint32_t tag, const void *data,
size_t data_count) {
- ATRACE_CALL();
status_t res;
if (mLocked) {
ALOGE("%s: CameraMetadata is locked", __FUNCTION__);
@@ -368,13 +357,11 @@
}
bool CameraMetadata::exists(uint32_t tag) const {
- ATRACE_CALL();
camera_metadata_ro_entry entry;
return find_camera_metadata_ro_entry(mBuffer, tag, &entry) == 0;
}
camera_metadata_entry_t CameraMetadata::find(uint32_t tag) {
- ATRACE_CALL();
status_t res;
camera_metadata_entry entry;
if (mLocked) {
@@ -391,7 +378,6 @@
}
camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const {
- ATRACE_CALL();
status_t res;
camera_metadata_ro_entry entry;
res = find_camera_metadata_ro_entry(mBuffer, tag, &entry);
@@ -403,7 +389,6 @@
}
status_t CameraMetadata::erase(uint32_t tag) {
- ATRACE_CALL();
camera_metadata_entry_t entry;
status_t res;
if (mLocked) {
@@ -434,7 +419,6 @@
status_t CameraMetadata::removePermissionEntries(metadata_vendor_id_t vendorId,
std::vector<int32_t> *tagsRemoved) {
- ATRACE_CALL();
uint32_t tagCount = 0;
std::vector<uint32_t> tagsToRemove;
@@ -511,7 +495,6 @@
}
status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) {
- ATRACE_CALL();
if (mBuffer == NULL) {
mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2);
if (mBuffer == NULL) {
@@ -551,7 +534,7 @@
status_t CameraMetadata::readFromParcel(const Parcel& data,
camera_metadata_t** out) {
- ATRACE_CALL();
+
status_t err = OK;
camera_metadata_t* metadata = NULL;
@@ -642,7 +625,6 @@
status_t CameraMetadata::writeToParcel(Parcel& data,
const camera_metadata_t* metadata) {
- ATRACE_CALL();
status_t res = OK;
/**
@@ -737,7 +719,7 @@
}
status_t CameraMetadata::readFromParcel(const Parcel *parcel) {
- ATRACE_CALL();
+
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
status_t res = OK;
@@ -769,7 +751,7 @@
}
status_t CameraMetadata::writeToParcel(Parcel *parcel) const {
- ATRACE_CALL();
+
ALOGV("%s: parcel = %p", __FUNCTION__, parcel);
if (parcel == NULL) {
@@ -798,7 +780,7 @@
status_t CameraMetadata::getTagFromName(const char *name,
const VendorTagDescriptor* vTags, uint32_t *tag) {
- ATRACE_CALL();
+
if (name == nullptr || tag == nullptr) return BAD_VALUE;
size_t nameLength = strlen(name);
diff --git a/camera/ICameraClient.cpp b/camera/ICameraClient.cpp
index 487b8b0..c02c81b 100644
--- a/camera/ICameraClient.cpp
+++ b/camera/ICameraClient.cpp
@@ -139,20 +139,18 @@
CHECK_INTERFACE(ICameraClient, data, reply);
int32_t msgType = data.readInt32();
sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
- camera_frame_metadata_t *metadata = NULL;
+ camera_frame_metadata_t metadata;
if (data.dataAvail() > 0) {
- metadata = new camera_frame_metadata_t;
- metadata->number_of_faces = data.readInt32();
- if (metadata->number_of_faces <= 0 ||
- metadata->number_of_faces > (int32_t)(INT32_MAX / sizeof(camera_face_t))) {
- ALOGE("%s: Too large face count: %d", __FUNCTION__, metadata->number_of_faces);
+ metadata.number_of_faces = data.readInt32();
+ if (metadata.number_of_faces <= 0 ||
+ metadata.number_of_faces > (int32_t)(INT32_MAX / sizeof(camera_face_t))) {
+ ALOGE("%s: Too large face count: %d", __FUNCTION__, metadata.number_of_faces);
return BAD_VALUE;
}
- metadata->faces = (camera_face_t *) data.readInplace(
- sizeof(camera_face_t) * metadata->number_of_faces);
+ metadata.faces = (camera_face_t *) data.readInplace(
+ sizeof(camera_face_t) * metadata.number_of_faces);
}
- dataCallback(msgType, imageData, metadata);
- if (metadata) delete metadata;
+ dataCallback(msgType, imageData, &metadata);
return NO_ERROR;
} break;
case DATA_CALLBACK_TIMESTAMP: {
diff --git a/camera/cameraserver/cameraserver.rc b/camera/cameraserver/cameraserver.rc
index a9aae0b..8f51458 100644
--- a/camera/cameraserver/cameraserver.rc
+++ b/camera/cameraserver/cameraserver.rc
@@ -3,5 +3,5 @@
user cameraserver
group audio camera input drmrpc
ioprio rt 4
- writepid /dev/cpuset/camera-daemon/tasks /dev/stune/top-app/tasks
+ task_profiles CameraServiceCapacity MaxPerformance
rlimit rtprio 10 10
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 16457ac..8763c62 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -1897,11 +1897,13 @@
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
- * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:
- * <em> Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
- * better precision compared to an integer value of ACAMERA_SCALER_CROP_REGION.
- * </em> Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
- * ACAMERA_SCALER_CROP_REGION doesn't.</p>
+ * zoom by switching between lenses. Using zoomRatio has benefits in the scenarios below:</p>
+ * <ul>
+ * <li>Zooming in from a wide-angle lens to a telephoto lens: A floating-point ratio provides
+ * better precision compared to an integer value of ACAMERA_SCALER_CROP_REGION.</li>
+ * <li>Zooming out from a wide lens to an ultrawide lens: zoomRatio supports zoom-out whereas
+ * ACAMERA_SCALER_CROP_REGION doesn't.</li>
+ * </ul>
* <p>To illustrate, here are several scenarios of different zoom ratios, crop regions,
* and output streams, for a hypothetical camera device with an active array of size
* <code>(2000,1500)</code>.</p>
@@ -4381,8 +4383,8 @@
ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES = // int32[n]
ACAMERA_SENSOR_START + 25,
/**
- * <p>Duration between the start of first row exposure
- * and the start of last row exposure.</p>
+ * <p>Duration between the start of exposure for the first row of the image sensor,
+ * and the start of exposure for one past the last row of the image sensor.</p>
*
* <p>Type: int64</p>
*
@@ -4391,12 +4393,22 @@
* <li>ACameraMetadata from ACameraCaptureSession_captureCallback_result callbacks</li>
* </ul></p>
*
- * <p>This is the exposure time skew between the first and last
- * row exposure start times. The first row and the last row are
- * the first and last rows inside of the
+ * <p>This is the exposure time skew between the first and <code>(last+1)</code> row exposure start times. The
+ * first row and the last row are the first and last rows inside of the
* ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE.</p>
- * <p>For typical camera sensors that use rolling shutters, this is also equivalent
- * to the frame readout time.</p>
+ * <p>For typical camera sensors that use rolling shutters, this is also equivalent to the frame
+ * readout time.</p>
+ * <p>If the image sensor is operating in a binned or cropped mode due to the current output
+ * target resolutions, it's possible this skew is reported to be larger than the exposure
+ * time, for example, since it is based on the full array even if a partial array is read
+ * out. Be sure to scale the number to cover the section of the sensor actually being used
+ * for the outputs you care about. So if your output covers N rows of the active array of
+ * height H, scale this value by N/H to get the total skew for that viewport.</p>
+ * <p><em>Note:</em> Prior to Android 11, this field was described as measuring duration from
+ * first to last row of the image sensor, which is not equal to the frame readout time for a
+ * rolling shutter sensor. Implementations generally reported the latter value, so to resolve
+ * the inconsistency, the description has been updated to range from (first, last+1) row
+ * exposure start, instead.</p>
*
* @see ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
diff --git a/drm/TEST_MAPPING b/drm/TEST_MAPPING
new file mode 100644
index 0000000..2595e3e
--- /dev/null
+++ b/drm/TEST_MAPPING
@@ -0,0 +1,27 @@
+{
+ "presubmit": [
+ // The following tests validate codec and drm path.
+ {
+ "name": "GtsMediaTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+ }
+ ]
+ },
+ {
+ "name": "GtsExoPlayerTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.SocPresubmit"
+ },
+ {
+ "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ }
+ ]
+ }
+ ]
+}
diff --git a/include/drm/TEST_MAPPING b/include/drm/TEST_MAPPING
new file mode 100644
index 0000000..28e432e
--- /dev/null
+++ b/include/drm/TEST_MAPPING
@@ -0,0 +1,26 @@
+{
+ "presubmit": [
+ {
+ "name": "GtsMediaTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "include-filter": "com.google.android.media.gts.WidevineGenericOpsTests"
+ }
+ ]
+ },
+ {
+ "name": "GtsExoPlayerTestCases",
+ "options" : [
+ {
+ "include-annotation": "android.platform.test.annotations.SocPresubmit"
+ },
+ {
+ "include-filter": "com.google.android.exoplayer.gts.DashTest#testWidevine23FpsH264Fixed"
+ }
+ ]
+ }
+ ]
+}
diff --git a/include/media/TEST_MAPPING b/include/media/TEST_MAPPING
new file mode 100644
index 0000000..ac697da
--- /dev/null
+++ b/include/media/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/av/include/drm"
+ }
+ ]
+}
diff --git a/include/private/media/TEST_MAPPING b/include/private/media/TEST_MAPPING
new file mode 100644
index 0000000..ac697da
--- /dev/null
+++ b/include/private/media/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "imports": [
+ {
+ "path": "frameworks/av/include/drm"
+ }
+ ]
+}
diff --git a/media/audioserver/audioserver.rc b/media/audioserver/audioserver.rc
index 38c2750..f05c2d2 100644
--- a/media/audioserver/audioserver.rc
+++ b/media/audioserver/audioserver.rc
@@ -5,7 +5,7 @@
group audio camera drmrpc media mediadrm net_bt net_bt_admin net_bw_acct wakelock
capabilities BLOCK_SUSPEND
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
onrestart setprop sys.audio.restart.hal 1
@@ -20,6 +20,14 @@
# Keep the original service names for backward compatibility
stop vendor.audio-hal-2-0
stop audio-hal-2-0
+ # See b/155364397. Need to have HAL service running for VTS.
+ # Can't use 'restart' because then HAL service would restart
+ # audioserver bringing it back into running state.
+ start vendor.audio-hal
+ start vendor.audio-hal-4-0-msd
+ # Keep the original service names for backward compatibility
+ start vendor.audio-hal-2-0
+ start audio-hal-2-0
on property:init.svc.audioserver=running
start vendor.audio-hal
diff --git a/media/bufferpool/2.0/Accessor.cpp b/media/bufferpool/2.0/Accessor.cpp
index 57b4609..e05b12a 100644
--- a/media/bufferpool/2.0/Accessor.cpp
+++ b/media/bufferpool/2.0/Accessor.cpp
@@ -117,6 +117,10 @@
Accessor::Impl::createInvalidator();
}
+void Accessor::createEvictor() {
+ Accessor::Impl::createEvictor();
+}
+
// Methods from ::android::hardware::media::bufferpool::V2_0::IAccessor follow.
Return<void> Accessor::connect(
const sp<::android::hardware::media::bufferpool::V2_0::IObserver>& observer,
diff --git a/media/bufferpool/2.0/Accessor.h b/media/bufferpool/2.0/Accessor.h
index 8d02519..8b43301 100644
--- a/media/bufferpool/2.0/Accessor.h
+++ b/media/bufferpool/2.0/Accessor.h
@@ -187,6 +187,8 @@
static void createInvalidator();
+ static void createEvictor();
+
private:
class Impl;
std::shared_ptr<Impl> mImpl;
diff --git a/media/bufferpool/2.0/AccessorImpl.cpp b/media/bufferpool/2.0/AccessorImpl.cpp
index cb55b07..6111fea 100644
--- a/media/bufferpool/2.0/AccessorImpl.cpp
+++ b/media/bufferpool/2.0/AccessorImpl.cpp
@@ -39,6 +39,9 @@
static constexpr size_t kMinAllocBytesForEviction = 1024*1024*15;
static constexpr size_t kMinBufferCountForEviction = 25;
+
+ static constexpr nsecs_t kEvictGranularityNs = 1000000000; // 1 sec
+ static constexpr nsecs_t kEvictDurationNs = 5000000000; // 5 secs
}
// Buffer structure in bufferpool process
@@ -136,7 +139,7 @@
}
#ifdef __ANDROID_VNDK__
-static constexpr uint32_t kSeqIdVndkBit = 1 << 31;
+static constexpr uint32_t kSeqIdVndkBit = 1U << 31;
#else
static constexpr uint32_t kSeqIdVndkBit = 0;
#endif
@@ -146,7 +149,7 @@
Accessor::Impl::Impl(
const std::shared_ptr<BufferPoolAllocator> &allocator)
- : mAllocator(allocator) {}
+ : mAllocator(allocator), mScheduleEvictTs(0) {}
Accessor::Impl::~Impl() {
}
@@ -184,6 +187,7 @@
}
mBufferPool.processStatusMessages();
mBufferPool.cleanUp();
+ scheduleEvictIfNeeded();
}
return status;
}
@@ -198,6 +202,7 @@
// Since close# will be called after all works are finished, it is OK to
// evict unused buffers.
mBufferPool.cleanUp(true);
+ scheduleEvictIfNeeded();
return ResultStatus::OK;
}
@@ -224,6 +229,7 @@
mBufferPool.handleOwnBuffer(connectionId, *bufferId);
}
mBufferPool.cleanUp();
+ scheduleEvictIfNeeded();
return status;
}
@@ -249,6 +255,7 @@
}
}
mBufferPool.cleanUp();
+ scheduleEvictIfNeeded();
return ResultStatus::CRITICAL_ERROR;
}
@@ -891,6 +898,88 @@
}
}
+void Accessor::Impl::evictorThread(
+ std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> &accessors,
+ std::mutex &mutex,
+ std::condition_variable &cv) {
+ std::list<const std::weak_ptr<Accessor::Impl>> evictList;
+ while (true) {
+ int expired = 0;
+ int evicted = 0;
+ {
+ nsecs_t now = systemTime();
+ std::unique_lock<std::mutex> lock(mutex);
+ if (accessors.size() == 0) {
+ cv.wait(lock);
+ }
+ auto it = accessors.begin();
+ while (it != accessors.end()) {
+ if (now > (it->second + kEvictDurationNs)) {
+ ++expired;
+ evictList.push_back(it->first);
+ it = accessors.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ // evict idle accessors;
+ for (auto it = evictList.begin(); it != evictList.end(); ++it) {
+ const std::shared_ptr<Accessor::Impl> accessor = it->lock();
+ if (accessor) {
+ accessor->cleanUp(true);
+ ++evicted;
+ }
+ }
+ if (expired > 0) {
+ ALOGD("evictor expired: %d, evicted: %d", expired, evicted);
+ }
+ evictList.clear();
+ ::usleep(kEvictGranularityNs / 1000);
+ }
+}
+
+Accessor::Impl::AccessorEvictor::AccessorEvictor() {
+ std::thread evictor(
+ evictorThread,
+ std::ref(mAccessors),
+ std::ref(mMutex),
+ std::ref(mCv));
+ evictor.detach();
+}
+
+void Accessor::Impl::AccessorEvictor::addAccessor(
+ const std::weak_ptr<Accessor::Impl> &impl, nsecs_t ts) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ bool notify = mAccessors.empty();
+ auto it = mAccessors.find(impl);
+ if (it == mAccessors.end()) {
+ mAccessors.emplace(impl, ts);
+ } else {
+ it->second = ts;
+ }
+ if (notify) {
+ mCv.notify_one();
+ }
+}
+
+std::unique_ptr<Accessor::Impl::AccessorEvictor> Accessor::Impl::sEvictor;
+
+void Accessor::Impl::createEvictor() {
+ if (!sEvictor) {
+ sEvictor = std::make_unique<Accessor::Impl::AccessorEvictor>();
+ }
+}
+
+void Accessor::Impl::scheduleEvictIfNeeded() {
+ nsecs_t now = systemTime();
+
+ if (now > (mScheduleEvictTs + kEvictGranularityNs)) {
+ mScheduleEvictTs = now;
+ sEvictor->addAccessor(shared_from_this(), now);
+ }
+}
+
} // namespace implementation
} // namespace V2_0
} // namespace bufferpool
diff --git a/media/bufferpool/2.0/AccessorImpl.h b/media/bufferpool/2.0/AccessorImpl.h
index 9888be5..cd1b4d0 100644
--- a/media/bufferpool/2.0/AccessorImpl.h
+++ b/media/bufferpool/2.0/AccessorImpl.h
@@ -20,6 +20,7 @@
#include <map>
#include <set>
#include <condition_variable>
+#include <utils/Timers.h>
#include "Accessor.h"
namespace android {
@@ -71,6 +72,8 @@
static void createInvalidator();
+ static void createEvictor();
+
private:
// ConnectionId = pid : (timestamp_created + seqId)
// in order to guarantee uniqueness for each connection
@@ -78,6 +81,8 @@
const std::shared_ptr<BufferPoolAllocator> mAllocator;
+ nsecs_t mScheduleEvictTs;
+
/**
* Buffer pool implementation.
*
@@ -389,6 +394,25 @@
std::mutex &mutex,
std::condition_variable &cv,
bool &ready);
+
+ struct AccessorEvictor {
+ std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> mAccessors;
+ std::mutex mMutex;
+ std::condition_variable mCv;
+
+ AccessorEvictor();
+ void addAccessor(const std::weak_ptr<Accessor::Impl> &impl, nsecs_t ts);
+ };
+
+ static std::unique_ptr<AccessorEvictor> sEvictor;
+
+ static void evictorThread(
+ std::map<const std::weak_ptr<Accessor::Impl>, nsecs_t, std::owner_less<>> &accessors,
+ std::mutex &mutex,
+ std::condition_variable &cv);
+
+ void scheduleEvictIfNeeded();
+
};
} // namespace implementation
diff --git a/media/bufferpool/2.0/ClientManager.cpp b/media/bufferpool/2.0/ClientManager.cpp
index 87ee4e8..54a20b9 100644
--- a/media/bufferpool/2.0/ClientManager.cpp
+++ b/media/bufferpool/2.0/ClientManager.cpp
@@ -484,6 +484,7 @@
sInstance = new ClientManager();
}
Accessor::createInvalidator();
+ Accessor::createEvictor();
return sInstance;
}
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
index 6578ad2..f7943be 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.cpp
@@ -336,11 +336,10 @@
memset(output, 0, outSamples * sizeof(int16_t));
} else {
int16_t FT;
- RX_State_wb rx_state;
int16_t numRecSamples;
mime_unsorting(const_cast<uint8_t *>(&input[1]),
- mInputSampleBuffer, &FT, &FM, 1, &rx_state);
+ mInputSampleBuffer, &FT, &FM, 1, &mRxState);
pvDecoder_AmrWb(FM, mInputSampleBuffer, output, &numRecSamples,
mDecoderBuf, FT, mDecoderCookie);
if (numRecSamples != outSamples) {
diff --git a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
index 6384450..afe1537 100644
--- a/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
+++ b/media/codec2/components/amr_nb_wb/C2SoftAmrDec.h
@@ -18,6 +18,8 @@
#define ANDROID_C2_SOFT_AMR_DEC_H_
#include <SimpleC2Component.h>
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
namespace android {
@@ -51,6 +53,7 @@
void *mAmrHandle;
void *mDecoderBuf;
int16_t *mDecoderCookie;
+ RX_State_wb mRxState{};
int16_t mInputSampleBuffer[477];
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index c1edb98..d7b9e12 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -549,7 +549,7 @@
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
if (outBuffer) {
- if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
return false;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index e4b911d..23104dc 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -544,7 +544,7 @@
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
if (outBuffer) {
- if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
return false;
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index c7ca18c..55dd475 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -618,7 +618,7 @@
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[1] = chromaSize;
ps_decode_ip->s_out_buffer.u4_min_out_buf_size[2] = chromaSize;
if (outBuffer) {
- if (outBuffer->width() < displayStride || outBuffer->height() < displayHeight) {
+ if (outBuffer->height() < displayHeight) {
ALOGE("Output buffer too small: provided (%dx%d) required (%ux%u)",
outBuffer->width(), outBuffer->height(), displayStride, displayHeight);
return false;
diff --git a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
index ecc56f5..823e11b 100644
--- a/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
+++ b/media/codec2/hidl/1.0/vts/functional/video/VtsHalMediaC2V1_0TargetVideoEncTest.cpp
@@ -41,7 +41,7 @@
: C2Buffer({block->share(C2Rect(block->width(), block->height()), ::C2Fence())}) {}
};
-static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
+static std::vector<std::tuple<std::string, std::string, std::string, std::string, std::string>>
kEncodeTestParameters;
static std::vector<std::tuple<std::string, std::string, std::string, std::string>>
kEncodeResolutionTestParameters;
@@ -100,6 +100,7 @@
}
mEos = false;
mCsd = false;
+ mConfigBPictures = false;
mFramesReceived = 0;
mFailedWorkReceived = 0;
mTimestampUs = 0u;
@@ -120,7 +121,7 @@
// Get the test parameters from GetParam call.
virtual void getParams() {}
- bool setupConfigParam(int32_t nWidth, int32_t nHeight);
+ bool setupConfigParam(int32_t nWidth, int32_t nHeight, int32_t nBFrame = 0);
// callback function to process onWorkDone received by Listener
void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
@@ -130,8 +131,10 @@
// previous timestamp
typedef std::unique_lock<std::mutex> ULock;
if (!mTimestampUslist.empty()) {
- EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
- mTimestampUs);
+ if (!mConfigBPictures) {
+ EXPECT_GE((work->worklets.front()->output.ordinal.timestamp.peeku()),
+ mTimestampUs);
+ }
mTimestampUs = work->worklets.front()->output.ordinal.timestamp.peeku();
// Currently this lock is redundant as no mTimestampUslist is only initialized
// before queuing any work to component. Once AdaptiveTest is added similar to
@@ -192,7 +195,7 @@
bool mEos;
bool mCsd;
bool mDisableTest;
- bool mConfig;
+ bool mConfigBPictures;
bool mTimestampDevTest;
standardComp mCompName;
uint32_t mFramesReceived;
@@ -269,13 +272,27 @@
}
// Set Default config param.
-bool Codec2VideoEncHidlTestBase::setupConfigParam(int32_t nWidth, int32_t nHeight) {
+bool Codec2VideoEncHidlTestBase::setupConfigParam(int32_t nWidth, int32_t nHeight,
+ int32_t nBFrame) {
+ c2_status_t status = C2_OK;
+ std::vector<std::unique_ptr<C2Param>> configParam;
std::vector<std::unique_ptr<C2SettingResult>> failures;
- C2StreamPictureSizeInfo::input inputSize(0u, nWidth, nHeight);
- std::vector<C2Param*> configParam{&inputSize};
- c2_status_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
- if (status == C2_OK && failures.size() == 0u) return true;
- return false;
+
+ configParam.push_back(std::make_unique<C2StreamPictureSizeInfo::input>(0u, nWidth, nHeight));
+
+ if (nBFrame > 0) {
+ std::unique_ptr<C2StreamGopTuning::output> gop =
+ C2StreamGopTuning::output::AllocUnique(2 /* flexCount */, 0u /* stream */);
+ gop->m.values[0] = {P_FRAME, UINT32_MAX};
+ gop->m.values[1] = {C2Config::picture_type_t(P_FRAME | B_FRAME), uint32_t(nBFrame)};
+ configParam.push_back(std::move(gop));
+ }
+
+ for (const std::unique_ptr<C2Param>& param : configParam) {
+ status = mComponent->config({param.get()}, C2_DONT_BLOCK, &failures);
+ if (status != C2_OK || failures.size() != 0u) return false;
+ }
+ return true;
}
// LookUpTable of clips for component testing
@@ -388,7 +405,7 @@
class Codec2VideoEncEncodeTest
: public Codec2VideoEncHidlTestBase,
public ::testing::WithParamInterface<
- std::tuple<std::string, std::string, std::string, std::string>> {
+ std::tuple<std::string, std::string, std::string, std::string, std::string>> {
void getParams() {
mInstanceName = std::get<0>(GetParam());
mComponentName = std::get<1>(GetParam());
@@ -405,6 +422,7 @@
bool signalEOS = !std::get<2>(GetParam()).compare("true");
// Send an empty frame to receive CSD data from encoder.
bool sendEmptyFirstFrame = !std::get<3>(GetParam()).compare("true");
+ mConfigBPictures = !std::get<4>(GetParam()).compare("true");
strcpy(mURL, sResourceDir.c_str());
GetURLForComponent(mURL);
@@ -428,10 +446,30 @@
inputFrames--;
}
- if (!setupConfigParam(nWidth, nHeight)) {
+ if (!setupConfigParam(nWidth, nHeight, mConfigBPictures ? 1 : 0)) {
std::cout << "[ WARN ] Test Skipped \n";
return;
}
+ std::vector<std::unique_ptr<C2Param>> inParams;
+ c2_status_t c2_status = mComponent->query({}, {C2StreamGopTuning::output::PARAM_TYPE},
+ C2_DONT_BLOCK, &inParams);
+
+ if (c2_status != C2_OK || inParams.size() == 0) {
+ std::cout << "[ WARN ] Bframe not supported for " << mComponentName
+ << " resetting num BFrames to 0\n";
+ mConfigBPictures = false;
+ } else {
+ size_t offset = sizeof(C2Param);
+ C2Param* param = inParams[0].get();
+ int32_t numBFrames = *(int32_t*)((uint8_t*)param + offset);
+
+ if (!numBFrames) {
+ std::cout << "[ WARN ] Bframe not supported for " << mComponentName
+ << " resetting num BFrames to 0\n";
+ mConfigBPictures = false;
+ }
+ }
+
ASSERT_EQ(mComponent->start(), C2_OK);
if (sendEmptyFirstFrame) {
@@ -816,14 +854,14 @@
int main(int argc, char** argv) {
kTestParameters = getTestParameters(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER);
for (auto params : kTestParameters) {
- kEncodeTestParameters.push_back(
- std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "true"));
- kEncodeTestParameters.push_back(
- std::make_tuple(std::get<0>(params), std::get<1>(params), "true", "false"));
- kEncodeTestParameters.push_back(
- std::make_tuple(std::get<0>(params), std::get<1>(params), "false", "true"));
- kEncodeTestParameters.push_back(
- std::make_tuple(std::get<0>(params), std::get<1>(params), "false", "false"));
+ constexpr char const* kBoolString[] = { "false", "true" };
+ for (size_t i = 0; i < 1 << 3; ++i) {
+ kEncodeTestParameters.push_back(std::make_tuple(
+ std::get<0>(params), std::get<1>(params),
+ kBoolString[i & 1],
+ kBoolString[(i >> 1) & 1],
+ kBoolString[(i >> 2) & 1]));
+ }
kEncodeResolutionTestParameters.push_back(
std::make_tuple(std::get<0>(params), std::get<1>(params), "52", "18"));
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 01d106f..a3fff35 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -1379,7 +1379,7 @@
state->set(STOPPING);
}
- mChannel->stop();
+ mChannel->reset();
(new AMessage(kWhatStop, this))->post();
}
@@ -1465,7 +1465,7 @@
}
}
- mChannel->stop();
+ mChannel->reset();
// thiz holds strong ref to this while the thread is running.
sp<CCodec> thiz(this);
std::thread([thiz, sendCallback] { thiz->release(sendCallback); }).detach();
@@ -1492,6 +1492,7 @@
state->set(RELEASED);
state->comp.reset();
}
+ (new AMessage(kWhatRelease, this))->post();
if (sendCallback) {
mCallback->onReleaseCompleted();
}
@@ -1756,6 +1757,12 @@
flush();
break;
}
+ case kWhatRelease: {
+ mChannel->release();
+ mClient.reset();
+ mClientListener.reset();
+ break;
+ }
case kWhatCreateInputSurface: {
// Surface operations may be briefly blocking.
setDeadline(now, 1500ms, "createInputSurface");
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index e902b5d..49c04a0 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -713,7 +713,7 @@
return;
} else {
Mutexed<Output>::Locked output(mOutput);
- if (output->buffers->numClientBuffers() >= output->numSlots) {
+ if (!output->buffers || output->buffers->numClientBuffers() >= output->numSlots) {
return;
}
}
@@ -851,31 +851,39 @@
if (hdrStaticInfo || hdr10PlusInfo) {
HdrMetadata hdr;
if (hdrStaticInfo) {
- struct android_smpte2086_metadata smpte2086_meta = {
- .displayPrimaryRed = {
- hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y
- },
- .displayPrimaryGreen = {
- hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y
- },
- .displayPrimaryBlue = {
- hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y
- },
- .whitePoint = {
- hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y
- },
- .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
- .minLuminance = hdrStaticInfo->mastering.minLuminance,
- };
-
- struct android_cta861_3_metadata cta861_meta = {
- .maxContentLightLevel = hdrStaticInfo->maxCll,
- .maxFrameAverageLightLevel = hdrStaticInfo->maxFall,
- };
-
- hdr.validTypes = HdrMetadata::SMPTE2086 | HdrMetadata::CTA861_3;
- hdr.smpte2086 = smpte2086_meta;
- hdr.cta8613 = cta861_meta;
+ // If mastering max and min luminance fields are 0, do not use them.
+ // It indicates the value may not be present in the stream.
+ if (hdrStaticInfo->mastering.maxLuminance > 0.0f &&
+ hdrStaticInfo->mastering.minLuminance > 0.0f) {
+ struct android_smpte2086_metadata smpte2086_meta = {
+ .displayPrimaryRed = {
+ hdrStaticInfo->mastering.red.x, hdrStaticInfo->mastering.red.y
+ },
+ .displayPrimaryGreen = {
+ hdrStaticInfo->mastering.green.x, hdrStaticInfo->mastering.green.y
+ },
+ .displayPrimaryBlue = {
+ hdrStaticInfo->mastering.blue.x, hdrStaticInfo->mastering.blue.y
+ },
+ .whitePoint = {
+ hdrStaticInfo->mastering.white.x, hdrStaticInfo->mastering.white.y
+ },
+ .maxLuminance = hdrStaticInfo->mastering.maxLuminance,
+ .minLuminance = hdrStaticInfo->mastering.minLuminance,
+ };
+ hdr.validTypes = HdrMetadata::SMPTE2086;
+ hdr.smpte2086 = smpte2086_meta;
+ }
+ // If the content light level fields are 0, do not use them, it
+ // indicates the value may not be present in the stream.
+ if (hdrStaticInfo->maxCll > 0.0f && hdrStaticInfo->maxFall > 0.0f) {
+ struct android_cta861_3_metadata cta861_meta = {
+ .maxContentLightLevel = hdrStaticInfo->maxCll,
+ .maxFrameAverageLightLevel = hdrStaticInfo->maxFall,
+ };
+ hdr.validTypes |= HdrMetadata::CTA861_3;
+ hdr.cta8613 = cta861_meta;
+ }
}
if (hdr10PlusInfo) {
hdr.validTypes |= HdrMetadata::HDR10PLUS;
@@ -1396,6 +1404,30 @@
}
}
+void CCodecBufferChannel::reset() {
+ stop();
+ {
+ Mutexed<Input>::Locked input(mInput);
+ input->buffers.reset(new DummyInputBuffers(""));
+ }
+ {
+ Mutexed<Output>::Locked output(mOutput);
+ output->buffers.reset();
+ }
+}
+
+void CCodecBufferChannel::release() {
+ mComponent.reset();
+ mInputAllocator.reset();
+ mOutputSurface.lock()->surface.clear();
+ {
+ Mutexed<BlockPools>::Locked blockPools{mBlockPools};
+ blockPools->inputPool.reset();
+ blockPools->outputPoolIntf.reset();
+ }
+}
+
+
void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
ALOGV("[%s] flush", mName);
{
@@ -1426,7 +1458,9 @@
}
{
Mutexed<Output>::Locked output(mOutput);
- output->buffers->flush(flushedWork);
+ if (output->buffers) {
+ output->buffers->flush(flushedWork);
+ }
}
mReorderStash.lock()->flush();
mPipelineWatcher.lock()->flush();
@@ -1464,20 +1498,25 @@
std::unique_ptr<C2Work> work,
const sp<AMessage> &outputFormat,
const C2StreamInitDataInfo::output *initData) {
- if (outputFormat != nullptr) {
+ {
Mutexed<Output>::Locked output(mOutput);
- ALOGD("[%s] onWorkDone: output format changed to %s",
- mName, outputFormat->debugString().c_str());
- output->buffers->setFormat(outputFormat);
+ if (!output->buffers) {
+ return false;
+ }
+ if (outputFormat != nullptr) {
+ ALOGD("[%s] onWorkDone: output format changed to %s",
+ mName, outputFormat->debugString().c_str());
+ output->buffers->setFormat(outputFormat);
- AString mediaType;
- if (outputFormat->findString(KEY_MIME, &mediaType)
- && mediaType == MIMETYPE_AUDIO_RAW) {
- int32_t channelCount;
- int32_t sampleRate;
- if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
- && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
- output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
+ AString mediaType;
+ if (outputFormat->findString(KEY_MIME, &mediaType)
+ && mediaType == MIMETYPE_AUDIO_RAW) {
+ int32_t channelCount;
+ int32_t sampleRate;
+ if (outputFormat->findInt32(KEY_CHANNEL_COUNT, &channelCount)
+ && outputFormat->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
+ output->buffers->updateSkipCutBuffer(sampleRate, channelCount);
+ }
}
}
}
@@ -1601,6 +1640,9 @@
size_t numInputSlots = mInput.lock()->numSlots;
{
Mutexed<Output>::Locked output(mOutput);
+ if (!output->buffers) {
+ return false;
+ }
output->outputDelay = outputDelay.value;
numOutputSlots = outputDelay.value + kSmoothnessFactor;
if (output->numSlots < numOutputSlots) {
@@ -1690,7 +1732,7 @@
if (initData != nullptr) {
Mutexed<Output>::Locked output(mOutput);
- if (output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
+ if (output->buffers && output->buffers->registerCsd(initData, &index, &outBuffer) == OK) {
outBuffer->meta()->setInt64("timeUs", timestamp.peek());
outBuffer->meta()->setInt32("flags", MediaCodec::BUFFER_FLAG_CODECCONFIG);
ALOGV("[%s] onWorkDone: csd index = %zu [%p]", mName, index, outBuffer.get());
@@ -1705,8 +1747,8 @@
}
}
- if (!buffer && !flags) {
- ALOGV("[%s] onWorkDone: Not reporting output buffer (%lld)",
+ if (!buffer && !flags && outputFormat == nullptr) {
+ ALOGV("[%s] onWorkDone: nothing to report from the work (%lld)",
mName, work->input.ordinal.frameIndex.peekull());
return true;
}
@@ -1753,6 +1795,9 @@
}
Mutexed<Output>::Locked output(mOutput);
+ if (!output->buffers) {
+ return;
+ }
status_t err = output->buffers->registerBuffer(entry.buffer, &index, &outBuffer);
if (err != OK) {
bool outputBuffersChanged = false;
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 0263211..f6e7024 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -138,6 +138,16 @@
*/
void stop();
+ /**
+ * Stop queueing buffers to the component and release all buffers.
+ */
+ void reset();
+
+ /**
+ * Release all resources.
+ */
+ void release();
+
void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
/**
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index 6244acd..eec79f1 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -416,7 +416,7 @@
size_t *index,
sp<Codec2Buffer> *buffer,
std::function<bool(const sp<Codec2Buffer> &)> match =
- [](const sp<Codec2Buffer> &) { return true; });
+ [](const sp<Codec2Buffer> &buffer) { return (buffer != nullptr); });
/**
* Return the buffer from the client, and get the C2Buffer object back from
diff --git a/media/extractors/Android.bp b/media/extractors/Android.bp
index bb42580..7c4e62f 100644
--- a/media/extractors/Android.bp
+++ b/media/extractors/Android.bp
@@ -24,6 +24,9 @@
"libmediandk#29",
],
+ // extractors are supposed to work on Q(29)
+ min_sdk_version: "29",
+
relative_install_path: "extractors",
compile_multilib: "first",
diff --git a/media/extractors/TEST_MAPPING b/media/extractors/TEST_MAPPING
new file mode 100644
index 0000000..abefb0f
--- /dev/null
+++ b/media/extractors/TEST_MAPPING
@@ -0,0 +1,17 @@
+{
+ "presubmit": [
+ // TODO(b/153661591) enable test once the bug is fixed
+ // This tests the extractor path
+ // {
+ // "name": "GtsYouTubeTestCases",
+ // "options" : [
+ // {
+ // "include-annotation": "android.platform.test.annotations.Presubmit"
+ // },
+ // {
+ // "include-filter": "com.google.android.youtube.gts.SimultaneousClearAndProtectedDecodeTest#testSimultaneousClearAndProtectedDecode"
+ // }
+ // ]
+ // }
+ ]
+}
diff --git a/media/extractors/mkv/MatroskaExtractor.cpp b/media/extractors/mkv/MatroskaExtractor.cpp
index 044c4d0..b88e4e8 100644
--- a/media/extractors/mkv/MatroskaExtractor.cpp
+++ b/media/extractors/mkv/MatroskaExtractor.cpp
@@ -339,7 +339,12 @@
}
void BlockIterator::advance_l() {
- for (;;) {
+ for (int i = 0;; i++) {
+ if (i == 1000) {
+ ALOGE("no block found after %d iterations, stopping", i);
+ mCluster = NULL;
+ break;
+ }
long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry);
ALOGV("GetEntry returned %ld", res);
@@ -809,11 +814,13 @@
int32_t sampleRate;
if (!AMediaFormat_getInt32(trackInfo->mMeta, AMEDIAFORMAT_KEY_SAMPLE_RATE,
&sampleRate)) {
+ mbuf->release();
return AMEDIA_ERROR_MALFORMED;
}
int64_t durationUs;
if (!AMediaFormat_getInt64(trackInfo->mMeta, AMEDIAFORMAT_KEY_DURATION,
&durationUs)) {
+ mbuf->release();
return AMEDIA_ERROR_MALFORMED;
}
// TODO: Explore if this can be handled similar to MPEG4 extractor where padding is
@@ -976,6 +983,7 @@
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
+ buffer->release();
clearPendingFrames();
return err;
}
@@ -995,6 +1003,7 @@
while (mPendingFrames.empty()) {
media_status_t err = readBlock();
if (err != OK) {
+ buffer->release();
clearPendingFrames();
return err;
}
diff --git a/media/extractors/mp3/MP3Extractor.cpp b/media/extractors/mp3/MP3Extractor.cpp
index a838ae6..5165822 100644
--- a/media/extractors/mp3/MP3Extractor.cpp
+++ b/media/extractors/mp3/MP3Extractor.cpp
@@ -227,17 +227,17 @@
private:
static const size_t kMaxFrameSize;
- AMediaFormat *mMeta;
- DataSourceHelper *mDataSource;
- off64_t mFirstFramePos;
- uint32_t mFixedHeader;
- off64_t mCurrentPos;
- int64_t mCurrentTimeUs;
- bool mStarted;
- MP3Seeker *mSeeker;
+ AMediaFormat *mMeta = NULL;
+ DataSourceHelper *mDataSource = NULL;
+ off64_t mFirstFramePos = 0;
+ uint32_t mFixedHeader = 0;
+ off64_t mCurrentPos = 0;
+ int64_t mCurrentTimeUs = 0;
+ bool mStarted = false;
+ MP3Seeker *mSeeker = NULL;
- int64_t mBasisTimeUs;
- int64_t mSamplesRead;
+ int64_t mBasisTimeUs = 0;
+ int64_t mSamplesRead = 0;
MP3Source(const MP3Source &);
MP3Source &operator=(const MP3Source &);
@@ -251,11 +251,7 @@
MP3Extractor::MP3Extractor(
DataSourceHelper *source, Mp3Meta *meta)
- : mInitCheck(NO_INIT),
- mDataSource(source),
- mFirstFramePos(-1),
- mFixedHeader(0),
- mSeeker(NULL) {
+ : mDataSource(source) {
off64_t pos = 0;
off64_t post_id3_pos;
@@ -442,6 +438,7 @@
// (8000 samples/sec * 8 bits/byte)) + 1 padding byte/frame = 2881 bytes/frame.
// Set our max frame size to the nearest power of 2 above this size (aka, 4kB)
const size_t MP3Source::kMaxFrameSize = (1 << 12); /* 4096 bytes */
+
MP3Source::MP3Source(
AMediaFormat *meta, DataSourceHelper *source,
off64_t first_frame_pos, uint32_t fixed_header,
@@ -450,12 +447,7 @@
mDataSource(source),
mFirstFramePos(first_frame_pos),
mFixedHeader(fixed_header),
- mCurrentPos(0),
- mCurrentTimeUs(0),
- mStarted(false),
- mSeeker(seeker),
- mBasisTimeUs(0),
- mSamplesRead(0) {
+ mSeeker(seeker) {
}
MP3Source::~MP3Source() {
diff --git a/media/extractors/mp3/MP3Extractor.h b/media/extractors/mp3/MP3Extractor.h
index 1e38ab7..a2345da 100644
--- a/media/extractors/mp3/MP3Extractor.h
+++ b/media/extractors/mp3/MP3Extractor.h
@@ -45,13 +45,13 @@
virtual const char * name() { return "MP3Extractor"; }
private:
- status_t mInitCheck;
+ status_t mInitCheck = NO_INIT;
- DataSourceHelper *mDataSource;
- off64_t mFirstFramePos;
- AMediaFormat *mMeta;
- uint32_t mFixedHeader;
- MP3Seeker *mSeeker;
+ DataSourceHelper *mDataSource = NULL;
+ off64_t mFirstFramePos = -1;
+ AMediaFormat *mMeta = NULL;
+ uint32_t mFixedHeader = 0;
+ MP3Seeker *mSeeker = NULL;
MP3Extractor(const MP3Extractor &);
MP3Extractor &operator=(const MP3Extractor &);
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 3981454..62927a0 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -44,6 +44,8 @@
struct AAudioMessageEvent {
aaudio_service_event_t event;
union {
+ // Align so that 32 and 64-bit code can exchange messages through shared memory.
+ alignas(8)
double dataDouble;
int64_t dataLong;
};
@@ -57,8 +59,10 @@
EVENT,
};
- code what;
+ code what;
union {
+ // Align so that 32 and 64-bit code can exchange messages through shared memory.
+ alignas(8)
AAudioMessageTimestamp timestamp; // what == TIMESTAMP
AAudioMessageEvent event; // what == EVENT
};
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 3db0099..79fa5ed 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -354,6 +354,12 @@
drainTimestampsFromService();
aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
+ if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+ ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ // Stealing was added in R. Coerce result to improve backward compatibility.
+ result = AAUDIO_ERROR_DISCONNECTED;
+ setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ }
startTime = AudioClock::getNanoseconds();
mClockModel.start(startTime);
@@ -397,7 +403,12 @@
if (isDataCallbackSet()
&& (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
mCallbackEnabled.store(false);
- return joinThread(NULL); // may temporarily unlock mStreamLock
+ aaudio_result_t result = joinThread(NULL); // may temporarily unlock mStreamLock
+ if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+ ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ result = AAUDIO_OK;
+ }
+ return result;
} else {
return AAUDIO_OK;
}
@@ -427,7 +438,12 @@
setState(AAUDIO_STREAM_STATE_STOPPING);
mAtomicInternalTimestamp.clear();
- return mServiceInterface.stopStream(mServiceStreamHandle);
+ result = mServiceInterface.stopStream(mServiceStreamHandle);
+ if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+ ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+ result = AAUDIO_OK;
+ }
+ return result;
}
aaudio_result_t AudioStreamInternal::registerThread() {
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 73fd896..a6e5f70 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -226,3 +226,15 @@
"libutils",
],
}
+
+cc_test {
+ name: "test_steal_exclusive",
+ defaults: ["libaaudio_tests_defaults"],
+ srcs: ["test_steal_exclusive.cpp"],
+ shared_libs: [
+ "libaaudio",
+ "libbinder",
+ "libcutils",
+ "libutils",
+ ],
+}
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
new file mode 100644
index 0000000..2a05910
--- /dev/null
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/**
+ * This test starts an exclusive stream.
+ * Then a few seconds later it starts a second exclusive stream.
+ * The first stream should get stolen and they should both end up
+ * as SHARED streams.
+ * The test will print PASS or FAIL.
+ *
+ * If you plug in a headset during the test then you can get them to both
+ * open at almost the same time. This can result in a race condition.
+ * Both streams may try to automatically reopen their streams in EXCLUSIVE mode.
+ * The first stream will have its EXCLUSIVE stream stolen by the second stream.
+ * It will usually get disconnected between its Open and Start calls.
+ * This can also occur in normal use. But is unlikely because the window is very narrow.
+ * In this case, where two streams are responding to the same disconnect event,
+ * it will usually happen.
+ *
+ * Because the stream has not started, this condition will not trigger an onError callback.
+ * But the stream will get an error returned from AAudioStream_requestStart().
+ * The test uses this result to trigger a retry in the onError callback.
+ * That is the best practice for any app restarting a stream.
+ *
+ * You should see that both streams are advancing after the disconnect.
+ *
+ * The headset can connect using a 3.5 mm jack, or USB-C or Bluetooth.
+ *
+ * This test can be used with INPUT by using the -i command line option.
+ * Before running the test you will need to enter "adb root" so that
+ * you can have permission to record.
+ * Also the headset needs to have a microphone.
+ * Then the test should behave essentially the same.
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <thread>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+
+#define DEFAULT_TIMEOUT_NANOS ((int64_t)1000000000)
+#define SOLO_DURATION_MSEC 2000
+#define DUET_DURATION_MSEC 8000
+#define SLEEP_DURATION_MSEC 500
+
+static const char * s_sharingModeToText(aaudio_sharing_mode_t mode) {
+ return (mode == AAUDIO_SHARING_MODE_EXCLUSIVE) ? "EXCLUSIVE"
+ : ((mode == AAUDIO_SHARING_MODE_SHARED) ? "SHARED"
+ : AAudio_convertResultToText(mode));
+}
+
+static void s_myErrorCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ aaudio_result_t error);
+
+struct AudioEngine {
+ AAudioStream *stream = nullptr;
+ std::thread *thread = nullptr;
+ aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+
+ // These counters are read and written by the callback and the main thread.
+ std::atomic<int32_t> framesRead{};
+ std::atomic<int32_t> framesCalled{};
+ std::atomic<int32_t> callbackCount{};
+
+ void reset() {
+ framesRead.store(0);
+ framesCalled.store(0);
+ callbackCount.store(0);
+ }
+};
+
+// Callback function that fills the audio output buffer.
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+ AAudioStream *stream,
+ void *userData,
+ void *audioData,
+ int32_t numFrames
+) {
+ (void) audioData;
+ (void) numFrames;
+ AudioEngine *engine = (struct AudioEngine *)userData;
+ engine->callbackCount++;
+
+ engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
+ engine->framesCalled += numFrames;
+ return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
+ aaudio_direction_t direction) {
+ AAudioStreamBuilder *builder = nullptr;
+ engine->direction = direction;
+
+ // Use an AAudioStreamBuilder to contain requested parameters.
+ aaudio_result_t result = AAudio_createStreamBuilder(&builder);
+ if (result != AAUDIO_OK) {
+ printf("AAudio_createStreamBuilder returned %s",
+ AAudio_convertResultToText(result));
+ return result;
+ }
+
+ // Request stream properties.
+ AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+ AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+ AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
+ AAudioStreamBuilder_setDirection(builder, direction);
+ AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, engine);
+ AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, engine);
+
+ // Create an AAudioStream using the Builder.
+ result = AAudioStreamBuilder_openStream(builder, &engine->stream);
+ AAudioStreamBuilder_delete(builder);
+ builder = nullptr;
+ if (result != AAUDIO_OK) {
+ printf("AAudioStreamBuilder_openStream returned %s",
+ AAudio_convertResultToText(result));
+ }
+
+ // See see what kind of stream we actually opened.
+ int32_t deviceId = AAudioStream_getDeviceId(engine->stream);
+ aaudio_sharing_mode_t actualSharingMode = AAudioStream_getSharingMode(engine->stream);
+ printf("-------- opened: deviceId = %3d, actualSharingMode = %s\n",
+ deviceId,
+ s_sharingModeToText(actualSharingMode));
+
+ return result;
+}
+
+static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
+ aaudio_result_t result = AAUDIO_OK;
+ if (engine->stream != nullptr) {
+ result = AAudioStream_close(engine->stream);
+ if (result != AAUDIO_OK) {
+ printf("AAudioStream_close returned %s\n",
+ AAudio_convertResultToText(result));
+ }
+ engine->stream = nullptr;
+ }
+ return result;
+}
+
+static void s_myRestartStreamProc(void *userData) {
+ printf("%s() - restart in separate thread\n", __func__);
+ AudioEngine *engine = (AudioEngine *) userData;
+ int retriesLeft = 1;
+ aaudio_result_t result;
+ do {
+ s_CloseAudioStream(engine);
+ s_OpenAudioStream(engine, engine->direction);
+ // It is possible for the stream to be disconnected, or stolen between the time
+ // it is opened and when it is started. If that happens then try again.
+ // If it was stolen then it should succeed the second time because there will already be
+ // a SHARED stream, which will not get stolen.
+ result = AAudioStream_requestStart(engine->stream);
+ printf("%s() - AAudioStream_requestStart() returns %s\n", __func__,
+ AAudio_convertResultToText(result));
+ } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+}
+
+static void s_myErrorCallbackProc(
+ AAudioStream * /* stream */,
+ void *userData,
+ aaudio_result_t error) {
+ printf("%s() - error = %s\n", __func__, AAudio_convertResultToText(error));
+ // Handle error on a separate thread.
+ std::thread t(s_myRestartStreamProc, userData);
+ t.detach();
+}
+
+static void s_usage() {
+ printf("test_steal_exclusive [-i]\n");
+ printf(" -i direction INPUT, otherwise OUTPUT\n");
+}
+
+/**
+ * @return 0 is OK, -1 for error
+ */
+static int s_checkEnginePositions(AudioEngine *engine) {
+ if (engine->stream == nullptr) return 0; // race condition with onError procs!
+
+ const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
+ const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
+ const int32_t delta = (int32_t)(framesWritten - framesRead);
+ printf("playing framesRead = %7d, framesWritten = %7d"
+ ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+ (int32_t) framesRead,
+ (int32_t) framesWritten,
+ delta,
+ engine->framesCalled.load(),
+ engine->callbackCount.load()
+ );
+ if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
+ printf("ERROR - delta > capacity\n");
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ (void) argc;
+ (void) argv;
+ struct AudioEngine victim;
+ struct AudioEngine thief;
+ aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+ aaudio_result_t result = AAUDIO_OK;
+ int errorCount = 0;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("Test Stealing an EXCLUSIVE stream V1.0\n");
+ printf("\n");
+
+ for (int i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (arg[0] == '-') {
+ char option = arg[1];
+ switch (option) {
+ case 'i':
+ direction = AAUDIO_DIRECTION_INPUT;
+ break;
+ default:
+ s_usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ } else {
+ s_usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ result = s_OpenAudioStream(&victim, direction);
+ if (result != AAUDIO_OK) {
+ printf("s_OpenAudioStream victim returned %s\n",
+ AAudio_convertResultToText(result));
+ errorCount++;
+ }
+ victim.reset();
+
+ // Start stream.
+ result = AAudioStream_requestStart(victim.stream);
+ printf("AAudioStream_requestStart(VICTIM) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+
+ if (result == AAUDIO_OK) {
+ const int watchLoops = SOLO_DURATION_MSEC / SLEEP_DURATION_MSEC;
+ for (int i = watchLoops; i > 0; i--) {
+ errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+ usleep(SLEEP_DURATION_MSEC * 1000);
+ }
+ }
+
+ printf("Try to start the THIEF stream that may steal the VICTIM MMAP resource -----\n");
+ result = s_OpenAudioStream(&thief, direction);
+ if (result != AAUDIO_OK) {
+ printf("s_OpenAudioStream victim returned %s\n",
+ AAudio_convertResultToText(result));
+ errorCount++;
+ }
+ thief.reset();
+
+ // Start stream.
+ result = AAudioStream_requestStart(thief.stream);
+ printf("AAudioStream_requestStart(THIEF) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+ printf("You might enjoy plugging in a headset now to see what happens...\n");
+
+ if (result == AAUDIO_OK) {
+ const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC;
+ for (int i = watchLoops; i > 0; i--) {
+ printf("victim: ");
+ errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+ printf(" thief: ");
+ errorCount += s_checkEnginePositions(&thief) ? 1 : 0;
+ usleep(SLEEP_DURATION_MSEC * 1000);
+ }
+ }
+
+ // Check for PASS/FAIL
+ aaudio_sharing_mode_t victimSharingMode = AAudioStream_getSharingMode(victim.stream);
+ aaudio_sharing_mode_t thiefSharingMode = AAudioStream_getSharingMode(thief.stream);
+ printf("victimSharingMode = %s, thiefSharingMode = %s, - ",
+ s_sharingModeToText(victimSharingMode),
+ s_sharingModeToText(thiefSharingMode));
+ if ((victimSharingMode == AAUDIO_SHARING_MODE_SHARED)
+ && (thiefSharingMode == AAUDIO_SHARING_MODE_SHARED)) {
+ printf("Both modes are SHARED => PASS\n");
+ } else {
+ errorCount++;
+ printf("Both modes should be SHARED => FAIL!!\n");
+ }
+
+ const int64_t victimFramesRead = AAudioStream_getFramesRead(victim.stream);
+ const int64_t thiefFramesRead = AAudioStream_getFramesRead(thief.stream);
+ printf("victimFramesRead = %d, thiefFramesRead = %d, - ",
+ (int)victimFramesRead, (int)thiefFramesRead);
+ if (victimFramesRead > 0 && thiefFramesRead > 0) {
+ printf("Both streams are running => PASS\n");
+ } else {
+ errorCount++;
+ printf("Both streams should be running => FAIL!!\n");
+ }
+
+ result = AAudioStream_requestStop(victim.stream);
+ printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+ result = AAudioStream_requestStop(thief.stream);
+ printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
+ if (result != AAUDIO_OK) {
+ errorCount++;
+ }
+
+ s_CloseAudioStream(&victim);
+ s_CloseAudioStream(&thief);
+
+ printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
+ printf("test %s\n", errorCount ? "FAILED" : "PASSED");
+
+ return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/media/libaudioclient/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 0bbceef..d4c421a 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -405,7 +405,7 @@
? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
: mCallerName.c_str())
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
.record(); });
@@ -481,7 +481,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
.record(); });
@@ -742,6 +742,8 @@
void *iMemPointer;
audio_track_cblk_t* cblk;
status_t status;
+ std::string flagsAsString;
+ std::string originalFlagsAsString;
if (audioFlinger == 0) {
ALOGE("%s(%d): Could not get audioflinger", __func__, mPortId);
@@ -920,13 +922,15 @@
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
+ InputFlagConverter::toString(mFlags, flagsAsString);
+ InputFlagConverter::toString(mOrigFlags, originalFlagsAsString);
mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD) + std::to_string(mPortId);
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
// the following are immutable (at least until restore)
- .set(AMEDIAMETRICS_PROP_FLAGS, (int32_t)mFlags)
- .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, (int32_t)mOrigFlags)
+ .set(AMEDIAMETRICS_PROP_FLAGS, flagsAsString.c_str())
+ .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, originalFlagsAsString.c_str())
.set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
.set(AMEDIAMETRICS_PROP_TRACKID, mPortId)
.set(AMEDIAMETRICS_PROP_SOURCE, toString(mAttributes.source).c_str())
@@ -1387,7 +1391,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mActive))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.set(AMEDIAMETRICS_PROP_WHERE, from)
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index ca80dc4..011b0fa 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -650,7 +650,7 @@
? AMEDIAMETRICS_PROP_CALLERNAME_VALUE_UNKNOWN
: mCallerName.c_str())
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)status)
.record(); });
@@ -783,7 +783,7 @@
mediametrics::Defer defer([&]() {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
.record();
logBufferSizeUnderruns();
@@ -845,7 +845,7 @@
mediametrics::Defer defer([&]() {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
.record(); });
@@ -886,7 +886,7 @@
mediametrics::Defer defer([&]() {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
.record(); });
@@ -1715,15 +1715,19 @@
// The creation of the audio track by AudioFlinger (in the code above)
// is the first log of the AudioTrack and must be present before
// any AudioTrack client logs will be accepted.
+
+ std::string flagsAsString;
+ OutputFlagConverter::toString(mFlags, flagsAsString);
+ std::string originalFlagsAsString;
+ OutputFlagConverter::toString(mOrigFlags, originalFlagsAsString);
mMetricsId = std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) + std::to_string(mPortId);
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATE)
// the following are immutable
- .set(AMEDIAMETRICS_PROP_FLAGS, (int32_t)mFlags)
- .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, (int32_t)mOrigFlags)
+ .set(AMEDIAMETRICS_PROP_FLAGS, flagsAsString.c_str())
+ .set(AMEDIAMETRICS_PROP_ORIGINALFLAGS, originalFlagsAsString.c_str())
.set(AMEDIAMETRICS_PROP_SESSIONID, (int32_t)mSessionId)
.set(AMEDIAMETRICS_PROP_TRACKID, mPortId) // dup from key
- .set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(mStreamType).c_str())
.set(AMEDIAMETRICS_PROP_CONTENTTYPE, toString(mAttributes.content_type).c_str())
.set(AMEDIAMETRICS_PROP_USAGE, toString(mAttributes.usage).c_str())
.set(AMEDIAMETRICS_PROP_THREADID, (int32_t)output.outputId)
@@ -2440,7 +2444,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(systemTime() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(systemTime() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, stateToString(mState))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.set(AMEDIAMETRICS_PROP_WHERE, from)
diff --git a/media/libaudiofoundation/DeviceDescriptorBase.cpp b/media/libaudiofoundation/DeviceDescriptorBase.cpp
index ef7576e..3dbe37d 100644
--- a/media/libaudiofoundation/DeviceDescriptorBase.cpp
+++ b/media/libaudiofoundation/DeviceDescriptorBase.cpp
@@ -80,9 +80,28 @@
toAudioPortConfig(&port->active_config);
port->id = mId;
port->ext.device.type = mDeviceTypeAddr.mType;
+ port->ext.device.encapsulation_modes = mEncapsulationModes;
+ port->ext.device.encapsulation_metadata_types = mEncapsulationMetadataTypes;
(void)audio_utils_strlcpy_zerofill(port->ext.device.address, mDeviceTypeAddr.getAddress());
}
+status_t DeviceDescriptorBase::setEncapsulationModes(uint32_t encapsulationModes) {
+ if ((encapsulationModes & ~AUDIO_ENCAPSULATION_MODE_ALL_POSITION_BITS) != 0) {
+ return BAD_VALUE;
+ }
+ mEncapsulationModes = encapsulationModes & ~(1 << AUDIO_ENCAPSULATION_MODE_NONE);
+ return NO_ERROR;
+}
+
+status_t DeviceDescriptorBase::setEncapsulationMetadataTypes(uint32_t encapsulationMetadataTypes) {
+ if ((encapsulationMetadataTypes & ~AUDIO_ENCAPSULATION_METADATA_TYPE_ALL_POSITION_BITS) != 0) {
+ return BAD_VALUE;
+ }
+ mEncapsulationMetadataTypes =
+ encapsulationMetadataTypes & ~(1 << AUDIO_ENCAPSULATION_METADATA_TYPE_NONE);
+ return NO_ERROR;
+}
+
void DeviceDescriptorBase::dump(std::string *dst, int spaces, int index,
const char* extraInfo, bool verbose) const
{
@@ -98,6 +117,12 @@
dst->append(base::StringPrintf("%*s- type: %-48s\n",
spaces, "", ::android::toString(mDeviceTypeAddr.mType).c_str()));
+ dst->append(base::StringPrintf(
+ "%*s- supported encapsulation modes: %u", spaces, "", mEncapsulationModes));
+ dst->append(base::StringPrintf(
+ "%*s- supported encapsulation metadata types: %u",
+ spaces, "", mEncapsulationMetadataTypes));
+
if (mDeviceTypeAddr.mAddress.size() != 0) {
dst->append(base::StringPrintf(
"%*s- address: %-32s\n", spaces, "", mDeviceTypeAddr.getAddress()));
@@ -135,6 +160,8 @@
if ((status = AudioPort::writeToParcel(parcel)) != NO_ERROR) return status;
if ((status = AudioPortConfig::writeToParcel(parcel)) != NO_ERROR) return status;
if ((status = parcel->writeParcelable(mDeviceTypeAddr)) != NO_ERROR) return status;
+ if ((status = parcel->writeUint32(mEncapsulationModes)) != NO_ERROR) return status;
+ if ((status = parcel->writeUint32(mEncapsulationMetadataTypes)) != NO_ERROR) return status;
return status;
}
@@ -144,6 +171,8 @@
if ((status = AudioPort::readFromParcel(parcel)) != NO_ERROR) return status;
if ((status = AudioPortConfig::readFromParcel(parcel)) != NO_ERROR) return status;
if ((status = parcel->readParcelable(&mDeviceTypeAddr)) != NO_ERROR) return status;
+ if ((status = parcel->readUint32(&mEncapsulationModes)) != NO_ERROR) return status;
+ if ((status = parcel->readUint32(&mEncapsulationMetadataTypes)) != NO_ERROR) return status;
return status;
}
diff --git a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
index 4c03667..af04721 100644
--- a/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
+++ b/media/libaudiofoundation/include/media/DeviceDescriptorBase.h
@@ -55,6 +55,9 @@
// AudioPort
virtual void toAudioPort(struct audio_port *port) const;
+ status_t setEncapsulationModes(uint32_t encapsulationModes);
+ status_t setEncapsulationMetadataTypes(uint32_t encapsulationMetadataTypes);
+
void dump(std::string *dst, int spaces, int index,
const char* extraInfo = nullptr, bool verbose = true) const;
void log() const;
@@ -67,6 +70,8 @@
protected:
AudioDeviceTypeAddr mDeviceTypeAddr;
+ uint32_t mEncapsulationModes = 0;
+ uint32_t mEncapsulationMetadataTypes = 0;
};
using DeviceDescriptorBaseVector = std::vector<sp<DeviceDescriptorBase>>;
diff --git a/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp b/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp
index 5baa072..068b5d8 100644
--- a/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp
+++ b/media/libaudiofoundation/tests/audiofoundation_parcelable_test.cpp
@@ -131,6 +131,9 @@
desc->setAudioProfiles(getAudioProfileVectorForTest());
desc->applyAudioPortConfig(&TEST_AUDIO_PORT_CONFIG);
desc->setAddress("DeviceDescriptorBaseTestAddress");
+ ASSERT_EQ(desc->setEncapsulationModes(1 << AUDIO_ENCAPSULATION_MODE_HANDLE), NO_ERROR);
+ ASSERT_EQ(desc->setEncapsulationMetadataTypes(
+ AUDIO_ENCAPSULATION_METADATA_TYPE_ALL_POSITION_BITS), NO_ERROR);
ASSERT_EQ(data.writeParcelable(*desc), NO_ERROR);
data.setDataPosition(0);
diff --git a/media/libaudiohal/impl/ConversionHelperHidl.cpp b/media/libaudiohal/impl/ConversionHelperHidl.cpp
index f29b0f3..ebed5fd 100644
--- a/media/libaudiohal/impl/ConversionHelperHidl.cpp
+++ b/media/libaudiohal/impl/ConversionHelperHidl.cpp
@@ -41,16 +41,25 @@
bool keepFormatValue = halKeys.size() == 2 &&
(halKeys.get(String8(AudioParameter::keyStreamSupportedChannels), value) == NO_ERROR ||
halKeys.get(String8(AudioParameter::keyStreamSupportedSamplingRates), value) == NO_ERROR);
+ // When querying encapsulation capabilities, "keyRouting=<value>" pair is used to identify
+ // the device. We need to transform it into a single key string so that it is carried over to
+ // the legacy HAL via HIDL.
+ bool keepRoutingValue =
+ halKeys.get(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES),
+ value) == NO_ERROR ||
+ halKeys.get(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES),
+ value) == NO_ERROR;
for (size_t i = 0; i < halKeys.size(); ++i) {
String8 key;
status_t status = halKeys.getAt(i, key);
if (status != OK) return status;
- if (keepFormatValue && key == AudioParameter::keyFormat) {
- AudioParameter formatParam;
+ if ((keepFormatValue && key == AudioParameter::keyFormat) ||
+ (keepRoutingValue && key == AudioParameter::keyRouting)) {
+ AudioParameter keepValueParam;
halKeys.getAt(i, key, value);
- formatParam.add(key, value);
- key = formatParam.toString();
+ keepValueParam.add(key, value);
+ key = keepValueParam.toString();
}
(*hidlKeys)[i] = key.string();
}
diff --git a/media/libmedia/IMediaSource.cpp b/media/libmedia/IMediaSource.cpp
index b18571f..36cd73b 100644
--- a/media/libmedia/IMediaSource.cpp
+++ b/media/libmedia/IMediaSource.cpp
@@ -107,7 +107,7 @@
data.writeInterfaceToken(BpMediaSource::getInterfaceDescriptor());
status_t ret = remote()->transact(GETFORMAT, data, &reply);
if (ret == NO_ERROR) {
- AutoMutex _l(mLock);
+ AutoMutex _l(mBpLock);
mMetaData = MetaData::createFromParcel(reply);
return mMetaData;
}
@@ -224,7 +224,7 @@
// XXX: could we use this for caching, or does metadata change on the fly?
sp<MetaData> mMetaData;
// ensure synchronize access to mMetaData
- Mutex mLock;
+ Mutex mBpLock;
// Cache all IMemory objects received from MediaExtractor.
// We gc IMemory objects that are no longer active (referenced by a MediaBuffer).
@@ -301,6 +301,7 @@
CHECK_INTERFACE(IMediaSource, data, reply);
mGroup->signalBufferReturned(nullptr);
status_t status = stop();
+ AutoMutex _l(mBnLock);
mIndexCache.reset();
mBuffersSinceStop = 0;
return status;
@@ -340,6 +341,7 @@
&& len == sizeof(opts)
&& data.read((void *)&opts, len) == NO_ERROR;
+ AutoMutex _l(mBnLock);
mGroup->signalBufferReturned(nullptr);
mIndexCache.gc();
size_t inlineTransferSize = 0;
diff --git a/media/libmedia/include/media/IMediaSource.h b/media/libmedia/include/media/IMediaSource.h
index f3fa39b..84310f0 100644
--- a/media/libmedia/include/media/IMediaSource.h
+++ b/media/libmedia/include/media/IMediaSource.h
@@ -135,6 +135,7 @@
private:
uint32_t mBuffersSinceStop; // Buffer tracking variable
+ Mutex mBnLock; // to guard readMultiple against concurrent access to the buffer cache
std::unique_ptr<MediaBufferGroup> mGroup;
diff --git a/media/libmediahelper/AudioParameter.cpp b/media/libmediahelper/AudioParameter.cpp
index 9f34035..fc8306c 100644
--- a/media/libmediahelper/AudioParameter.cpp
+++ b/media/libmediahelper/AudioParameter.cpp
@@ -53,6 +53,10 @@
const char * const AudioParameter::valueListSeparator = AUDIO_PARAMETER_VALUE_LIST_SEPARATOR;
const char * const AudioParameter::keyReconfigA2dp = AUDIO_PARAMETER_RECONFIG_A2DP;
const char * const AudioParameter::keyReconfigA2dpSupported = AUDIO_PARAMETER_A2DP_RECONFIG_SUPPORTED;
+// const char * const AudioParameter::keyDeviceSupportedEncapsulationModes =
+// AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES;
+// const char * const AudioParameter::keyDeviceSupportedEncapsulationMetadataTypes =
+// AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES;
AudioParameter::AudioParameter(const String8& keyValuePairs)
{
diff --git a/media/libmediahelper/include/media/AudioParameter.h b/media/libmediahelper/include/media/AudioParameter.h
index 3c190f2..66d8dfb 100644
--- a/media/libmediahelper/include/media/AudioParameter.h
+++ b/media/libmediahelper/include/media/AudioParameter.h
@@ -92,6 +92,18 @@
static const char * const keyReconfigA2dp;
static const char * const keyReconfigA2dpSupported;
+ // For querying device supported encapsulation capabilities. All returned values are integer,
+ // which are bit fields composed from using encapsulation capability values as position bits.
+ // Encapsulation capability values are defined in audio_encapsulation_mode_t and
+ // audio_encapsulation_metadata_type_t. For instance, if the supported encapsulation mode is
+ // AUDIO_ENCAPSULATION_MODE_ELEMENTARY_STREAM, the returned value is
+ // "supEncapsulationModes=1 << AUDIO_ENCAPSULATION_MODE_HANDLE".
+ // When querying device supported encapsulation capabilities, the key should use with device
+ // type and address so that it is able to identify the device. The device will be a key. The
+ // device type will be the value of key AUDIO_PARAMETER_STREAM_ROUTING.
+ // static const char * const keyDeviceSupportedEncapsulationModes;
+ // static const char * const keyDeviceSupportedEncapsulationMetadataTypes;
+
String8 toString() const { return toStringImpl(true); }
String8 keysToString() const { return toStringImpl(false); }
diff --git a/media/libmediametrics/include/MediaMetricsConstants.h b/media/libmediametrics/include/MediaMetricsConstants.h
index ec0e133..b916a78 100644
--- a/media/libmediametrics/include/MediaMetricsConstants.h
+++ b/media/libmediametrics/include/MediaMetricsConstants.h
@@ -37,6 +37,9 @@
// They must be appended with another value to make a key.
#define AMEDIAMETRICS_KEY_PREFIX_AUDIO "audio."
+// Device related key prefix.
+#define AMEDIAMETRICS_KEY_PREFIX_AUDIO_DEVICE AMEDIAMETRICS_KEY_PREFIX_AUDIO "device."
+
// The AudioMmap key appends the "trackId" to the prefix.
// This is the AudioFlinger equivalent of the AAudio Stream.
// TODO: unify with AMEDIAMETRICS_KEY_PREFIX_AUDIO_STREAM
@@ -92,6 +95,7 @@
#define AMEDIAMETRICS_PROP_SUFFIX_CHAR_DUPLICATES_ALLOWED '#'
#define AMEDIAMETRICS_PROP_ALLOWUID "_allowUid" // int32_t, allow client uid to post
+#define AMEDIAMETRICS_PROP_AUDIOMODE "audioMode" // string (audio.flinger)
#define AMEDIAMETRICS_PROP_AUXEFFECTID "auxEffectId" // int32 (AudioTrack)
#define AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES "bufferSizeFrames" // int32
#define AMEDIAMETRICS_PROP_BUFFERCAPACITYFRAMES "bufferCapacityFrames" // int32
@@ -112,6 +116,7 @@
#define AMEDIAMETRICS_PROP_DURATIONNS "durationNs" // int64 duration time span
#define AMEDIAMETRICS_PROP_ENCODING "encoding" // string value of format
#define AMEDIAMETRICS_PROP_EVENT "event#" // string value (often func name)
+#define AMEDIAMETRICS_PROP_EXECUTIONTIMENS "executionTimeNs" // time to execute the event
// TODO: fix inconsistency in flags: AudioRecord / AudioTrack int32, AudioThread string
#define AMEDIAMETRICS_PROP_FLAGS "flags"
@@ -146,6 +151,7 @@
#define AMEDIAMETRICS_PROP_UNDERRUN "underrun" // int32
#define AMEDIAMETRICS_PROP_UNDERRUNFRAMES "underrunFrames" // int64_t from Thread
#define AMEDIAMETRICS_PROP_USAGE "usage" // string attributes (ATrack)
+#define AMEDIAMETRICS_PROP_VOICEVOLUME "voiceVolume" // double (audio.flinger)
#define AMEDIAMETRICS_PROP_VOLUME_LEFT "volume.left" // double (AudioTrack)
#define AMEDIAMETRICS_PROP_VOLUME_RIGHT "volume.right" // double (AudioTrack)
#define AMEDIAMETRICS_PROP_WHERE "where" // string value
@@ -170,7 +176,9 @@
#define AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE "pause" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS "readParameters" // Thread
#define AMEDIAMETRICS_PROP_EVENT_VALUE_RESTORE "restore"
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE "setMode" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETPLAYBACKPARAM "setPlaybackParam" // AudioTrack
+#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME "setVoiceVolume" // AudioFlinger
#define AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOLUME "setVolume" // AudioTrack
#define AMEDIAMETRICS_PROP_EVENT_VALUE_START "start" // AudioTrack, AudioRecord
#define AMEDIAMETRICS_PROP_EVENT_VALUE_STOP "stop" // AudioTrack, AudioRecord
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 24afd43..dc144b2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -116,6 +116,7 @@
updateMetrics("destructor");
logMetrics("destructor");
+ Mutex::Autolock autoLock(mMetricsLock);
if (mMetricsItem != NULL) {
delete mMetricsItem;
mMetricsItem = NULL;
@@ -129,6 +130,8 @@
status_t NuPlayerDriver::setUID(uid_t uid) {
mPlayer->setUID(uid);
mClientUid = uid;
+
+ Mutex::Autolock autoLock(mMetricsLock);
if (mMetricsItem) {
mMetricsItem->setUid(mClientUid);
}
@@ -542,10 +545,50 @@
}
ALOGV("updateMetrics(%p) from %s at state %d", this, where, mState);
- // gather the final stats for this record
+ // avoid nested locks by gathering our data outside of the metrics lock.
+
+ // final track statistics for this record
Vector<sp<AMessage>> trackStats;
mPlayer->getStats(&trackStats);
+ // getDuration() uses mLock
+ int duration_ms = -1;
+ getDuration(&duration_ms);
+
+ mPlayer->updateInternalTimers();
+
+ int64_t playingTimeUs;
+ int64_t rebufferingTimeUs;
+ int32_t rebufferingEvents;
+ bool rebufferingAtExit;
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ playingTimeUs = mPlayingTimeUs;
+ rebufferingTimeUs = mRebufferingTimeUs;
+ rebufferingEvents = mRebufferingEvents;
+ rebufferingAtExit = mRebufferingAtExit;
+ }
+
+ // finish the rest of the gathering under our mutex to avoid metrics races.
+ // some of the fields we read are updated under mLock.
+ Mutex::Autolock autoLock(mMetricsLock);
+
+ if (mMetricsItem == NULL) {
+ return;
+ }
+
+ mMetricsItem->setInt64(kPlayerDuration, duration_ms);
+ mMetricsItem->setInt64(kPlayerPlaying, (playingTimeUs+500)/1000 );
+
+ if (rebufferingEvents != 0) {
+ mMetricsItem->setInt64(kPlayerRebuffering, (rebufferingTimeUs+500)/1000 );
+ mMetricsItem->setInt32(kPlayerRebufferingCount, rebufferingEvents);
+ mMetricsItem->setInt32(kPlayerRebufferingAtExit, rebufferingAtExit);
+ }
+
+ mMetricsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
+
if (trackStats.size() > 0) {
for (size_t i = 0; i < trackStats.size(); ++i) {
const sp<AMessage> &stats = trackStats.itemAt(i);
@@ -590,26 +633,6 @@
}
}
}
-
- // always provide duration and playing time, even if they have 0/unknown values.
-
- // getDuration() uses mLock for mutex -- careful where we use it.
- int duration_ms = -1;
- getDuration(&duration_ms);
- mMetricsItem->setInt64(kPlayerDuration, duration_ms);
-
- mPlayer->updateInternalTimers();
-
- mMetricsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
-
- if (mRebufferingEvents != 0) {
- mMetricsItem->setInt64(kPlayerRebuffering, (mRebufferingTimeUs+500)/1000 );
- mMetricsItem->setInt32(kPlayerRebufferingCount, mRebufferingEvents);
- mMetricsItem->setInt32(kPlayerRebufferingAtExit, mRebufferingAtExit);
-
- }
-
- mMetricsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
}
@@ -619,6 +642,9 @@
}
ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
+ // ensure mMetricsItem stability while we write it out
+ Mutex::Autolock autoLock(mMetricsLock);
+
if (mMetricsItem == NULL || mMetricsItem->isEnabled() == false) {
return;
}
@@ -777,11 +803,16 @@
status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
- if (key == FOURCC('m','t','r','X') && mMetricsItem != NULL) {
+ if (key == FOURCC('m','t','r','X')) {
// mtrX -- a play on 'metrics' (not matrix)
// gather current info all together, parcel it, and send it back
updateMetrics("api");
- mMetricsItem->writeToParcel(reply);
+
+ // ensure mMetricsItem stability while writing to parcel
+ Mutex::Autolock autoLock(mMetricsLock);
+ if (mMetricsItem != NULL) {
+ mMetricsItem->writeToParcel(reply);
+ }
return OK;
}
@@ -1005,12 +1036,15 @@
// when we have an error, add it to the analytics for this playback.
// ext1 is our primary 'error type' value. Only add ext2 when non-zero.
// [test against msg is due to fall through from previous switch value]
- if (msg == MEDIA_ERROR && mMetricsItem != NULL) {
- mMetricsItem->setInt32(kPlayerError, ext1);
- if (ext2 != 0) {
- mMetricsItem->setInt32(kPlayerErrorCode, ext2);
+ if (msg == MEDIA_ERROR) {
+ Mutex::Autolock autoLock(mMetricsLock);
+ if (mMetricsItem != NULL) {
+ mMetricsItem->setInt32(kPlayerError, ext1);
+ if (ext2 != 0) {
+ mMetricsItem->setInt32(kPlayerErrorCode, ext2);
+ }
+ mMetricsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
}
- mMetricsItem->setCString(kPlayerErrorState, stateString(mState).c_str());
}
mAtEOS = true;
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 7001f4a..f4b1968 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -142,6 +142,7 @@
uint32_t mPlayerFlags;
mediametrics::Item *mMetricsItem;
+ mutable Mutex mMetricsLock;
uid_t mClientUid;
bool mAtEOS;
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 11f2f38..e748b01 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -2641,6 +2641,7 @@
}
mResourceManagerProxy->removeClient();
+ mReleaseSurface.reset();
if (mReplyID != nullptr) {
(new AMessage)->postReply(mReplyID);
diff --git a/media/libstagefright/codecs/amrnb/common/Android.bp b/media/libstagefright/codecs/amrnb/common/Android.bp
index bcf63d5..59a791d 100644
--- a/media/libstagefright/codecs/amrnb/common/Android.bp
+++ b/media/libstagefright/codecs/amrnb/common/Android.bp
@@ -2,6 +2,7 @@
name: "libstagefright_amrnb_common",
vendor_available: true,
host_supported: true,
+ min_sdk_version: "29",
srcs: [
"src/add.cpp",
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index 3381d2e..b8e00b3 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -2,6 +2,7 @@
name: "libstagefright_amrnbdec",
vendor_available: true,
host_supported: true,
+ min_sdk_version: "29",
srcs: [
"src/a_refl.cpp",
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
index 34dd011..cdfc03a 100644
--- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -20,9 +20,6 @@
#include "SoftAMR.h"
-#include "gsmamr_dec.h"
-#include "pvamrwbdecoder.h"
-
#include <media/stagefright/foundation/ADebug.h>
namespace android {
@@ -470,11 +467,10 @@
memset(outPtr, 0, kNumSamplesPerFrameWB * sizeof(int16_t));
} else if (mode < 9) {
int16 frameType;
- RX_State_wb rx_state;
mime_unsorting(
const_cast<uint8_t *>(&inputPtr[1]),
mInputSampleBuffer,
- &frameType, &mode, 1, &rx_state);
+ &frameType, &mode, 1, &mRxState);
int16_t numSamplesOutput;
pvDecoder_AmrWb(
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
index 869b81d..d5aaed3 100644
--- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.h
@@ -19,6 +19,8 @@
#define SOFT_AMR_H_
#include <media/stagefright/omx/SimpleSoftOMXComponent.h>
+#include "gsmamr_dec.h"
+#include "pvamrwbdecoder.h"
namespace android {
@@ -60,6 +62,7 @@
void *mState;
void *mDecoderBuf;
int16_t *mDecoderCookie;
+ RX_State_wb mRxState{};
size_t mInputBufferCount;
int64_t mAnchorTimeUs;
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index 438ed04..73a1d4b 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_amrnbenc",
vendor_available: true,
+ min_sdk_version: "29",
srcs: [
"src/amrencode.cpp",
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index d8cb568..204cbe3 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -2,6 +2,7 @@
name: "libstagefright_amrwbdec",
vendor_available: true,
host_supported: true,
+ min_sdk_version: "29",
srcs: [
"src/agc2_amr_wb.cpp",
diff --git a/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
index 6dc0270..592a6ec 100644
--- a/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
+++ b/media/libstagefright/codecs/amrwb/fuzzer/amrwb_dec_fuzzer.cpp
@@ -65,6 +65,7 @@
}
void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ RX_State_wb rx_state{};
while (size > 0) {
uint8_t modeByte = *data;
bool quality = modeByte & 0x01;
@@ -81,7 +82,6 @@
memcpy(inputBuf, data, minSize);
int16 frameMode = mode;
int16 frameType;
- RX_State_wb rx_state;
mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, quality, &rx_state);
int16_t numSamplesOutput;
diff --git a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
index 2aad81b..7221b92 100644
--- a/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
+++ b/media/libstagefright/codecs/amrwb/test/AmrwbDecoderTest.cpp
@@ -74,6 +74,7 @@
uint8_t inputBuf[kInputBufferSize];
int16_t inputSampleBuf[kMaxSourceDataUnitSize];
int16_t outputBuf[kOutputBufferSize];
+ RX_State_wb rx_state{};
while (frameCount > 0) {
uint8_t modeByte;
@@ -98,7 +99,6 @@
int16 frameMode = mode;
int16 frameType;
- RX_State_wb rx_state;
mime_unsorting(inputBuf, inputSampleBuf, &frameType, &frameMode, 1, &rx_state);
int16_t numSamplesOutput;
diff --git a/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp b/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
index b04bafd..1bc90e8 100644
--- a/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
+++ b/media/libstagefright/codecs/amrwb/test/amrwbdec_test.cpp
@@ -110,6 +110,7 @@
// Decode loop.
int retVal = EXIT_SUCCESS;
+ RX_State_wb rx_state{};
while (1) {
// Read mode.
uint8_t modeByte;
@@ -134,7 +135,6 @@
if (bytesRead != frameSize) break;
int16 frameType, frameMode;
- RX_State_wb rx_state;
frameMode = mode;
mime_unsorting(
(uint8_t *)inputBuf,
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 084be0a..64f302c 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_amrwbenc",
vendor_available: true,
+ min_sdk_version: "29",
srcs: [
"src/autocorr.c",
diff --git a/media/libstagefright/codecs/common/Android.bp b/media/libstagefright/codecs/common/Android.bp
index c5a076a..260a60a 100644
--- a/media/libstagefright/codecs/common/Android.bp
+++ b/media/libstagefright/codecs/common/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libstagefright_enc_common",
vendor_available: true,
+ min_sdk_version: "29",
srcs: ["cmnMemory.c"],
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
index 679b091..a11f55e 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
@@ -409,7 +409,9 @@
if (!BitstreamRead1Bits(stream)) return PV_FAIL;
/* video_object_layer_width (13 bits) */
- video->displayWidth = video->width = (int) BitstreamReadBits16(stream, 13);
+ tmpvar = BitstreamReadBits16(stream, 13);
+ if (!tmpvar) return PV_FAIL;
+ video->displayWidth = video->width = tmpvar;
/* round up to a multiple of MB_SIZE. 08/09/2000 */
video->width = (video->width + 15) & -16;
@@ -419,7 +421,9 @@
if (!BitstreamRead1Bits(stream)) return PV_FAIL;
/* video_object_layer_height (13 bits) */
- video->displayHeight = video->height = (int) BitstreamReadBits16(stream, 13);
+ tmpvar = BitstreamReadBits16(stream, 13);
+ if (!tmpvar) return PV_FAIL;
+ video->displayHeight = video->height = tmpvar;
/* round up to a multiple of MB_SIZE. 08/09/2000 */
video->height = (video->height + 15) & -16;
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index b630524..96106f1 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libstagefright_mp3dec",
vendor_available: true,
+ min_sdk_version: "29",
srcs: [
"src/pvmp3_normalize.cpp",
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index d65a663..32b2075 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libstagefright_flacdec",
vendor_available: true,
+ min_sdk_version: "29",
srcs: [
"FLACDecoder.cpp",
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index e0324e3..4324c45 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -615,7 +615,7 @@
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
}
- err = parseCipherInfo(line, &itemMeta, mBaseURI);
+ err = parseCipherInfo(line, &itemMeta);
} else if (line.startsWith("#EXT-X-ENDLIST")) {
mIsComplete = true;
} else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:EVENT")) {
@@ -936,7 +936,7 @@
// static
status_t M3UParser::parseCipherInfo(
- const AString &line, sp<AMessage> *meta, const AString &baseURI) {
+ const AString &line, sp<AMessage> *meta) {
ssize_t colonPos = line.find(":");
if (colonPos < 0) {
@@ -985,13 +985,9 @@
val = tmp;
}
- AString absURI;
- if (MakeURL(baseURI.c_str(), val.c_str(), &absURI)) {
- val = absURI;
- } else {
- ALOGE("failed to make absolute url for %s.",
- uriDebugString(baseURI).c_str());
- }
+ // To save space, we only store the partial Uri here
+ // The full Uri will be constructed from this plus
+ // the base Uri as needed by PlaylistFetcher
}
key.insert(AString("cipher-"), 0);
@@ -1003,6 +999,14 @@
return OK;
}
+AString M3UParser::getFullCipherUri(const AString &partial) {
+ AString full;
+ if (MakeURL(mBaseURI.c_str(), partial.c_str(), &full)) {
+ return full;
+ }
+ return AString("");
+}
+
// static
status_t M3UParser::parseByteRange(
const AString &line, uint64_t curOffset,
diff --git a/media/libstagefright/httplive/M3UParser.h b/media/libstagefright/httplive/M3UParser.h
index c85335a..9f7a66a 100644
--- a/media/libstagefright/httplive/M3UParser.h
+++ b/media/libstagefright/httplive/M3UParser.h
@@ -55,6 +55,8 @@
bool getTypeURI(size_t index, const char *key, AString *uri) const;
bool hasType(size_t index, const char *key) const;
+ AString getFullCipherUri(const AString &partial);
+
protected:
virtual ~M3UParser();
@@ -99,7 +101,7 @@
const AString &line, sp<AMessage> *meta) const;
static status_t parseCipherInfo(
- const AString &line, sp<AMessage> *meta, const AString &baseURI);
+ const AString &line, sp<AMessage> *meta);
static status_t parseByteRange(
const AString &line, uint64_t curOffset,
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index fdcde29..b23aa8a 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -345,6 +345,7 @@
ALOGE("Missing key uri");
return ERROR_MALFORMED;
}
+ keyURI = mPlaylist->getFullCipherUri(keyURI);
ssize_t index = mAESKeyForURI.indexOfKey(keyURI);
diff --git a/media/libstagefright/mpeg2ts/Android.bp b/media/libstagefright/mpeg2ts/Android.bp
index 42afea3..fbb2d0c 100644
--- a/media/libstagefright/mpeg2ts/Android.bp
+++ b/media/libstagefright/mpeg2ts/Android.bp
@@ -46,4 +46,6 @@
whole_static_libs: [
"libstagefright_metadatautils",
],
+
+ min_sdk_version: "29",
}
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 657144c..4bb21fa 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -1153,7 +1153,7 @@
}
const RangeInfo &info = *mRangeInfos.begin();
- if (mBuffer->size() < info.mLength) {
+ if (info.mLength == 0 || mBuffer->size() < info.mLength) {
return NULL;
}
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 8c5ca6e..094b1f5 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -16,6 +16,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "OMXMaster"
+#include <android-base/properties.h>
#include <utils/Log.h>
#include <media/stagefright/omx/OMXMaster.h>
@@ -67,6 +68,10 @@
}
void OMXMaster::addPlugin(const char *libname) {
+ if (::android::base::GetIntProperty("vendor.media.omx", int64_t(1)) == 0) {
+ return;
+ }
+
void *libHandle = android_load_sphal_library(libname, RTLD_NOW);
if (libHandle == NULL) {
diff --git a/media/mediaserver/mediaserver.rc b/media/mediaserver/mediaserver.rc
index ecb75a9..05373c9 100644
--- a/media/mediaserver/mediaserver.rc
+++ b/media/mediaserver/mediaserver.rc
@@ -6,4 +6,4 @@
user media
group audio camera inet net_bt net_bt_admin net_bw_acct drmrpc mediadrm
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index de7ae40..f014209 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1176,6 +1176,10 @@
mPlaybackThreads.valueAt(i)->setMode(mode);
}
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE)
+ .set(AMEDIAMETRICS_PROP_AUDIOMODE, toString(mode))
+ .record();
return ret;
}
@@ -1741,6 +1745,10 @@
ret = dev->setVoiceVolume(value);
mHardwareStatus = AUDIO_HW_IDLE;
+ mediametrics::LogItem(mMetricsId)
+ .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME)
+ .set(AMEDIAMETRICS_PROP_VOICEVOLUME, (double)value)
+ .record();
return ret;
}
diff --git a/services/audioflinger/DeviceEffectManager.cpp b/services/audioflinger/DeviceEffectManager.cpp
index a3c3b84..5ff7215 100644
--- a/services/audioflinger/DeviceEffectManager.cpp
+++ b/services/audioflinger/DeviceEffectManager.cpp
@@ -144,7 +144,8 @@
write(fd, result.string(), result.size());
}
- write(fd, "\nDevice Effects:\n", sizeof("\nDevice Effects:\n"));
+ String8 heading("\nDevice Effects:\n");
+ write(fd, heading.string(), heading.size());
for (const auto& iter : mDeviceEffects) {
String8 outStr;
outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "",
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 55f2952..3dfeb83 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -2812,7 +2812,7 @@
if (t == nullptr) {
return false;
}
- return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::MMAP;
+ return t->isOffloadOrMmap();
}
uint32_t AudioFlinger::EffectChain::EffectCallback::sampleRate() const {
diff --git a/services/audioflinger/ThreadMetrics.h b/services/audioflinger/ThreadMetrics.h
index 7989de1..6526655 100644
--- a/services/audioflinger/ThreadMetrics.h
+++ b/services/audioflinger/ThreadMetrics.h
@@ -58,9 +58,11 @@
// 2) We come out of standby
void logBeginInterval() {
std::lock_guard l(mLock);
- if (mDevices != mCreatePatchDevices) {
+ // The devices we look for change depend on whether the Thread is input or output.
+ const std::string& patchDevices = mIsOut ? mCreatePatchOutDevices : mCreatePatchInDevices;
+ if (mDevices != patchDevices) {
deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
- mDevices = mCreatePatchDevices; // set after endAudioIntervalGroup
+ mDevices = patchDevices; // set after endAudioIntervalGroup
resetIntervalGroupMetrics();
deliverDeviceMetrics(
AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, mDevices.c_str());
@@ -80,12 +82,14 @@
.record();
}
- void logCreatePatch(const std::string& devices) {
+ void logCreatePatch(const std::string& inDevices, const std::string& outDevices) {
std::lock_guard l(mLock);
- mCreatePatchDevices = devices;
+ mCreatePatchInDevices = inDevices;
+ mCreatePatchOutDevices = outDevices;
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
- .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, devices)
+ .set(AMEDIAMETRICS_PROP_INPUTDEVICES, inDevices)
+ .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, outDevices)
.record();
}
@@ -115,11 +119,13 @@
mDeviceLatencyMs.add(latencyMs);
}
- // TODO: further implement this.
- void logUnderrunFrames(size_t count, size_t frames) {
+ void logUnderrunFrames(size_t frames) {
std::lock_guard l(mLock);
- mUnderrunCount = count;
- mUnderrunFrames = frames;
+ if (mLastUnderrun == false && frames > 0) {
+ ++mUnderrunCount; // count non-continguous underrun sequences.
+ }
+ mLastUnderrun = (frames > 0);
+ mUnderrunFrames += frames;
}
const std::string& getMetricsId() const {
@@ -164,6 +170,7 @@
mDeviceLatencyMs.reset();
+ mLastUnderrun = false;
mUnderrunCount = 0;
mUnderrunFrames = 0;
}
@@ -174,8 +181,9 @@
mutable std::mutex mLock;
// Devices in the interval group.
- std::string mDevices GUARDED_BY(mLock);
- std::string mCreatePatchDevices GUARDED_BY(mLock);
+ std::string mDevices GUARDED_BY(mLock); // last input or output devices based on mIsOut.
+ std::string mCreatePatchInDevices GUARDED_BY(mLock);
+ std::string mCreatePatchOutDevices GUARDED_BY(mLock);
// Number of intervals and playing time
int32_t mIntervalCount GUARDED_BY(mLock) = 0;
@@ -187,8 +195,9 @@
audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
// underrun count and frames
- int64_t mUnderrunCount GUARDED_BY(mLock) = 0;
- int64_t mUnderrunFrames GUARDED_BY(mLock) = 0;
+ bool mLastUnderrun GUARDED_BY(mLock) = false; // checks consecutive underruns
+ int64_t mUnderrunCount GUARDED_BY(mLock) = 0; // number of consecutive underruns
+ int64_t mUnderrunFrames GUARDED_BY(mLock) = 0; // total estimated frames underrun
};
} // namespace android
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 594baf8..4a4899f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -484,8 +484,10 @@
return "RECORD";
case OFFLOAD:
return "OFFLOAD";
- case MMAP:
- return "MMAP";
+ case MMAP_PLAYBACK:
+ return "MMAP_PLAYBACK";
+ case MMAP_CAPTURE:
+ return "MMAP_CAPTURE";
default:
return "unknown";
}
@@ -967,8 +969,10 @@
return String16("AudioIn");
case OFFLOAD:
return String16("AudioOffload");
- case MMAP:
- return String16("Mmap");
+ case MMAP_PLAYBACK:
+ return String16("MmapPlayback");
+ case MMAP_CAPTURE:
+ return String16("MmapCapture");
default:
ALOG_ASSERT(false);
return String16("AudioUnknown");
@@ -1477,7 +1481,7 @@
}
void AudioFlinger::ThreadBase::onEffectEnable(const sp<EffectModule>& effect) {
- if (mType == OFFLOAD || mType == MMAP) {
+ if (isOffloadOrMmap()) {
Mutex::Autolock _l(mLock);
broadcast_l();
}
@@ -1493,7 +1497,7 @@
}
void AudioFlinger::ThreadBase::onEffectDisable() {
- if (mType == OFFLOAD || mType == MMAP) {
+ if (isOffloadOrMmap()) {
Mutex::Autolock _l(mLock);
broadcast_l();
}
@@ -2088,6 +2092,12 @@
outputFlags = (audio_output_flags_t)(outputFlags | AUDIO_OUTPUT_FLAG_FAST);
}
+ // Set DIRECT flag if current thread is DirectOutputThread. This can happen when the playback is
+ // rerouted to direct output thread by dynamic audio policy.
+ if (mType == DIRECT) {
+ *flags = (audio_output_flags_t)(*flags | AUDIO_OUTPUT_FLAG_DIRECT);
+ }
+
// Check if requested flags are compatible with output stream flags
if ((*flags & outputFlags) != *flags) {
ALOGW("createTrack_l(): mismatch between requested flags (%08x) and output flags (%08x)",
@@ -4260,7 +4270,7 @@
const std::string patchSinksAsString = patchSinksToString(patch);
mThreadMetrics.logEndInterval();
- mThreadMetrics.logCreatePatch(patchSinksAsString);
+ mThreadMetrics.logCreatePatch(/* inDevices */ {}, patchSinksAsString);
mThreadMetrics.logBeginInterval();
// also dispatch to active AudioTracks for MediaMetrics
for (const auto &track : mActiveTracks) {
@@ -4809,19 +4819,24 @@
// DeferredOperations handles statistics after setting mixerStatus.
class DeferredOperations {
public:
- explicit DeferredOperations(mixer_state *mixerStatus)
- : mMixerStatus(mixerStatus) {}
+ DeferredOperations(mixer_state *mixerStatus, ThreadMetrics *threadMetrics)
+ : mMixerStatus(mixerStatus)
+ , mThreadMetrics(threadMetrics) {}
// when leaving scope, tally frames properly.
~DeferredOperations() {
// Tally underrun frames only if we are actually mixing (MIXER_TRACKS_READY)
// because that is when the underrun occurs.
// We do not distinguish between FastTracks and NormalTracks here.
+ size_t maxUnderrunFrames = 0;
if (*mMixerStatus == MIXER_TRACKS_READY && mUnderrunFrames.size() > 0) {
for (const auto &underrun : mUnderrunFrames) {
underrun.first->tallyUnderrunFrames(underrun.second);
+ maxUnderrunFrames = max(underrun.second, maxUnderrunFrames);
}
}
+ // send the max underrun frames for this mixer period
+ mThreadMetrics->logUnderrunFrames(maxUnderrunFrames);
}
// tallyUnderrunFrames() is called to update the track counters
@@ -4833,8 +4848,9 @@
private:
const mixer_state * const mMixerStatus;
+ ThreadMetrics * const mThreadMetrics;
std::vector<std::pair<sp<Track>, size_t>> mUnderrunFrames;
- } deferredOperations(&mixerStatus);
+ } deferredOperations(&mixerStatus, &mThreadMetrics);
// implicit nested scope for variable capture
bool noFastHapticTrack = true;
@@ -8428,6 +8444,17 @@
// AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints.
// But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks?
+
+ audio_input_flags_t flags = mInput->flags;
+ mediametrics::LogItem item(mThreadMetrics.getMetricsId());
+ item.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_READPARAMETERS)
+ .set(AMEDIAMETRICS_PROP_ENCODING, formatToString(mFormat).c_str())
+ .set(AMEDIAMETRICS_PROP_FLAGS, toString(flags).c_str())
+ .set(AMEDIAMETRICS_PROP_SAMPLERATE, (int32_t)mSampleRate)
+ .set(AMEDIAMETRICS_PROP_CHANNELMASK, (int32_t)mChannelMask)
+ .set(AMEDIAMETRICS_PROP_CHANNELCOUNT, (int32_t)mChannelCount)
+ .set(AMEDIAMETRICS_PROP_FRAMECOUNT, (int32_t)mFrameCount)
+ .record();
}
uint32_t AudioFlinger::RecordThread::getInputFramesLost()
@@ -8558,7 +8585,7 @@
const std::string pathSourcesAsString = patchSourcesToString(patch);
mThreadMetrics.logEndInterval();
- mThreadMetrics.logCreatePatch(pathSourcesAsString);
+ mThreadMetrics.logCreatePatch(pathSourcesAsString, /* outDevices */ {});
mThreadMetrics.logBeginInterval();
// also dispatch to active AudioRecords
for (const auto &track : mActiveTracks) {
@@ -8672,7 +8699,7 @@
AudioFlinger::MmapThread::MmapThread(
const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
AudioHwDevice *hwDev, sp<StreamHalInterface> stream, bool systemReady, bool isOut)
- : ThreadBase(audioFlinger, id, MMAP, systemReady, isOut),
+ : ThreadBase(audioFlinger, id, (isOut ? MMAP_PLAYBACK : MMAP_CAPTURE), systemReady, isOut),
mSessionId(AUDIO_SESSION_NONE),
mPortId(AUDIO_PORT_HANDLE_NONE),
mHalStream(stream), mHalDevice(hwDev->hwDevice()), mAudioHwDev(hwDev),
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 5b8c081..c1ac2e4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -30,7 +30,8 @@
DUPLICATING, // Thread class is DuplicatingThread
RECORD, // Thread class is RecordThread
OFFLOAD, // Thread class is OffloadThread
- MMAP // control thread for MMAP stream
+ MMAP_PLAYBACK, // Thread class for MMAP playback stream
+ MMAP_CAPTURE, // Thread class for MMAP capture stream
// If you add any values here, also update ThreadBase::threadTypeToString()
};
@@ -332,6 +333,17 @@
bool isOutput() const { return mIsOut; }
+ bool isOffloadOrMmap() const {
+ switch (mType) {
+ case OFFLOAD:
+ case MMAP_PLAYBACK:
+ case MMAP_CAPTURE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
virtual sp<StreamHalInterface> stream() const = 0;
sp<EffectHandle> createEffect_l(
diff --git a/services/audioflinger/TrackMetrics.h b/services/audioflinger/TrackMetrics.h
index 399c788..12bd341 100644
--- a/services/audioflinger/TrackMetrics.h
+++ b/services/audioflinger/TrackMetrics.h
@@ -67,16 +67,21 @@
mIntervalStartTimeNs = systemTime();
}
- void logConstructor(pid_t creatorPid, uid_t creatorUid) const {
+ void logConstructor(pid_t creatorPid, uid_t creatorUid,
+ audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
// Once this item is logged by the server, the client can add properties.
// no lock required, all local or const variables.
- mediametrics::LogItem(mMetricsId)
- .setPid(creatorPid)
+ mediametrics::LogItem item(mMetricsId);
+ item.setPid(creatorPid)
.setUid(creatorUid)
.set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
.set(AMEDIAMETRICS_PROP_EVENT,
- AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
- .record();
+ AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR);
+ // log streamType from the service, since client doesn't know chosen streamType.
+ if (streamType != AUDIO_STREAM_DEFAULT) {
+ item.set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(streamType).c_str());
+ }
+ item.record();
}
// Called when we are removed from the Thread.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 58c61c9..73a40d3 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -602,7 +602,7 @@
}
// Once this item is logged by the server, the client can add properties.
- mTrackMetrics.logConstructor(creatorPid, uid);
+ mTrackMetrics.logConstructor(creatorPid, uid, streamType);
}
AudioFlinger::PlaybackThread::Track::~Track()
diff --git a/services/audiopolicy/common/managerdefinitions/Android.bp b/services/audiopolicy/common/managerdefinitions/Android.bp
index fad3c5b..57f0b5b 100644
--- a/services/audiopolicy/common/managerdefinitions/Android.bp
+++ b/services/audiopolicy/common/managerdefinitions/Android.bp
@@ -25,12 +25,14 @@
"libhidlbase",
"liblog",
"libmedia",
+ "libmedia_helper",
"libutils",
"libxml2",
],
export_shared_lib_headers: [
"libaudiofoundation",
"libmedia",
+ "libmedia_helper",
],
static_libs: [
"libaudioutils",
diff --git a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
index a6562d7..dd1499c 100644
--- a/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/DeviceDescriptor.h
@@ -28,6 +28,8 @@
namespace android {
+class AudioPolicyClientInterface;
+
class DeviceDescriptor : public DeviceDescriptorBase,
public PolicyAudioPort, public PolicyAudioPortConfig
{
@@ -87,6 +89,8 @@
void importAudioPortAndPickAudioProfile(const sp<PolicyAudioPort>& policyPort,
bool force = false);
+ void setEncapsulationInfoFromHal(AudioPolicyClientInterface *clientInterface);
+
void dump(String8 *dst, int spaces, int index, bool verbose = true) const;
private:
diff --git a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
index 86dbba8..a29e60e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/DeviceDescriptor.cpp
@@ -17,9 +17,12 @@
#define LOG_TAG "APM::Devices"
//#define LOG_NDEBUG 0
-#include <audio_utils/string.h>
-#include <media/TypeConverter.h>
#include <set>
+
+#include <AudioPolicyInterface.h>
+#include <audio_utils/string.h>
+#include <media/AudioParameter.h>
+#include <media/TypeConverter.h>
#include "DeviceDescriptor.h"
#include "TypeConverter.h"
#include "HwModule.h"
@@ -165,6 +168,29 @@
policyPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
}
+void DeviceDescriptor::setEncapsulationInfoFromHal(
+ AudioPolicyClientInterface *clientInterface) {
+ AudioParameter param(String8(mDeviceTypeAddr.getAddress()));
+ param.addInt(String8(AudioParameter::keyRouting), mDeviceTypeAddr.mType);
+ param.addKey(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES));
+ param.addKey(String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES));
+ String8 reply = clientInterface->getParameters(AUDIO_IO_HANDLE_NONE, param.toString());
+ AudioParameter repliedParameters(reply);
+ int value;
+ if (repliedParameters.getInt(
+ String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_MODES), value) == NO_ERROR) {
+ if (setEncapsulationModes(value) != NO_ERROR) {
+ ALOGE("Failed to set encapsulation mode(%d)", value);
+ }
+ }
+ if (repliedParameters.getInt(
+ String8(AUDIO_PARAMETER_DEVICE_SUP_ENCAPSULATION_METADATA_TYPES), value) == NO_ERROR) {
+ if (setEncapsulationMetadataTypes(value) != NO_ERROR) {
+ ALOGE("Failed to set encapsulation metadata types(%d)", value);
+ }
+ }
+}
+
void DeviceDescriptor::dump(String8 *dst, int spaces, int index, bool verbose) const
{
String8 extraInfo;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index bcf6f38..2a9a4c4 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -178,6 +178,9 @@
return INVALID_OPERATION;
}
+ // Populate encapsulation information when a output device is connected.
+ device->setEncapsulationInfoFromHal(mpClientInterface);
+
// outputs should never be empty here
ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
"checkOutputsForDevice() returned no outputs but status OK");
@@ -4635,6 +4638,7 @@
if (!device->isAttached()) {
device->attach(hwModule);
mAvailableOutputDevices.add(device);
+ device->setEncapsulationInfoFromHal(mpClientInterface);
if (newDevices) newDevices->add(device);
setEngineDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_AVAILABLE);
}
diff --git a/services/audiopolicy/service/Android.mk b/services/audiopolicy/service/Android.mk
index 94e4811..680b077 100644
--- a/services/audiopolicy/service/Android.mk
+++ b/services/audiopolicy/service/Android.mk
@@ -44,7 +44,7 @@
LOCAL_MODULE:= libaudiopolicyservice
LOCAL_CFLAGS += -fvisibility=hidden
-LOCAL_CFLAGS += -Wall -Werror
+LOCAL_CFLAGS += -Wall -Werror -Wthread-safety
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/service/AudioPolicyEffects.cpp b/services/audiopolicy/service/AudioPolicyEffects.cpp
index 738a279..1ec0c5e 100644
--- a/services/audiopolicy/service/AudioPolicyEffects.cpp
+++ b/services/audiopolicy/service/AudioPolicyEffects.cpp
@@ -928,7 +928,10 @@
loadProcessingChain(result.parsedConfig->preprocess, mInputSources);
loadProcessingChain(result.parsedConfig->postprocess, mOutputStreams);
- loadDeviceProcessingChain(result.parsedConfig->deviceprocess, mDeviceEffects);
+ {
+ Mutex::Autolock _l(mLock);
+ loadDeviceProcessingChain(result.parsedConfig->deviceprocess, mDeviceEffects);
+ }
// Casting from ssize_t to status_t is probably safe, there should not be more than 2^31 errors
return result.nbSkippedElement;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index a15970a..9b61e74 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -58,8 +58,6 @@
AudioPolicyService::AudioPolicyService()
: BnAudioPolicyService(),
- mpAudioPolicyDev(NULL),
- mpAudioPolicy(NULL),
mAudioPolicyManager(NULL),
mAudioPolicyClient(NULL),
mPhoneState(AUDIO_MODE_INVALID),
@@ -78,21 +76,19 @@
mAudioPolicyClient = new AudioPolicyClient(this);
mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient);
-
- mSupportedSystemUsages = std::vector<audio_usage_t> {};
}
// load audio processing modules
- sp<AudioPolicyEffects>audioPolicyEffects = new AudioPolicyEffects();
+ sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();
+ sp<UidPolicy> uidPolicy = new UidPolicy(this);
+ sp<SensorPrivacyPolicy> sensorPrivacyPolicy = new SensorPrivacyPolicy(this);
{
Mutex::Autolock _l(mLock);
mAudioPolicyEffects = audioPolicyEffects;
+ mUidPolicy = uidPolicy;
+ mSensorPrivacyPolicy = sensorPrivacyPolicy;
}
-
- mUidPolicy = new UidPolicy(this);
- mUidPolicy->registerSelf();
-
- mSensorPrivacyPolicy = new SensorPrivacyPolicy(this);
- mSensorPrivacyPolicy->registerSelf();
+ uidPolicy->registerSelf();
+ sensorPrivacyPolicy->registerSelf();
}
AudioPolicyService::~AudioPolicyService()
@@ -107,9 +103,9 @@
mAudioPolicyEffects.clear();
mUidPolicy->unregisterSelf();
- mUidPolicy.clear();
-
mSensorPrivacyPolicy->unregisterSelf();
+
+ mUidPolicy.clear();
mSensorPrivacyPolicy.clear();
}
@@ -172,20 +168,20 @@
// removeNotificationClient() is called when the client process dies.
void AudioPolicyService::removeNotificationClient(uid_t uid, pid_t pid)
{
+ bool hasSameUid = false;
{
Mutex::Autolock _l(mNotificationClientsLock);
int64_t token = ((int64_t)uid<<32) | pid;
mNotificationClients.removeItem(token);
- }
- {
- Mutex::Autolock _l(mLock);
- bool hasSameUid = false;
for (size_t i = 0; i < mNotificationClients.size(); i++) {
if (mNotificationClients.valueAt(i)->uid() == uid) {
hasSameUid = true;
break;
}
}
+ }
+ {
+ Mutex::Autolock _l(mLock);
if (mAudioPolicyManager && !hasSameUid) {
// called from binder death notification: no need to clear caller identity
mAudioPolicyManager->releaseResourcesForUid(uid);
@@ -381,10 +377,14 @@
IPCThreadState::self()->getCallingPid());
}
-static bool dumpTryLock(Mutex& mutex)
+static bool dumpTryLock(Mutex& mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
{
- status_t err = mutex.timedLock(kDumpLockTimeoutNs);
- return err == NO_ERROR;
+ return mutex.timedLock(kDumpLockTimeoutNs) == NO_ERROR;
+}
+
+static void dumpReleaseLock(Mutex& mutex, bool locked) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
+{
+ if (locked) mutex.unlock();
}
status_t AudioPolicyService::dumpInternals(int fd)
@@ -564,7 +564,7 @@
bool isTopOrLatestSensitive = topSensitiveActive == nullptr ?
false : current->uid == topSensitiveActive->uid;
- auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) {
+ auto canCaptureIfInCallOrCommunication = [&](const auto &recordClient) REQUIRES(mLock) {
bool canCaptureCall = recordClient->canCaptureOutput;
bool canCaptureCommunication = recordClient->canCaptureOutput
|| recordClient->uid == mPhoneStateOwnerUid
@@ -703,7 +703,7 @@
if (!dumpAllowed()) {
dumpPermissionDenial(fd);
} else {
- bool locked = dumpTryLock(mLock);
+ const bool locked = dumpTryLock(mLock);
if (!locked) {
String8 result(kDeadlockedString);
write(fd, result.string(), result.size());
@@ -720,7 +720,7 @@
mPackageManager.dump(fd);
- if (locked) mLock.unlock();
+ dumpReleaseLock(mLock, locked);
}
return NO_ERROR;
}
@@ -839,8 +839,16 @@
return BAD_VALUE;
}
- mUidPolicy->addOverrideUid(uid, active);
- return NO_ERROR;
+ sp<UidPolicy> uidPolicy;
+ {
+ Mutex::Autolock _l(mLock);
+ uidPolicy = mUidPolicy;
+ }
+ if (uidPolicy) {
+ uidPolicy->addOverrideUid(uid, active);
+ return NO_ERROR;
+ }
+ return NO_INIT;
}
status_t AudioPolicyService::handleResetUidState(Vector<String16>& args, int err) {
@@ -860,8 +868,16 @@
return BAD_VALUE;
}
- mUidPolicy->removeOverrideUid(uid);
- return NO_ERROR;
+ sp<UidPolicy> uidPolicy;
+ {
+ Mutex::Autolock _l(mLock);
+ uidPolicy = mUidPolicy;
+ }
+ if (uidPolicy) {
+ uidPolicy->removeOverrideUid(uid);
+ return NO_ERROR;
+ }
+ return NO_INIT;
}
status_t AudioPolicyService::handleGetUidState(Vector<String16>& args, int out, int err) {
@@ -881,11 +897,15 @@
return BAD_VALUE;
}
- if (mUidPolicy->isUidActive(uid)) {
- return dprintf(out, "active\n");
- } else {
- return dprintf(out, "idle\n");
+ sp<UidPolicy> uidPolicy;
+ {
+ Mutex::Autolock _l(mLock);
+ uidPolicy = mUidPolicy;
}
+ if (uidPolicy) {
+ return dprintf(out, uidPolicy->isUidActive(uid) ? "active\n" : "idle\n");
+ }
+ return NO_INIT;
}
status_t AudioPolicyService::printHelp(int out) {
@@ -958,7 +978,7 @@
}
}
ActivityManager am;
- bool active = am.isUidActiveOrForeground(uid, String16("audioserver"));
+ bool active = am.isUidActive(uid, String16("audioserver"));
{
Mutex::Autolock _l(mLock);
mCachedUids.insert(std::pair<uid_t,
@@ -1003,7 +1023,7 @@
}
}
ActivityManager am;
- bool active = am.isUidActiveOrForeground(uid, String16("audioserver"));
+ bool active = am.isUidActive(uid, String16("audioserver"));
int state = ActivityManager::PROCESS_STATE_UNKNOWN;
if (active) {
state = am.getUidProcessState(uid, String16("audioserver"));
@@ -1402,7 +1422,7 @@
result.append(buffer);
write(fd, result.string(), result.size());
- bool locked = dumpTryLock(mLock);
+ const bool locked = dumpTryLock(mLock);
if (!locked) {
String8 result2(kCmdDeadlockedString);
write(fd, result2.string(), result2.size());
@@ -1425,7 +1445,7 @@
write(fd, result.string(), result.size());
- if (locked) mLock.unlock();
+ dumpReleaseLock(mLock, locked);
return NO_ERROR;
}
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index f77a481..869a963 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIOPOLICYSERVICE_H
#define ANDROID_AUDIOPOLICYSERVICE_H
+#include <android-base/thread_annotations.h>
#include <cutils/misc.h>
#include <cutils/config_utils.h>
#include <cutils/compiler.h>
@@ -330,13 +331,13 @@
AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
- status_t dumpInternals(int fd);
+ status_t dumpInternals(int fd) REQUIRES(mLock);
// Handles binder shell commands
virtual status_t shellCommand(int in, int out, int err, Vector<String16>& args);
// Sets whether the given UID records only silence
- virtual void setAppState_l(audio_port_handle_t portId, app_state_t state);
+ virtual void setAppState_l(audio_port_handle_t portId, app_state_t state) REQUIRES(mLock);
// Overrides the UID state as if it is idle
status_t handleSetUidState(Vector<String16>& args, int err);
@@ -361,9 +362,9 @@
status_t validateUsage(audio_usage_t usage, pid_t pid, uid_t uid);
void updateUidStates();
- void updateUidStates_l();
+ void updateUidStates_l() REQUIRES(mLock);
- void silenceAllRecordings_l();
+ void silenceAllRecordings_l() REQUIRES(mLock);
static bool isVirtualSource(audio_source_t source);
@@ -420,13 +421,13 @@
wp<AudioPolicyService> mService;
Mutex mLock;
ActivityManager mAm;
- bool mObserverRegistered;
+ bool mObserverRegistered = false;
std::unordered_map<uid_t, std::pair<bool, int>> mOverrideUids;
std::unordered_map<uid_t, std::pair<bool, int>> mCachedUids;
- uid_t mAssistantUid;
+ uid_t mAssistantUid = -1;
std::vector<uid_t> mA11yUids;
- uid_t mCurrentImeUid;
- bool mRttEnabled;
+ uid_t mCurrentImeUid = -1;
+ bool mRttEnabled = false;
};
// If sensor privacy is enabled then all apps, including those that are active, should be
@@ -447,7 +448,7 @@
private:
wp<AudioPolicyService> mService;
- std::atomic_bool mSensorPrivacyEnabled;
+ std::atomic_bool mSensorPrivacyEnabled = false;
};
// Thread used to send audio config commands to audio flinger
@@ -880,26 +881,27 @@
// and possibly back in to audio policy service and acquire mEffectsLock.
sp<AudioCommandThread> mAudioCommandThread; // audio commands thread
sp<AudioCommandThread> mOutputCommandThread; // process stop and release output
- struct audio_policy_device *mpAudioPolicyDev;
- struct audio_policy *mpAudioPolicy;
AudioPolicyInterface *mAudioPolicyManager;
AudioPolicyClient *mAudioPolicyClient;
std::vector<audio_usage_t> mSupportedSystemUsages;
- DefaultKeyedVector< int64_t, sp<NotificationClient> > mNotificationClients;
- Mutex mNotificationClientsLock; // protects mNotificationClients
+ Mutex mNotificationClientsLock;
+ DefaultKeyedVector<int64_t, sp<NotificationClient>> mNotificationClients
+ GUARDED_BY(mNotificationClientsLock);
// Manage all effects configured in audio_effects.conf
// never hold AudioPolicyService::mLock when calling AudioPolicyEffects methods as
// those can call back into AudioPolicyService methods and try to acquire the mutex
- sp<AudioPolicyEffects> mAudioPolicyEffects;
- audio_mode_t mPhoneState;
- uid_t mPhoneStateOwnerUid;
+ sp<AudioPolicyEffects> mAudioPolicyEffects GUARDED_BY(mLock);
+ audio_mode_t mPhoneState GUARDED_BY(mLock);
+ uid_t mPhoneStateOwnerUid GUARDED_BY(mLock);
- sp<UidPolicy> mUidPolicy;
- sp<SensorPrivacyPolicy> mSensorPrivacyPolicy;
+ sp<UidPolicy> mUidPolicy GUARDED_BY(mLock);
+ sp<SensorPrivacyPolicy> mSensorPrivacyPolicy GUARDED_BY(mLock);
- DefaultKeyedVector< audio_port_handle_t, sp<AudioRecordClient> > mAudioRecordClients;
- DefaultKeyedVector< audio_port_handle_t, sp<AudioPlaybackClient> > mAudioPlaybackClients;
+ DefaultKeyedVector<audio_port_handle_t, sp<AudioRecordClient>> mAudioRecordClients
+ GUARDED_BY(mLock);
+ DefaultKeyedVector<audio_port_handle_t, sp<AudioPlaybackClient>> mAudioPlaybackClients
+ GUARDED_BY(mLock);
MediaPackageManager mPackageManager; // To check allowPlaybackCapture
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index f92d673..c9d2c68 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -3192,9 +3192,7 @@
// some polling which should happen pretty rarely anyway as the race is hard
// to hit.
active = mActiveUids.find(uid) != mActiveUids.end();
- if (!active) {
- active = am.isUidActiveOrForeground(uid, callingPackage);
- }
+ if (!active) active = am.isUidActive(uid, callingPackage);
if (active) {
break;
}
diff --git a/services/camera/libcameraservice/api2/CompositeStream.cpp b/services/camera/libcameraservice/api2/CompositeStream.cpp
index b47ee2e..a61dac7 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/CompositeStream.cpp
@@ -28,19 +28,19 @@
namespace android {
namespace camera3 {
-CompositeStream::CompositeStream(wp<CameraDeviceBase> device,
+CompositeStream::CompositeStream(sp<CameraDeviceBase> device,
wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
mDevice(device),
mRemoteCallback(cb),
mNumPartialResults(1),
mErrorState(false) {
- sp<CameraDeviceBase> cameraDevice = device.promote();
- if (cameraDevice.get() != nullptr) {
- CameraMetadata staticInfo = cameraDevice->info();
+ if (device != nullptr) {
+ CameraMetadata staticInfo = device->info();
camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
if (entry.count > 0) {
mNumPartialResults = entry.data.i32[0];
}
+ mStatusTracker = device->getStatusTracker();
}
}
@@ -174,7 +174,7 @@
ret = onStreamBufferError(resultExtras);
break;
case hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST:
- // Invalid request, this shouldn't affect composite streams.
+ onRequestError(resultExtras);
break;
default:
ALOGE("%s: Unrecoverable error: %d detected!", __FUNCTION__, errorCode);
@@ -186,7 +186,7 @@
return ret;
}
-void CompositeStream::notifyError(int64_t frameNumber) {
+void CompositeStream::notifyError(int64_t frameNumber, int32_t requestId) {
sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb =
mRemoteCallback.promote();
@@ -194,6 +194,7 @@
CaptureResultExtras extras;
extras.errorStreamId = getStreamId();
extras.frameNumber = frameNumber;
+ extras.requestId = requestId;
remoteCb->onDeviceError(
hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER,
extras);
diff --git a/services/camera/libcameraservice/api2/CompositeStream.h b/services/camera/libcameraservice/api2/CompositeStream.h
index e5baf1a..5f62d47 100644
--- a/services/camera/libcameraservice/api2/CompositeStream.h
+++ b/services/camera/libcameraservice/api2/CompositeStream.h
@@ -38,7 +38,7 @@
class CompositeStream : public camera3::Camera3StreamBufferListener {
public:
- CompositeStream(wp<CameraDeviceBase> device, wp<hardware::camera2::ICameraDeviceCallbacks> cb);
+ CompositeStream(sp<CameraDeviceBase> device, wp<hardware::camera2::ICameraDeviceCallbacks> cb);
virtual ~CompositeStream() {}
status_t createStream(const std::vector<sp<Surface>>& consumers,
@@ -95,7 +95,7 @@
status_t registerCompositeStreamListener(int32_t streamId);
void eraseResult(int64_t frameNumber);
void flagAnErrorFrameNumber(int64_t frameNumber);
- void notifyError(int64_t frameNumber);
+ void notifyError(int64_t frameNumber, int32_t requestId);
// Subclasses should check for buffer errors from internal streams and return 'true' in
// case the error notification should remain within camera service.
@@ -105,11 +105,16 @@
// internal processing needs result data.
virtual void onResultError(const CaptureResultExtras& resultExtras) = 0;
+ // Subclasses can decide how to handle request errors depending on whether
+ // or not the internal processing needs clean up.
+ virtual void onRequestError(const CaptureResultExtras& /*resultExtras*/) {}
+
// Device and/or service is in unrecoverable error state.
// Composite streams should behave accordingly.
void enableErrorState();
wp<CameraDeviceBase> mDevice;
+ wp<camera3::StatusTracker> mStatusTracker;
wp<hardware::camera2::ICameraDeviceCallbacks> mRemoteCallback;
mutable Mutex mMutex;
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
index 16ce52c..c6859be 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.cpp
@@ -29,7 +29,7 @@
namespace android {
namespace camera3 {
-DepthCompositeStream::DepthCompositeStream(wp<CameraDeviceBase> device,
+DepthCompositeStream::DepthCompositeStream(sp<CameraDeviceBase> device,
wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
CompositeStream(device, cb),
mBlobStreamId(-1),
@@ -43,9 +43,8 @@
mProducerListener(new ProducerListener()),
mMaxJpegSize(-1),
mIsLogicalCamera(false) {
- sp<CameraDeviceBase> cameraDevice = device.promote();
- if (cameraDevice.get() != nullptr) {
- CameraMetadata staticInfo = cameraDevice->info();
+ if (device != nullptr) {
+ CameraMetadata staticInfo = device->info();
auto entry = staticInfo.find(ANDROID_JPEG_MAX_SIZE);
if (entry.count > 0) {
mMaxJpegSize = entry.data.i32[0];
@@ -385,7 +384,8 @@
}
if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) {
- notifyError(inputFrame->frameNumber);
+ //TODO: Figure out correct requestId
+ notifyError(inputFrame->frameNumber, -1 /*requestId*/);
inputFrame->errorNotified = true;
}
}
diff --git a/services/camera/libcameraservice/api2/DepthCompositeStream.h b/services/camera/libcameraservice/api2/DepthCompositeStream.h
index 1bf714d..cab52b6 100644
--- a/services/camera/libcameraservice/api2/DepthCompositeStream.h
+++ b/services/camera/libcameraservice/api2/DepthCompositeStream.h
@@ -41,7 +41,7 @@
public CpuConsumer::FrameAvailableListener {
public:
- DepthCompositeStream(wp<CameraDeviceBase> device,
+ DepthCompositeStream(sp<CameraDeviceBase> device,
wp<hardware::camera2::ICameraDeviceCallbacks> cb);
~DepthCompositeStream() override;
@@ -80,8 +80,9 @@
bool error;
bool errorNotified;
int64_t frameNumber;
+ int32_t requestId;
- InputFrame() : error(false), errorNotified(false), frameNumber(-1) { }
+ InputFrame() : error(false), errorNotified(false), frameNumber(-1), requestId(-1) { }
};
// Helper methods
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index f335c20..1a0881f 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -45,7 +45,7 @@
namespace android {
namespace camera3 {
-HeicCompositeStream::HeicCompositeStream(wp<CameraDeviceBase> device,
+HeicCompositeStream::HeicCompositeStream(sp<CameraDeviceBase> device,
wp<hardware::camera2::ICameraDeviceCallbacks> cb) :
CompositeStream(device, cb),
mUseHeic(false),
@@ -68,7 +68,8 @@
mLockedAppSegmentBufferCnt(0),
mCodecOutputCounter(0),
mQuality(-1),
- mGridTimestampUs(0) {
+ mGridTimestampUs(0),
+ mStatusId(StatusTracker::NO_STATUS_ID) {
}
HeicCompositeStream::~HeicCompositeStream() {
@@ -188,9 +189,17 @@
}
mOutputSurface = consumers[0];
- res = registerCompositeStreamListener(getStreamId());
+ res = registerCompositeStreamListener(mMainImageStreamId);
if (res != OK) {
- ALOGE("%s: Failed to register HAL main image stream", __FUNCTION__);
+ ALOGE("%s: Failed to register HAL main image stream: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ return res;
+ }
+
+ res = registerCompositeStreamListener(mAppSegmentStreamId);
+ if (res != OK) {
+ ALOGE("%s: Failed to register HAL app segment stream: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
return res;
}
@@ -224,6 +233,19 @@
mOutputSurface->disconnect(NATIVE_WINDOW_API_CAMERA);
mOutputSurface.clear();
}
+
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != nullptr && mStatusId != StatusTracker::NO_STATUS_ID) {
+ statusTracker->removeComponent(mStatusId);
+ mStatusId = StatusTracker::NO_STATUS_ID;
+ }
+
+ if (mPendingInputFrames.size() > 0) {
+ ALOGW("%s: mPendingInputFrames has %zu stale entries",
+ __FUNCTION__, mPendingInputFrames.size());
+ mPendingInputFrames.clear();
+ }
+
return res;
}
@@ -232,9 +254,16 @@
if (bufferInfo.mError) return;
- mCodecOutputBufferTimestamps.push(bufferInfo.mTimestamp);
- ALOGV("%s: [%" PRId64 "]: Adding codecOutputBufferTimestamp (%zu timestamps in total)",
- __FUNCTION__, bufferInfo.mTimestamp, mCodecOutputBufferTimestamps.size());
+ if (bufferInfo.mStreamId == mMainImageStreamId) {
+ mMainImageFrameNumbers.push(bufferInfo.mFrameNumber);
+ mCodecOutputBufferFrameNumbers.push(bufferInfo.mFrameNumber);
+ ALOGV("%s: [%" PRId64 "]: Adding main image frame number (%zu frame numbers in total)",
+ __FUNCTION__, bufferInfo.mFrameNumber, mMainImageFrameNumbers.size());
+ } else if (bufferInfo.mStreamId == mAppSegmentStreamId) {
+ mAppSegmentFrameNumbers.push(bufferInfo.mFrameNumber);
+ ALOGV("%s: [%" PRId64 "]: Adding app segment frame number (%zu frame numbers in total)",
+ __FUNCTION__, bufferInfo.mFrameNumber, mAppSegmentFrameNumbers.size());
+ }
}
// We need to get the settings early to handle the case where the codec output
@@ -264,7 +293,7 @@
quality = entry.data.i32[0];
}
- mSettingsByFrameNumber[frameNumber] = std::make_pair(orientation, quality);
+ mSettingsByFrameNumber[frameNumber] = {orientation, quality};
}
void HeicCompositeStream::onFrameAvailable(const BufferItem& item) {
@@ -479,6 +508,11 @@
return res;
}
+ sp<camera3::StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != nullptr) {
+ mStatusId = statusTracker->addComponent();
+ }
+
run("HeicCompositeStreamProc");
return NO_ERROR;
@@ -524,30 +558,44 @@
}
if (mSettingsByFrameNumber.find(resultExtras.frameNumber) != mSettingsByFrameNumber.end()) {
- ALOGV("%s: [%" PRId64 "]: frameNumber %" PRId64, __FUNCTION__,
- timestamp, resultExtras.frameNumber);
- mFrameNumberMap.emplace(resultExtras.frameNumber, timestamp);
- mSettingsByTimestamp[timestamp] = mSettingsByFrameNumber[resultExtras.frameNumber];
- mSettingsByFrameNumber.erase(resultExtras.frameNumber);
+ ALOGV("%s: [%" PRId64 "]: timestamp %" PRId64 ", requestId %d", __FUNCTION__,
+ resultExtras.frameNumber, timestamp, resultExtras.requestId);
+ mSettingsByFrameNumber[resultExtras.frameNumber].shutterNotified = true;
+ mSettingsByFrameNumber[resultExtras.frameNumber].timestamp = timestamp;
+ mSettingsByFrameNumber[resultExtras.frameNumber].requestId = resultExtras.requestId;
mInputReadyCondition.signal();
}
}
void HeicCompositeStream::compilePendingInputLocked() {
- while (!mSettingsByTimestamp.empty()) {
- auto it = mSettingsByTimestamp.begin();
- mPendingInputFrames[it->first].orientation = it->second.first;
- mPendingInputFrames[it->first].quality = it->second.second;
- mSettingsByTimestamp.erase(it);
+ auto i = mSettingsByFrameNumber.begin();
+ while (i != mSettingsByFrameNumber.end()) {
+ if (i->second.shutterNotified) {
+ mPendingInputFrames[i->first].orientation = i->second.orientation;
+ mPendingInputFrames[i->first].quality = i->second.quality;
+ mPendingInputFrames[i->first].timestamp = i->second.timestamp;
+ mPendingInputFrames[i->first].requestId = i->second.requestId;
+ ALOGV("%s: [%" PRId64 "]: timestamp is %" PRId64, __FUNCTION__,
+ i->first, i->second.timestamp);
+ i = mSettingsByFrameNumber.erase(i);
- // Set encoder quality if no inflight encoding
- if (mPendingInputFrames.size() == 1) {
- int32_t newQuality = mPendingInputFrames.begin()->second.quality;
- updateCodecQualityLocked(newQuality);
+ // Set encoder quality if no inflight encoding
+ if (mPendingInputFrames.size() == 1) {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != nullptr) {
+ statusTracker->markComponentActive(mStatusId);
+ ALOGV("%s: Mark component as active", __FUNCTION__);
+ }
+
+ int32_t newQuality = mPendingInputFrames.begin()->second.quality;
+ updateCodecQualityLocked(newQuality);
+ }
+ } else {
+ i++;
}
}
- while (!mInputAppSegmentBuffers.empty()) {
+ while (!mInputAppSegmentBuffers.empty() && mAppSegmentFrameNumbers.size() > 0) {
CpuConsumer::LockedBuffer imgBuffer;
auto it = mInputAppSegmentBuffers.begin();
auto res = mAppSegmentConsumer->lockNextBuffer(&imgBuffer);
@@ -569,17 +617,29 @@
continue;
}
- if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) &&
- (mPendingInputFrames[imgBuffer.timestamp].error)) {
+ if (mPendingInputFrames.find(mAppSegmentFrameNumbers.front()) == mPendingInputFrames.end()) {
+ ALOGE("%s: mPendingInputFrames doesn't contain frameNumber %" PRId64, __FUNCTION__,
+ mAppSegmentFrameNumbers.front());
+ mInputYuvBuffers.erase(it);
+ continue;
+ }
+
+ int64_t frameNumber = mAppSegmentFrameNumbers.front();
+ // If mPendingInputFrames doesn't contain the expected frame number, the captured
+ // input app segment frame must have been dropped via a buffer error. Simply
+ // return the buffer to the buffer queue.
+ if ((mPendingInputFrames.find(frameNumber) == mPendingInputFrames.end()) ||
+ (mPendingInputFrames[frameNumber].error)) {
mAppSegmentConsumer->unlockBuffer(imgBuffer);
} else {
- mPendingInputFrames[imgBuffer.timestamp].appSegmentBuffer = imgBuffer;
+ mPendingInputFrames[frameNumber].appSegmentBuffer = imgBuffer;
mLockedAppSegmentBufferCnt++;
}
mInputAppSegmentBuffers.erase(it);
+ mAppSegmentFrameNumbers.pop();
}
- while (!mInputYuvBuffers.empty() && !mYuvBufferAcquired) {
+ while (!mInputYuvBuffers.empty() && !mYuvBufferAcquired && mMainImageFrameNumbers.size() > 0) {
CpuConsumer::LockedBuffer imgBuffer;
auto it = mInputYuvBuffers.begin();
auto res = mMainImageConsumer->lockNextBuffer(&imgBuffer);
@@ -600,59 +660,67 @@
continue;
}
- if ((mPendingInputFrames.find(imgBuffer.timestamp) != mPendingInputFrames.end()) &&
- (mPendingInputFrames[imgBuffer.timestamp].error)) {
+ if (mPendingInputFrames.find(mMainImageFrameNumbers.front()) == mPendingInputFrames.end()) {
+ ALOGE("%s: mPendingInputFrames doesn't contain frameNumber %" PRId64, __FUNCTION__,
+ mMainImageFrameNumbers.front());
+ mInputYuvBuffers.erase(it);
+ continue;
+ }
+
+ int64_t frameNumber = mMainImageFrameNumbers.front();
+ // If mPendingInputFrames doesn't contain the expected frame number, the captured
+ // input main image must have been dropped via a buffer error. Simply
+ // return the buffer to the buffer queue.
+ if ((mPendingInputFrames.find(frameNumber) == mPendingInputFrames.end()) ||
+ (mPendingInputFrames[frameNumber].error)) {
mMainImageConsumer->unlockBuffer(imgBuffer);
} else {
- mPendingInputFrames[imgBuffer.timestamp].yuvBuffer = imgBuffer;
+ mPendingInputFrames[frameNumber].yuvBuffer = imgBuffer;
mYuvBufferAcquired = true;
}
mInputYuvBuffers.erase(it);
+ mMainImageFrameNumbers.pop();
}
while (!mCodecOutputBuffers.empty()) {
auto it = mCodecOutputBuffers.begin();
- // Bitstream buffer timestamp doesn't necessarily directly correlate with input
- // buffer timestamp. Assume encoder input to output is FIFO, use a queue
- // to look up timestamp.
- int64_t bufferTime = -1;
- if (mCodecOutputBufferTimestamps.empty()) {
- ALOGV("%s: Failed to find buffer timestamp for codec output buffer!", __FUNCTION__);
+ // Assume encoder input to output is FIFO, use a queue to look up
+ // frameNumber when handling codec outputs.
+ int64_t bufferFrameNumber = -1;
+ if (mCodecOutputBufferFrameNumbers.empty()) {
+ ALOGV("%s: Failed to find buffer frameNumber for codec output buffer!", __FUNCTION__);
break;
} else {
- // Direct mapping between camera timestamp (in ns) and codec timestamp (in us).
- bufferTime = mCodecOutputBufferTimestamps.front();
+ // Direct mapping between camera frame number and codec timestamp (in us).
+ bufferFrameNumber = mCodecOutputBufferFrameNumbers.front();
mCodecOutputCounter++;
if (mCodecOutputCounter == mNumOutputTiles) {
- mCodecOutputBufferTimestamps.pop();
+ mCodecOutputBufferFrameNumbers.pop();
mCodecOutputCounter = 0;
}
- mPendingInputFrames[bufferTime].codecOutputBuffers.push_back(*it);
- ALOGV("%s: [%" PRId64 "]: Pushing codecOutputBuffers (time %" PRId64 " us)",
- __FUNCTION__, bufferTime, it->timeUs);
+ mPendingInputFrames[bufferFrameNumber].codecOutputBuffers.push_back(*it);
+ ALOGV("%s: [%" PRId64 "]: Pushing codecOutputBuffers (frameNumber %" PRId64 ")",
+ __FUNCTION__, bufferFrameNumber, it->timeUs);
}
mCodecOutputBuffers.erase(it);
}
- while (!mFrameNumberMap.empty()) {
- auto it = mFrameNumberMap.begin();
- mPendingInputFrames[it->second].frameNumber = it->first;
- ALOGV("%s: [%" PRId64 "]: frameNumber is %" PRId64, __FUNCTION__, it->second, it->first);
- mFrameNumberMap.erase(it);
- }
-
while (!mCaptureResults.empty()) {
auto it = mCaptureResults.begin();
- // Negative timestamp indicates that something went wrong during the capture result
+ // Negative frame number indicates that something went wrong during the capture result
// collection process.
- if (it->first >= 0) {
- if (mPendingInputFrames[it->first].frameNumber == std::get<0>(it->second)) {
- mPendingInputFrames[it->first].result =
+ int64_t frameNumber = std::get<0>(it->second);
+ if (it->first >= 0 &&
+ mPendingInputFrames.find(frameNumber) != mPendingInputFrames.end()) {
+ if (mPendingInputFrames[frameNumber].timestamp == it->first) {
+ mPendingInputFrames[frameNumber].result =
std::make_unique<CameraMetadata>(std::get<1>(it->second));
} else {
ALOGE("%s: Capture result frameNumber/timestamp mapping changed between "
- "shutter and capture result!", __FUNCTION__);
+ "shutter and capture result! before: %" PRId64 ", after: %" PRId64,
+ __FUNCTION__, mPendingInputFrames[frameNumber].timestamp,
+ it->first);
}
}
mCaptureResults.erase(it);
@@ -661,22 +729,24 @@
// mErrorFrameNumbers stores frame number of dropped buffers.
auto it = mErrorFrameNumbers.begin();
while (it != mErrorFrameNumbers.end()) {
- bool frameFound = false;
- for (auto &inputFrame : mPendingInputFrames) {
- if (inputFrame.second.frameNumber == *it) {
- inputFrame.second.error = true;
- frameFound = true;
- break;
- }
- }
-
- if (frameFound) {
- it = mErrorFrameNumbers.erase(it);
+ if (mPendingInputFrames.find(*it) != mPendingInputFrames.end()) {
+ mPendingInputFrames[*it].error = true;
} else {
+ //Error callback is guaranteed to arrive after shutter notify, which
+ //results in mPendingInputFrames being populated.
ALOGW("%s: Not able to find failing input with frame number: %" PRId64, __FUNCTION__,
*it);
- it++;
}
+ it = mErrorFrameNumbers.erase(it);
+ }
+
+ // mExifErrorFrameNumbers stores the frame number of dropped APP_SEGMENT buffers
+ it = mExifErrorFrameNumbers.begin();
+ while (it != mExifErrorFrameNumbers.end()) {
+ if (mPendingInputFrames.find(*it) != mPendingInputFrames.end()) {
+ mPendingInputFrames[*it].exifError = true;
+ }
+ it = mExifErrorFrameNumbers.erase(it);
}
// Distribute codec input buffers to be filled out from YUV output
@@ -701,8 +771,8 @@
}
}
-bool HeicCompositeStream::getNextReadyInputLocked(int64_t *currentTs /*out*/) {
- if (currentTs == nullptr) {
+bool HeicCompositeStream::getNextReadyInputLocked(int64_t *frameNumber /*out*/) {
+ if (frameNumber == nullptr) {
return false;
}
@@ -715,7 +785,8 @@
// This makes sure that muxer gets created only when an output tile is
// generated, because right now we only handle 1 HEIC output buffer at a
// time (max dequeued buffer count is 1).
- bool appSegmentReady = (it.second.appSegmentBuffer.data != nullptr) &&
+ bool appSegmentReady =
+ (it.second.appSegmentBuffer.data != nullptr || it.second.exifError) &&
!it.second.appSegmentWritten && it.second.result != nullptr &&
it.second.muxer != nullptr;
bool codecOutputReady = !it.second.codecOutputBuffers.empty();
@@ -724,9 +795,8 @@
bool hasOutputBuffer = it.second.muxer != nullptr ||
(mDequeuedOutputBufferCnt < kMaxOutputSurfaceProducerCount);
if ((!it.second.error) &&
- (it.first < *currentTs) &&
(appSegmentReady || (codecOutputReady && hasOutputBuffer) || codecInputReady)) {
- *currentTs = it.first;
+ *frameNumber = it.first;
if (it.second.format == nullptr && mFormat != nullptr) {
it.second.format = mFormat->dup();
}
@@ -738,16 +808,12 @@
return newInputAvailable;
}
-int64_t HeicCompositeStream::getNextFailingInputLocked(int64_t *currentTs /*out*/) {
+int64_t HeicCompositeStream::getNextFailingInputLocked() {
int64_t res = -1;
- if (currentTs == nullptr) {
- return res;
- }
for (const auto& it : mPendingInputFrames) {
- if (it.second.error && !it.second.errorNotified && (it.first < *currentTs)) {
- *currentTs = it.first;
- res = it.second.frameNumber;
+ if (it.second.error) {
+ res = it.first;
break;
}
}
@@ -755,12 +821,13 @@
return res;
}
-status_t HeicCompositeStream::processInputFrame(nsecs_t timestamp,
+status_t HeicCompositeStream::processInputFrame(int64_t frameNumber,
InputFrame &inputFrame) {
ATRACE_CALL();
status_t res = OK;
- bool appSegmentReady = inputFrame.appSegmentBuffer.data != nullptr &&
+ bool appSegmentReady =
+ (inputFrame.appSegmentBuffer.data != nullptr || inputFrame.exifError) &&
!inputFrame.appSegmentWritten && inputFrame.result != nullptr &&
inputFrame.muxer != nullptr;
bool codecOutputReady = inputFrame.codecOutputBuffers.size() > 0;
@@ -770,8 +837,9 @@
(mDequeuedOutputBufferCnt < kMaxOutputSurfaceProducerCount);
ALOGV("%s: [%" PRId64 "]: appSegmentReady %d, codecOutputReady %d, codecInputReady %d,"
- " dequeuedOutputBuffer %d", __FUNCTION__, timestamp, appSegmentReady,
- codecOutputReady, codecInputReady, mDequeuedOutputBufferCnt);
+ " dequeuedOutputBuffer %d, timestamp %" PRId64, __FUNCTION__, frameNumber,
+ appSegmentReady, codecOutputReady, codecInputReady, mDequeuedOutputBufferCnt,
+ inputFrame.timestamp);
// Handle inputs for Hevc tiling
if (codecInputReady) {
@@ -791,7 +859,7 @@
// codecOutputReady must be true. Otherwise, appSegmentReady is guaranteed
// to be false, and the function must have returned early.
if (inputFrame.muxer == nullptr) {
- res = startMuxerForInputFrame(timestamp, inputFrame);
+ res = startMuxerForInputFrame(frameNumber, inputFrame);
if (res != OK) {
ALOGE("%s: Failed to create and start muxer: %s (%d)", __FUNCTION__,
strerror(-res), res);
@@ -801,7 +869,7 @@
// Write JPEG APP segments data to the muxer.
if (appSegmentReady) {
- res = processAppSegment(timestamp, inputFrame);
+ res = processAppSegment(frameNumber, inputFrame);
if (res != OK) {
ALOGE("%s: Failed to process JPEG APP segments: %s (%d)", __FUNCTION__,
strerror(-res), res);
@@ -811,7 +879,7 @@
// Write media codec bitstream buffers to muxer.
while (!inputFrame.codecOutputBuffers.empty()) {
- res = processOneCodecOutputFrame(timestamp, inputFrame);
+ res = processOneCodecOutputFrame(frameNumber, inputFrame);
if (res != OK) {
ALOGE("%s: Failed to process codec output frame: %s (%d)", __FUNCTION__,
strerror(-res), res);
@@ -821,7 +889,7 @@
if (inputFrame.pendingOutputTiles == 0) {
if (inputFrame.appSegmentWritten) {
- res = processCompletedInputFrame(timestamp, inputFrame);
+ res = processCompletedInputFrame(frameNumber, inputFrame);
if (res != OK) {
ALOGE("%s: Failed to process completed input frame: %s (%d)", __FUNCTION__,
strerror(-res), res);
@@ -837,7 +905,7 @@
return res;
}
-status_t HeicCompositeStream::startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame) {
+status_t HeicCompositeStream::startMuxerForInputFrame(int64_t frameNumber, InputFrame &inputFrame) {
sp<ANativeWindow> outputANW = mOutputSurface;
auto res = outputANW->dequeueBuffer(mOutputSurface.get(), &inputFrame.anb, &inputFrame.fenceFd);
@@ -851,7 +919,7 @@
// Combine current thread id, stream id and timestamp to uniquely identify image.
std::ostringstream tempOutputFile;
tempOutputFile << "HEIF-" << pthread_self() << "-"
- << getStreamId() << "-" << timestamp;
+ << getStreamId() << "-" << frameNumber;
inputFrame.fileFd = syscall(__NR_memfd_create, tempOutputFile.str().c_str(), MFD_CLOEXEC);
if (inputFrame.fileFd < 0) {
ALOGE("%s: Failed to create file %s. Error no is %d", __FUNCTION__,
@@ -889,22 +957,27 @@
}
ALOGV("%s: [%" PRId64 "]: Muxer started for inputFrame", __FUNCTION__,
- timestamp);
+ frameNumber);
return OK;
}
-status_t HeicCompositeStream::processAppSegment(nsecs_t timestamp, InputFrame &inputFrame) {
+status_t HeicCompositeStream::processAppSegment(int64_t frameNumber, InputFrame &inputFrame) {
size_t app1Size = 0;
- auto appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data,
- inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height,
- &app1Size);
- if (appSegmentSize == 0) {
- ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__);
- return NO_INIT;
+ size_t appSegmentSize = 0;
+ if (!inputFrame.exifError) {
+ appSegmentSize = findAppSegmentsSize(inputFrame.appSegmentBuffer.data,
+ inputFrame.appSegmentBuffer.width * inputFrame.appSegmentBuffer.height,
+ &app1Size);
+ if (appSegmentSize == 0) {
+ ALOGE("%s: Failed to find JPEG APP segment size", __FUNCTION__);
+ return NO_INIT;
+ }
}
std::unique_ptr<ExifUtils> exifUtils(ExifUtils::create());
- auto exifRes = exifUtils->initialize(inputFrame.appSegmentBuffer.data, app1Size);
+ auto exifRes = inputFrame.exifError ?
+ exifUtils->initializeEmpty() :
+ exifUtils->initialize(inputFrame.appSegmentBuffer.data, app1Size);
if (!exifRes) {
ALOGE("%s: Failed to initialize ExifUtils object!", __FUNCTION__);
return BAD_VALUE;
@@ -945,7 +1018,7 @@
sp<ABuffer> aBuffer = new ABuffer(appSegmentBuffer, appSegmentBufferSize);
auto res = inputFrame.muxer->writeSampleData(aBuffer, inputFrame.trackIndex,
- timestamp, MediaCodec::BUFFER_FLAG_MUXER_DATA);
+ inputFrame.timestamp, MediaCodec::BUFFER_FLAG_MUXER_DATA);
delete[] appSegmentBuffer;
if (res != OK) {
@@ -955,13 +1028,14 @@
}
ALOGV("%s: [%" PRId64 "]: appSegmentSize is %zu, width %d, height %d, app1Size %zu",
- __FUNCTION__, timestamp, appSegmentSize, inputFrame.appSegmentBuffer.width,
+ __FUNCTION__, frameNumber, appSegmentSize, inputFrame.appSegmentBuffer.width,
inputFrame.appSegmentBuffer.height, app1Size);
inputFrame.appSegmentWritten = true;
// Release the buffer now so any pending input app segments can be processed
mAppSegmentConsumer->unlockBuffer(inputFrame.appSegmentBuffer);
inputFrame.appSegmentBuffer.data = nullptr;
+ inputFrame.exifError = false;
mLockedAppSegmentBufferCnt--;
return OK;
@@ -1010,7 +1084,7 @@
return OK;
}
-status_t HeicCompositeStream::processOneCodecOutputFrame(nsecs_t timestamp,
+status_t HeicCompositeStream::processOneCodecOutputFrame(int64_t frameNumber,
InputFrame &inputFrame) {
auto it = inputFrame.codecOutputBuffers.begin();
sp<MediaCodecBuffer> buffer;
@@ -1028,7 +1102,7 @@
sp<ABuffer> aBuffer = new ABuffer(buffer->data(), buffer->size());
res = inputFrame.muxer->writeSampleData(
- aBuffer, inputFrame.trackIndex, timestamp, 0 /*flags*/);
+ aBuffer, inputFrame.trackIndex, inputFrame.timestamp, 0 /*flags*/);
if (res != OK) {
ALOGE("%s: Failed to write buffer index %d to muxer: %s (%d)",
__FUNCTION__, it->index, strerror(-res), res);
@@ -1045,11 +1119,11 @@
inputFrame.codecOutputBuffers.erase(inputFrame.codecOutputBuffers.begin());
ALOGV("%s: [%" PRId64 "]: Output buffer index %d",
- __FUNCTION__, timestamp, it->index);
+ __FUNCTION__, frameNumber, it->index);
return OK;
}
-status_t HeicCompositeStream::processCompletedInputFrame(nsecs_t timestamp,
+status_t HeicCompositeStream::processCompletedInputFrame(int64_t frameNumber,
InputFrame &inputFrame) {
sp<ANativeWindow> outputANW = mOutputSurface;
inputFrame.muxer->stop();
@@ -1088,7 +1162,7 @@
blobHeader->blobId = static_cast<CameraBlobId>(0x00FE);
blobHeader->blobSize = fSize;
- res = native_window_set_buffers_timestamp(mOutputSurface.get(), timestamp);
+ res = native_window_set_buffers_timestamp(mOutputSurface.get(), inputFrame.timestamp);
if (res != OK) {
ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
__FUNCTION__, getStreamId(), strerror(-res), res);
@@ -1104,13 +1178,14 @@
inputFrame.anb = nullptr;
mDequeuedOutputBufferCnt--;
- ALOGV("%s: [%" PRId64 "]", __FUNCTION__, timestamp);
- ATRACE_ASYNC_END("HEIC capture", inputFrame.frameNumber);
+ ALOGV("%s: [%" PRId64 "]", __FUNCTION__, frameNumber);
+ ATRACE_ASYNC_END("HEIC capture", frameNumber);
return OK;
}
-void HeicCompositeStream::releaseInputFrameLocked(InputFrame *inputFrame /*out*/) {
+void HeicCompositeStream::releaseInputFrameLocked(int64_t frameNumber,
+ InputFrame *inputFrame /*out*/) {
if (inputFrame == nullptr) {
return;
}
@@ -1138,9 +1213,9 @@
inputFrame->codecInputBuffers.erase(it);
}
- if ((inputFrame->error || mErrorState) && !inputFrame->errorNotified) {
- notifyError(inputFrame->frameNumber);
- inputFrame->errorNotified = true;
+ if (inputFrame->error || mErrorState) {
+ ALOGV("%s: notifyError called for frameNumber %" PRId64, __FUNCTION__, frameNumber);
+ notifyError(frameNumber, inputFrame->requestId);
}
if (inputFrame->fileFd >= 0) {
@@ -1152,6 +1227,8 @@
sp<ANativeWindow> outputANW = mOutputSurface;
outputANW->cancelBuffer(mOutputSurface.get(), inputFrame->anb, /*fence*/ -1);
inputFrame->anb = nullptr;
+
+ mDequeuedOutputBufferCnt--;
}
}
@@ -1161,8 +1238,8 @@
while (it != mPendingInputFrames.end()) {
auto& inputFrame = it->second;
if (inputFrame.error ||
- (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
- releaseInputFrameLocked(&inputFrame);
+ (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
+ releaseInputFrameLocked(it->first, &inputFrame);
it = mPendingInputFrames.erase(it);
inputFrameDone = true;
} else {
@@ -1179,6 +1256,8 @@
auto firstPendingFrame = mPendingInputFrames.begin();
if (firstPendingFrame != mPendingInputFrames.end()) {
updateCodecQualityLocked(firstPendingFrame->second.quality);
+ } else {
+ markTrackerIdle();
}
}
}
@@ -1397,20 +1476,6 @@
return expectedSize;
}
-int64_t HeicCompositeStream::findTimestampInNsLocked(int64_t timeInUs) {
- for (const auto& fn : mFrameNumberMap) {
- if (timeInUs == ns2us(fn.second)) {
- return fn.second;
- }
- }
- for (const auto& inputFrame : mPendingInputFrames) {
- if (timeInUs == ns2us(inputFrame.first)) {
- return inputFrame.first;
- }
- }
- return -1;
-}
-
status_t HeicCompositeStream::copyOneYuvTile(sp<MediaCodecBuffer>& codecBuffer,
const CpuConsumer::LockedBuffer& yuvBuffer,
size_t top, size_t left, size_t width, size_t height) {
@@ -1584,7 +1649,7 @@
}
bool HeicCompositeStream::threadLoop() {
- int64_t currentTs = INT64_MAX;
+ int64_t frameNumber = -1;
bool newInputAvailable = false;
{
@@ -1600,19 +1665,25 @@
while (!newInputAvailable) {
compilePendingInputLocked();
- newInputAvailable = getNextReadyInputLocked(¤tTs);
+ newInputAvailable = getNextReadyInputLocked(&frameNumber);
if (!newInputAvailable) {
- auto failingFrameNumber = getNextFailingInputLocked(¤tTs);
+ auto failingFrameNumber = getNextFailingInputLocked();
if (failingFrameNumber >= 0) {
- // We cannot erase 'mPendingInputFrames[currentTs]' at this point because it is
- // possible for two internal stream buffers to fail. In such scenario the
- // composite stream should notify the client about a stream buffer error only
- // once and this information is kept within 'errorNotified'.
- // Any present failed input frames will be removed on a subsequent call to
- // 'releaseInputFramesLocked()'.
- releaseInputFrameLocked(&mPendingInputFrames[currentTs]);
- currentTs = INT64_MAX;
+ releaseInputFrameLocked(failingFrameNumber,
+ &mPendingInputFrames[failingFrameNumber]);
+
+ // It's okay to remove the entry from mPendingInputFrames
+ // because:
+ // 1. Only one internal stream (main input) is critical in
+ // backing the output stream.
+ // 2. If captureResult/appSegment arrives after the entry is
+ // removed, they are simply skipped.
+ mPendingInputFrames.erase(failingFrameNumber);
+ if (mPendingInputFrames.size() == 0) {
+ markTrackerIdle();
+ }
+ return true;
}
auto ret = mInputReadyCondition.waitRelative(mMutex, kWaitDuration);
@@ -1627,12 +1698,13 @@
}
}
- auto res = processInputFrame(currentTs, mPendingInputFrames[currentTs]);
+ auto res = processInputFrame(frameNumber, mPendingInputFrames[frameNumber]);
Mutex::Autolock l(mMutex);
if (res != OK) {
- ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ": %s (%d)",
- __FUNCTION__, currentTs, strerror(-res), res);
- mPendingInputFrames[currentTs].error = true;
+ ALOGE("%s: Failed processing frame with timestamp: %" PRIu64 ", frameNumber: %"
+ PRId64 ": %s (%d)", __FUNCTION__, mPendingInputFrames[frameNumber].timestamp,
+ frameNumber, strerror(-res), res);
+ mPendingInputFrames[frameNumber].error = true;
}
releaseInputFramesLocked();
@@ -1640,14 +1712,26 @@
return true;
}
+void HeicCompositeStream::flagAnExifErrorFrameNumber(int64_t frameNumber) {
+ Mutex::Autolock l(mMutex);
+ mExifErrorFrameNumbers.emplace(frameNumber);
+ mInputReadyCondition.signal();
+}
+
bool HeicCompositeStream::onStreamBufferError(const CaptureResultExtras& resultExtras) {
bool res = false;
+ int64_t frameNumber = resultExtras.frameNumber;
+
// Buffer errors concerning internal composite streams should not be directly visible to
// camera clients. They must only receive a single buffer error with the public composite
// stream id.
- if ((resultExtras.errorStreamId == mAppSegmentStreamId) ||
- (resultExtras.errorStreamId == mMainImageStreamId)) {
- flagAnErrorFrameNumber(resultExtras.frameNumber);
+ if (resultExtras.errorStreamId == mAppSegmentStreamId) {
+ ALOGV("%s: APP_SEGMENT frameNumber: %" PRId64, __FUNCTION__, frameNumber);
+ flagAnExifErrorFrameNumber(frameNumber);
+ res = true;
+ } else if (resultExtras.errorStreamId == mMainImageStreamId) {
+ ALOGV("%s: YUV frameNumber: %" PRId64, __FUNCTION__, frameNumber);
+ flagAnErrorFrameNumber(frameNumber);
res = true;
}
@@ -1660,16 +1744,16 @@
Mutex::Autolock l(mMutex);
int64_t timestamp = -1;
- for (const auto& fn : mFrameNumberMap) {
+ for (const auto& fn : mSettingsByFrameNumber) {
if (fn.first == resultExtras.frameNumber) {
- timestamp = fn.second;
+ timestamp = fn.second.timestamp;
break;
}
}
if (timestamp == -1) {
for (const auto& inputFrame : mPendingInputFrames) {
- if (inputFrame.second.frameNumber == resultExtras.frameNumber) {
- timestamp = inputFrame.first;
+ if (inputFrame.first == resultExtras.frameNumber) {
+ timestamp = inputFrame.second.timestamp;
break;
}
}
@@ -1681,9 +1765,33 @@
}
mCaptureResults.emplace(timestamp, std::make_tuple(resultExtras.frameNumber, CameraMetadata()));
+ ALOGV("%s: timestamp %" PRId64 ", frameNumber %" PRId64, __FUNCTION__,
+ timestamp, resultExtras.frameNumber);
mInputReadyCondition.signal();
}
+void HeicCompositeStream::onRequestError(const CaptureResultExtras& resultExtras) {
+ auto frameNumber = resultExtras.frameNumber;
+ ALOGV("%s: frameNumber: %" PRId64, __FUNCTION__, frameNumber);
+ Mutex::Autolock l(mMutex);
+ auto numRequests = mSettingsByFrameNumber.erase(frameNumber);
+ if (numRequests == 0) {
+ // Pending request has been populated into mPendingInputFrames
+ mErrorFrameNumbers.emplace(frameNumber);
+ mInputReadyCondition.signal();
+ } else {
+ // REQUEST_ERROR was received without onShutter.
+ }
+}
+
+void HeicCompositeStream::markTrackerIdle() {
+ sp<StatusTracker> statusTracker = mStatusTracker.promote();
+ if (statusTracker != nullptr) {
+ statusTracker->markComponentIdle(mStatusId, Fence::NO_FENCE);
+ ALOGV("%s: Mark component as idle", __FUNCTION__);
+ }
+}
+
void HeicCompositeStream::CodecCallbackHandler::onMessageReceived(const sp<AMessage> &msg) {
sp<HeicCompositeStream> parent = mParent.promote();
if (parent == nullptr) return;
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.h b/services/camera/libcameraservice/api2/HeicCompositeStream.h
index 8fc521e..33ca69a 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.h
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.h
@@ -37,7 +37,7 @@
class HeicCompositeStream : public CompositeStream, public Thread,
public CpuConsumer::FrameAvailableListener {
public:
- HeicCompositeStream(wp<CameraDeviceBase> device,
+ HeicCompositeStream(sp<CameraDeviceBase> device,
wp<hardware::camera2::ICameraDeviceCallbacks> cb);
~HeicCompositeStream() override;
@@ -81,6 +81,7 @@
bool threadLoop() override;
bool onStreamBufferError(const CaptureResultExtras& resultExtras) override;
void onResultError(const CaptureResultExtras& resultExtras) override;
+ void onRequestError(const CaptureResultExtras& resultExtras) override;
private:
//
@@ -156,9 +157,10 @@
CpuConsumer::LockedBuffer yuvBuffer;
std::vector<CodecInputBufferInfo> codecInputBuffers;
- bool error;
- bool errorNotified;
- int64_t frameNumber;
+ bool error; // Main input image buffer error
+ bool exifError; // Exif/APP_SEGMENT buffer error
+ int64_t timestamp;
+ int32_t requestId;
sp<AMessage> format;
sp<MediaMuxer> muxer;
@@ -172,30 +174,29 @@
size_t codecInputCounter;
InputFrame() : orientation(0), quality(kDefaultJpegQuality), error(false),
- errorNotified(false), frameNumber(-1), fenceFd(-1), fileFd(-1),
- trackIndex(-1), anb(nullptr), appSegmentWritten(false),
+ exifError(false), timestamp(-1), requestId(-1), fenceFd(-1),
+ fileFd(-1), trackIndex(-1), anb(nullptr), appSegmentWritten(false),
pendingOutputTiles(0), codecInputCounter(0) { }
};
void compilePendingInputLocked();
- // Find first complete and valid frame with smallest timestamp
- bool getNextReadyInputLocked(int64_t *currentTs /*out*/);
- // Find next failing frame number with smallest timestamp and return respective frame number
- int64_t getNextFailingInputLocked(int64_t *currentTs /*out*/);
+ // Find first complete and valid frame with smallest frame number
+ bool getNextReadyInputLocked(int64_t *frameNumber /*out*/);
+ // Find next failing frame number with smallest frame number and return respective frame number
+ int64_t getNextFailingInputLocked();
- status_t processInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+ status_t processInputFrame(int64_t frameNumber, InputFrame &inputFrame);
status_t processCodecInputFrame(InputFrame &inputFrame);
- status_t startMuxerForInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
- status_t processAppSegment(nsecs_t timestamp, InputFrame &inputFrame);
- status_t processOneCodecOutputFrame(nsecs_t timestamp, InputFrame &inputFrame);
- status_t processCompletedInputFrame(nsecs_t timestamp, InputFrame &inputFrame);
+ status_t startMuxerForInputFrame(int64_t frameNumber, InputFrame &inputFrame);
+ status_t processAppSegment(int64_t frameNumber, InputFrame &inputFrame);
+ status_t processOneCodecOutputFrame(int64_t frameNumber, InputFrame &inputFrame);
+ status_t processCompletedInputFrame(int64_t frameNumber, InputFrame &inputFrame);
- void releaseInputFrameLocked(InputFrame *inputFrame /*out*/);
+ void releaseInputFrameLocked(int64_t frameNumber, InputFrame *inputFrame /*out*/);
void releaseInputFramesLocked();
size_t findAppSegmentsSize(const uint8_t* appSegmentBuffer, size_t maxSize,
size_t* app1SegmentSize);
- int64_t findTimestampInNsLocked(int64_t timeInUs);
status_t copyOneYuvTile(sp<MediaCodecBuffer>& codecBuffer,
const CpuConsumer::LockedBuffer& yuvBuffer,
size_t top, size_t left, size_t width, size_t height);
@@ -218,12 +219,14 @@
sp<CpuConsumer> mAppSegmentConsumer;
sp<Surface> mAppSegmentSurface;
size_t mAppSegmentMaxSize;
+ std::queue<int64_t> mAppSegmentFrameNumbers;
CameraMetadata mStaticInfo;
int mMainImageStreamId, mMainImageSurfaceId;
sp<Surface> mMainImageSurface;
sp<CpuConsumer> mMainImageConsumer; // Only applicable for HEVC codec.
bool mYuvBufferAcquired; // Only applicable to HEVC codec
+ std::queue<int64_t> mMainImageFrameNumbers;
static const int32_t kMaxOutputSurfaceProducerCount = 1;
sp<Surface> mOutputSurface;
@@ -231,9 +234,22 @@
int32_t mDequeuedOutputBufferCnt;
// Map from frame number to JPEG setting of orientation+quality
- std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByFrameNumber;
- // Map from timestamp to JPEG setting of orientation+quality
- std::map<int64_t, std::pair<int32_t, int32_t>> mSettingsByTimestamp;
+ struct HeicSettings {
+ int32_t orientation;
+ int32_t quality;
+ int64_t timestamp;
+ int32_t requestId;
+ bool shutterNotified;
+
+ HeicSettings() : orientation(0), quality(95), timestamp(0),
+ requestId(-1), shutterNotified(false) {}
+ HeicSettings(int32_t _orientation, int32_t _quality) :
+ orientation(_orientation),
+ quality(_quality), timestamp(0),
+ requestId(-1), shutterNotified(false) {}
+
+ };
+ std::map<int64_t, HeicSettings> mSettingsByFrameNumber;
// Keep all incoming APP segment Blob buffer pending further processing.
std::vector<int64_t> mInputAppSegmentBuffers;
@@ -241,7 +257,7 @@
// Keep all incoming HEIC blob buffer pending further processing.
std::vector<CodecOutputBufferInfo> mCodecOutputBuffers;
- std::queue<int64_t> mCodecOutputBufferTimestamps;
+ std::queue<int64_t> mCodecOutputBufferFrameNumbers;
size_t mCodecOutputCounter;
int32_t mQuality;
@@ -253,11 +269,19 @@
// Artificial strictly incremental YUV grid timestamp to make encoder happy.
int64_t mGridTimestampUs;
- // In most common use case, entries are accessed in order.
+ // Indexed by frame number. In most common use case, entries are accessed in order.
std::map<int64_t, InputFrame> mPendingInputFrames;
// Function pointer of libyuv row copy.
void (*mFnCopyRow)(const uint8_t* src, uint8_t* dst, int width);
+
+ // A set of APP_SEGMENT error frame numbers
+ std::set<int64_t> mExifErrorFrameNumbers;
+ void flagAnExifErrorFrameNumber(int64_t frameNumber);
+
+ // The status id for tracking the active/idle status of this composite stream
+ int mStatusId;
+ void markTrackerIdle();
};
}; // namespace camera3
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 3662a65..a537ef5 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -33,6 +33,7 @@
#include "camera/CaptureResult.h"
#include "gui/IGraphicBufferProducer.h"
#include "device3/Camera3StreamInterface.h"
+#include "device3/StatusTracker.h"
#include "binder/Status.h"
#include "FrameProducer.h"
@@ -362,6 +363,10 @@
virtual status_t setRotateAndCropAutoBehavior(
camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) = 0;
+ /**
+ * Get the status tracker of the camera device
+ */
+ virtual wp<camera3::StatusTracker> getStatusTracker() = 0;
};
}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 10b653e..32d118d 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -949,6 +949,51 @@
return res;
}
+status_t CameraProviderManager::ProviderInfo::DeviceInfo3::addPreCorrectionActiveArraySize() {
+ status_t res = OK;
+ auto& c = mCameraCharacteristics;
+
+ auto activeArraySize = c.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ auto preCorrectionActiveArraySize = c.find(
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+ if (activeArraySize.count == 4 && preCorrectionActiveArraySize.count == 0) {
+ std::vector<int32_t> preCorrectionArray(
+ activeArraySize.data.i32, activeArraySize.data.i32+4);
+ res = c.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE,
+ preCorrectionArray.data(), 4);
+ if (res != OK) {
+ ALOGE("%s: Failed to add ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ } else {
+ return res;
+ }
+
+ auto charTags = c.find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
+ bool hasPreCorrectionActiveArraySize = std::find(charTags.data.i32,
+ charTags.data.i32 + charTags.count,
+ ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE) !=
+ (charTags.data.i32 + charTags.count);
+ if (!hasPreCorrectionActiveArraySize) {
+ std::vector<int32_t> supportedCharTags;
+ supportedCharTags.reserve(charTags.count + 1);
+ supportedCharTags.insert(supportedCharTags.end(), charTags.data.i32,
+ charTags.data.i32 + charTags.count);
+ supportedCharTags.push_back(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
+
+ res = c.update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, supportedCharTags.data(),
+ supportedCharTags.size());
+ if (res != OK) {
+ ALOGE("%s: Failed to update ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s(%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+ }
+
+ return res;
+}
+
status_t CameraProviderManager::ProviderInfo::DeviceInfo3::removeAvailableKeys(
CameraMetadata& c, const std::vector<uint32_t>& keys, uint32_t keyTag) {
status_t res = OK;
@@ -2254,7 +2299,11 @@
ALOGE("%s: Unable to add default SCALER_ROTATE_AND_CROP tags: %s (%d)", __FUNCTION__,
strerror(-res), res);
}
-
+ res = addPreCorrectionActiveArraySize();
+ if (OK != res) {
+ ALOGE("%s: Unable to add PRE_CORRECTION_ACTIVE_ARRAY_SIZE: %s (%d)", __FUNCTION__,
+ strerror(-res), res);
+ }
res = camera3::ZoomRatioMapper::overrideZoomRatioTags(
&mCameraCharacteristics, &mSupportNativeZoomRatio);
if (OK != res) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 50044d8..25d3639 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -566,6 +566,7 @@
status_t addDynamicDepthTags();
status_t deriveHeicTags();
status_t addRotateCropTags();
+ status_t addPreCorrectionActiveArraySize();
static void getSupportedSizes(const CameraMetadata& ch, uint32_t tag,
android_pixel_format_t format,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 19ecf4b..c059f55 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -231,6 +231,9 @@
status_t setRotateAndCropAutoBehavior(
camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
+ // Get the status trackeer for the camera device
+ wp<camera3::StatusTracker> getStatusTracker() { return mStatusTracker; }
+
/**
* Helper functions to map between framework and HIDL values
*/
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index ef0d919..bda2961 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -269,8 +269,6 @@
}
}
- mBufferReturnedSignal.signal();
-
if (output) {
mLastTimestamp = timestamp;
}
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 750f64d..448379c 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -55,7 +55,6 @@
// number of output buffers that are currently acquired by HAL. This will be
// Redundant when camera3 streams are no longer bidirectional streams.
size_t mHandoutOutputBufferCount;
- Condition mBufferReturnedSignal;
uint32_t mFrameCount;
// Last received output buffer's timestamp
nsecs_t mLastTimestamp;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 7916ddb..e54a99b 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -814,6 +814,8 @@
info.mError = (buffer.status == CAMERA3_BUFFER_STATUS_ERROR);
info.mFrameNumber = frameNumber;
info.mTimestamp = timestamp;
+ info.mStreamId = getId();
+
// TODO: rest of fields
for (it = mBufferListenerList.begin(), end = mBufferListenerList.end();
diff --git a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
index d0aee27..170da5a 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamBufferListener.h
@@ -29,6 +29,7 @@
public:
struct BufferInfo {
+ int mStreamId;
bool mOutput; // if false then input buffer
Rect mCrop;
uint32_t mTransform;
diff --git a/services/mediametrics/AnalyticsActions.h b/services/mediametrics/AnalyticsActions.h
index 0151134..897e567 100644
--- a/services/mediametrics/AnalyticsActions.h
+++ b/services/mediametrics/AnalyticsActions.h
@@ -78,8 +78,8 @@
template <typename T, typename U, typename A>
void addAction(T&& url, U&& value, A&& action) {
std::lock_guard l(mLock);
- mFilters[ { std::forward<T>(url), std::forward<U>(value) } ]
- = std::forward<A>(action);
+ mFilters.emplace(Trigger{ std::forward<T>(url), std::forward<U>(value) },
+ std::forward<A>(action));
}
// TODO: remove an action.
@@ -94,36 +94,15 @@
std::vector<Action> actions;
std::lock_guard l(mLock);
- // Essentially the code looks like this:
- /*
- for (auto &[trigger, action] : mFilters) {
- if (isMatch(trigger, item)) {
- actions.push_back(action);
- }
- }
- */
-
- // Optimization: there should only be one match for a non-wildcard url.
- auto it = mFilters.upper_bound( {item->getKey(), std::monostate{} });
- if (it != mFilters.end()) {
- const auto &[trigger, action] = *it;
- if (isMatch(trigger, item)) {
+ for (const auto &[trigger, action] : mFilters) {
+ if (isWildcardMatch(trigger, item) ==
+ mediametrics::Item::RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) {
actions.push_back(action);
}
}
- // Optimization: for wildcard URLs we go backwards until there is no
- // match with the prefix before the wildcard.
- while (it != mFilters.begin()) { // this walks backwards, cannot start at begin.
- const auto &[trigger, action] = *--it; // look backwards
- int ret = isWildcardMatch(trigger, item);
- if (ret == mediametrics::Item::RECURSIVE_WILDCARD_CHECK_MATCH_FOUND) {
- actions.push_back(action); // match found.
- } else if (ret == mediametrics::Item::RECURSIVE_WILDCARD_CHECK_NO_MATCH_NO_WILDCARD) {
- break; // no match before wildcard.
- }
- // a wildcard was encountered when matching prefix, so we should check again.
- }
+ // TODO: Optimize for prefix search and wildcarding.
+
return actions;
}
@@ -145,7 +124,9 @@
}
mutable std::mutex mLock;
- std::map<Trigger, Action> mFilters GUARDED_BY(mLock);
+
+ using FilterType = std::multimap<Trigger, Action>;
+ FilterType mFilters GUARDED_BY(mLock);
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index fb4022e..c87fbd9 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -36,6 +36,7 @@
srcs: [
"AudioAnalytics.cpp",
+ "AudioPowerUsage.cpp",
"iface_statsd.cpp",
"MediaMetricsService.cpp",
"statsd_audiopolicy.cpp",
@@ -55,6 +56,7 @@
shared_libs: [
"libbinder",
+ "libcutils",
"liblog",
"libmediametrics",
"libmediautils",
@@ -76,5 +78,6 @@
"-Wall",
"-Werror",
"-Wextra",
+ "-Wthread-safety",
],
}
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 3f9a42f..6138d32 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -19,8 +19,12 @@
#include <utils/Log.h>
#include "AudioAnalytics.h"
+#include "MediaMetricsService.h" // package info
+#include <audio_utils/clock.h> // clock conversions
+#include <statslog.h> // statsd
-#include <audio_utils/clock.h> // clock conversions
+// Enable for testing of delivery to statsd
+// #define STATSD
namespace android::mediametrics {
@@ -45,53 +49,116 @@
// Perhaps report this.
}));
- // Check underruns
+ // Handle device use record statistics
mActions.addAction(
- AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
- std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_UNDERRUN),
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
- std::string threadId = item->getKey().substr(
- sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) - 1);
- std::string outputDevices;
- mAnalyticsState->timeMachine().get(
- item->getKey(), AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
- ALOGD("(key=%s) Thread underrun event detected on io handle:%s device:%s",
- item->getKey().c_str(), threadId.c_str(), outputDevices.c_str());
- if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
- // report this for Bluetooth
- }
+ mDeviceUse.endAudioIntervalGroup(item, DeviceUse::RECORD);
}));
- // Check latencies, playback and startup
+ // Handle device use thread statistics
mActions.addAction(
- AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_LATENCYMS,
- std::monostate{}, // accept any value
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
std::make_shared<AnalyticsActions::Function>(
[this](const std::shared_ptr<const android::mediametrics::Item> &item){
- double latencyMs{};
- double startupMs{};
- if (!item->get(AMEDIAMETRICS_PROP_LATENCYMS, &latencyMs)
- || !item->get(AMEDIAMETRICS_PROP_STARTUPMS, &startupMs)) return;
+ mDeviceUse.endAudioIntervalGroup(item, DeviceUse::THREAD);
+ }));
- std::string trackId = item->getKey().substr(
- sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK) - 1);
- std::string thread = getThreadFromTrack(item->getKey());
- std::string outputDevices;
- mAnalyticsState->timeMachine().get(
- thread, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
- ALOGD("(key=%s) Track latencyMs:%lf startupMs:%lf detected on port:%s device:%s",
- item->getKey().c_str(), latencyMs, startupMs,
- trackId.c_str(), outputDevices.c_str());
- if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
- // report this for Bluetooth
- }
+ // Handle device use track statistics
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceUse.endAudioIntervalGroup(item, DeviceUse::TRACK);
+ }));
+
+
+ // Handle device connection statistics
+
+ // We track connections (not disconnections) for the time to connect.
+ // TODO: consider BT requests in their A2dp service
+ // AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
+ // AudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
+ // AudioDeviceBroker.postA2dpActiveDeviceChange
+ mActions.addAction(
+ "audio.device.a2dp.state",
+ "connected",
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceConnection.a2dpConnected(item);
+ }));
+ // If audio is active, we expect to see a createAudioPatch after the device is connected.
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string("createAudioPatch"),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceConnection.createPatch(item);
+ }));
+
+ // Called from BT service
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_DEVICE
+ "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent"
+ "." AMEDIAMETRICS_PROP_STATE,
+ "connected",
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mDeviceConnection.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(item);
+ }));
+
+ // Handle power usage
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mAudioPowerUsage.checkTrackRecord(item, true /* isTrack */);
+ }));
+
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mAudioPowerUsage.checkTrackRecord(item, false /* isTrack */);
+ }));
+
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETMODE),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ // ALOGD("(key=%s) Audioflinger setMode", item->getKey().c_str());
+ mAudioPowerUsage.checkMode(item);
+ }));
+
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_AUDIO_FLINGER "." AMEDIAMETRICS_PROP_EVENT,
+ std::string(AMEDIAMETRICS_PROP_EVENT_VALUE_SETVOICEVOLUME),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ // ALOGD("(key=%s) Audioflinger setVoiceVolume", item->getKey().c_str());
+ mAudioPowerUsage.checkVoiceVolume(item);
+ }));
+
+ mActions.addAction(
+ AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD "*." AMEDIAMETRICS_PROP_EVENT,
+ std::string("createAudioPatch"),
+ std::make_shared<AnalyticsActions::Function>(
+ [this](const std::shared_ptr<const android::mediametrics::Item> &item){
+ mAudioPowerUsage.checkCreatePatch(item);
}));
}
AudioAnalytics::~AudioAnalytics()
{
ALOGD("%s", __func__);
+ mTimedAction.quit(); // ensure no deferred access during destructor.
}
status_t AudioAnalytics::submit(
@@ -127,6 +194,12 @@
ss << s;
ll -= l;
}
+
+ if (ll > 0 && prefix == nullptr) {
+ auto [s, l] = mAudioPowerUsage.dump(ll);
+ ss << s;
+ ll -= l;
+ }
return { ss.str(), lines - ll };
}
@@ -151,4 +224,374 @@
return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD) + std::to_string(threadId_int32);
}
+// DeviceUse helper class.
+void AudioAnalytics::DeviceUse::endAudioIntervalGroup(
+ const std::shared_ptr<const android::mediametrics::Item> &item, ItemType itemType) const {
+ const std::string& key = item->getKey();
+ const std::string id = key.substr(
+ (itemType == THREAD ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_THREAD)
+ : itemType == TRACK ? sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_TRACK)
+ : sizeof(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD))
+ - 1);
+ // deliver statistics
+ int64_t deviceTimeNs = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs);
+ std::string encoding;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ENCODING, &encoding);
+ int32_t frameCount = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_FRAMECOUNT, &frameCount);
+ std::string inputDevices;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_INPUTDEVICES, &inputDevices);
+ int32_t intervalCount = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_INTERVALCOUNT, &intervalCount);
+ std::string outputDevices;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
+ int32_t sampleRate = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SAMPLERATE, &sampleRate);
+ std::string flags;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_FLAGS, &flags);
+ // We may have several devices.
+ // Strings allow us to mix input and output devices together.
+ // TODO: review if we want to separate them.
+ std::stringstream ss;
+ for (const auto& devicePairs : { outputDevices, inputDevices }) {
+ const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(devicePairs);
+ for (const auto& [device, addr] : devaddrvec) {
+ if (ss.tellp() > 0) ss << "|"; // delimit devices with '|'.
+ ss << device;
+ }
+ }
+ std::string devices = ss.str();
+
+ // Get connected device name if from bluetooth.
+ bool isBluetooth = false;
+ std::string deviceNames; // we only have one device name at this time.
+ if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH") != std::string::npos) {
+ isBluetooth = true;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ "audio.device.bt_a2dp", AMEDIAMETRICS_PROP_NAME, &deviceNames);
+ // We don't check if deviceName is sanitized.
+ // TODO: remove reserved chars such as '|' and replace with a char like '_'.
+ }
+
+ switch (itemType) {
+ case RECORD: {
+ std::string callerName;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName);
+
+ std::string packageName;
+ int64_t versionCode = 0;
+ int32_t uid = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ALLOWUID, &uid);
+ if (uid != -1) {
+ std::tie(packageName, versionCode) =
+ MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+ }
+
+ int32_t selectedDeviceId = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
+ std::string source;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SOURCE, &source);
+
+ ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
+ "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+ "sampleRate:%d "
+ "packageName:%s "
+ "selectedDeviceId:%d "
+ "callerName:%s source:%s",
+ key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
+ (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+ sampleRate,
+ packageName.c_str(), selectedDeviceId,
+ callerName.c_str(), source.c_str());
+
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIORECORDDEVICEUSAGE_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , devices.c_str()
+ , deviceNames.c_str()
+ , deviceTimeNs
+ , encoding.c_str()
+ , frameCount
+ , intervalCount
+ , sampleRate
+ , flags.c_str()
+
+ , packageName.c_str()
+ , selectedDeviceId
+ , callerName.c_str()
+ , source.c_str()
+ );
+ }
+#endif
+ } break;
+ case THREAD: {
+ std::string type;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_TYPE, &type);
+ int32_t underrun = 0; // zero for record types
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
+ ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
+ "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+ "sampleRate:%d underrun:%d "
+ "flags:%s type:%s",
+ key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
+ (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+ sampleRate, underrun,
+ flags.c_str(), type.c_str());
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIOTHREADDEVICEUSAGE_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , devices.c_str()
+ , deviceNames.c_str()
+ , deviceTimeNs
+ , encoding.c_str()
+ , frameCount
+ , intervalCount
+ , sampleRate
+ , flags.c_str()
+
+ , underrun
+ , type.c_str()
+ );
+ }
+#endif
+ } break;
+ case TRACK: {
+ std::string callerName;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CALLERNAME, &callerName);
+ std::string contentType;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CONTENTTYPE, &contentType);
+ double deviceLatencyMs = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICELATENCYMS, &deviceLatencyMs);
+ double deviceStartupMs = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICESTARTUPMS, &deviceStartupMs);
+ double deviceVolume = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume);
+ std::string packageName;
+ int64_t versionCode = 0;
+ int32_t uid = -1;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_ALLOWUID, &uid);
+ if (uid != -1) {
+ std::tie(packageName, versionCode) =
+ MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+ }
+ double playbackPitch = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_PLAYBACK_PITCH, &playbackPitch);
+ double playbackSpeed = 0.;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_PLAYBACK_SPEED, &playbackSpeed);
+ int32_t selectedDeviceId = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SELECTEDDEVICEID, &selectedDeviceId);
+ std::string streamType;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_STREAMTYPE, &streamType);
+ int32_t underrun = 0;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_UNDERRUN, &underrun);
+ std::string usage;
+ mAudioAnalytics.mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_USAGE, &usage);
+
+ ALOGD("(key=%s) id:%s endAudioIntervalGroup devices:%s deviceNames:%s "
+ "deviceTimeNs:%lld encoding:%s frameCount:%d intervalCount:%d "
+ "sampleRate:%d underrun:%d "
+ "callerName:%s contentType:%s "
+ "deviceLatencyMs:%lf deviceStartupMs:%lf deviceVolume:%lf "
+ "packageName:%s playbackPitch:%lf playbackSpeed:%lf "
+ "selectedDeviceId:%d streamType:%s usage:%s",
+ key.c_str(), id.c_str(), devices.c_str(), deviceNames.c_str(),
+ (long long)deviceTimeNs, encoding.c_str(), frameCount, intervalCount,
+ sampleRate, underrun,
+ callerName.c_str(), contentType.c_str(),
+ deviceLatencyMs, deviceStartupMs, deviceVolume,
+ packageName.c_str(), playbackPitch, playbackSpeed,
+ selectedDeviceId, streamType.c_str(), usage.c_str());
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIOTRACKDEVICEUSAGE_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , devices.c_str()
+ , deviceNames.c_str()
+ , deviceTimeNs
+ , encoding.c_str()
+ , frameCount
+ , intervalCount
+ , sampleRate
+ , flags.c_str()
+ , underrun
+
+ , packageName.c_str()
+ , (float)deviceLatencyMs
+ , (float)deviceStartupMs
+ , (float)deviceVolume
+ , selectedDeviceId
+ , streamType.c_str()
+ , usage.c_str()
+ , contentType.c_str()
+ , callerName.c_str()
+ );
+ }
+#endif
+ } break;
+ }
+
+ // Report this as needed.
+ if (isBluetooth) {
+ // report this for Bluetooth
+ }
+}
+
+// DeviceConnection helper class.
+void AudioAnalytics::DeviceConnection::a2dpConnected(
+ const std::shared_ptr<const android::mediametrics::Item> &item) {
+ const std::string& key = item->getKey();
+ const int64_t atNs = item->getTimestamp();
+ {
+ std::lock_guard l(mLock);
+ mA2dpConnectionServiceNs = atNs;
+ ++mA2dpConnectionServices;
+
+ if (mA2dpConnectionRequestNs == 0) {
+ mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
+ }
+ // This sets the time we were connected. Now we look for the delta in the future.
+ }
+ std::string name;
+ item->get(AMEDIAMETRICS_PROP_NAME, &name);
+ ALOGD("(key=%s) a2dp connected device:%s atNs:%lld",
+ key.c_str(), name.c_str(), (long long)atNs);
+
+}
+
+void AudioAnalytics::DeviceConnection::createPatch(
+ const std::shared_ptr<const android::mediametrics::Item> &item) {
+ std::lock_guard l(mLock);
+ if (mA2dpConnectionServiceNs == 0) return; // patch unrelated to us.
+ const std::string& key = item->getKey();
+ std::string outputDevices;
+ item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices);
+ if (outputDevices.find("AUDIO_DEVICE_OUT_BLUETOOTH_A2DP") != std::string::npos) {
+ // TODO compare address
+ int64_t timeDiff = item->getTimestamp();
+ if (mA2dpConnectionRequestNs == 0) {
+ ALOGD("%s: A2DP create patch didn't see a connection request", __func__);
+ timeDiff -= mA2dpConnectionServiceNs;
+ } else {
+ timeDiff -= mA2dpConnectionRequestNs;
+ }
+ ALOGD("(key=%s) A2DP device connection time: %lld", key.c_str(), (long long)timeDiff);
+ mA2dpConnectionRequestNs = 0;
+ mA2dpConnectionServiceNs = 0;
+ ++mA2dpConnectionSuccesses;
+
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
+ , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__SUCCESS
+ , /* connection_time_ms */ timeDiff * 1e-6 /* NS to MS */
+ , /* connection_count */ 1
+ );
+ }
+#endif
+ }
+}
+
+// Called through AudioManager when the BT service wants to enable
+void AudioAnalytics::DeviceConnection::postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ const std::shared_ptr<const android::mediametrics::Item> &item) {
+ const int64_t atNs = item->getTimestamp();
+ const std::string& key = item->getKey();
+ std::string state;
+ item->get(AMEDIAMETRICS_PROP_STATE, &state);
+ if (state != "connected") return;
+ {
+ std::lock_guard l(mLock);
+ mA2dpConnectionRequestNs = atNs;
+ ++mA2dpConnectionRequests;
+ }
+ ALOGD("(key=%s) a2dp connection request atNs:%lld",
+ key.c_str(), (long long)atNs);
+ // TODO: attempt to cancel a timed event, rather than let it expire.
+ mAudioAnalytics.mTimedAction.postIn(std::chrono::seconds(5), [this](){ expire(); });
+}
+
+void AudioAnalytics::DeviceConnection::expire() {
+ std::lock_guard l(mLock);
+ if (mA2dpConnectionRequestNs == 0) return; // ignore (this was an internal connection).
+ if (mA2dpConnectionServiceNs == 0) {
+ ALOGD("A2DP device connection service cancels");
+ ++mA2dpConnectionJavaServiceCancels; // service did not connect to A2DP
+
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
+ , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__JAVA_SERVICE_CANCEL
+ , /* connection_time_ms */ 0.f
+ , /* connection_count */ 1
+ );
+ }
+#endif
+ return;
+ }
+
+ // AudioFlinger didn't play - an expiration may occur because there is no audio playing.
+ // Should we check elsewhere?
+ // TODO: disambiguate this case.
+ ALOGD("A2DP device connection expired, state unknown");
+ mA2dpConnectionRequestNs = 0;
+ mA2dpConnectionServiceNs = 0;
+ ++mA2dpConnectionUnknowns; // connection result unknown
+#ifdef STATSD
+ if (mAudioAnalytics.mDeliverStatistics) {
+ (void)android::util::stats_write(
+ android::util::MEDIAMETRICS_AUDIODEVICECONNECTION_REPORTED
+ /* timestamp, */
+ /* mediaApexVersion, */
+ , "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"
+ , android::util::MEDIAMETRICS_AUDIO_DEVICE_CONNECTION_REPORTED__RESULT__UNKNOWN
+ , /* connection_time_ms */ 0.f
+ , /* connection_count */ 1
+ );
+ }
+#endif
+}
+
} // namespace android
diff --git a/services/mediametrics/AudioAnalytics.h b/services/mediametrics/AudioAnalytics.h
index ba4c3f2..9089d6f 100644
--- a/services/mediametrics/AudioAnalytics.h
+++ b/services/mediametrics/AudioAnalytics.h
@@ -16,14 +16,20 @@
#pragma once
+#include <android-base/thread_annotations.h>
#include "AnalyticsActions.h"
#include "AnalyticsState.h"
+#include "AudioPowerUsage.h"
+#include "TimedAction.h"
#include "Wrap.h"
namespace android::mediametrics {
class AudioAnalytics
{
+ // AudioAnalytics action / state helper classes
+ friend AudioPowerUsage;
+
public:
AudioAnalytics();
~AudioAnalytics();
@@ -70,10 +76,24 @@
// underlying state is locked.
mPreviousAnalyticsState->clear();
mAnalyticsState->clear();
+
+ // Clear power usage state.
+ mAudioPowerUsage.clear();
}
private:
+ /*
+ * AudioAnalytics class does not contain a monitor mutex.
+ * Instead, all of its variables are individually locked for access.
+ * Since data and items are generally added only (gc removes it), this is a reasonable
+ * compromise for availability/concurrency versus consistency.
+ *
+ * It is possible for concurrent threads to be reading and writing inside of AudioAnalytics.
+ * Reads based on a prior time (e.g. one second) in the past from the TimeMachine can be
+ * used to achieve better consistency if needed.
+ */
+
/**
* Checks for any pending actions for a particular item.
*
@@ -89,6 +109,8 @@
*/
std::string getThreadFromTrack(const std::string& track) const;
+ const bool mDeliverStatistics __unused = true;
+
// Actions is individually locked
AnalyticsActions mActions;
@@ -97,6 +119,73 @@
SharedPtrWrap<AnalyticsState> mAnalyticsState;
SharedPtrWrap<AnalyticsState> mPreviousAnalyticsState;
+
+ TimedAction mTimedAction; // locked internally
+
+ // DeviceUse is a nested class which handles audio device usage accounting.
+ // We define this class at the end to ensure prior variables all properly constructed.
+ // TODO: Track / Thread interaction
+ // TODO: Consider statistics aggregation.
+ class DeviceUse {
+ public:
+ enum ItemType {
+ RECORD = 0,
+ THREAD = 1,
+ TRACK = 2,
+ };
+
+ explicit DeviceUse(AudioAnalytics &audioAnalytics) : mAudioAnalytics{audioAnalytics} {}
+
+ // Called every time an endAudioIntervalGroup message is received.
+ void endAudioIntervalGroup(
+ const std::shared_ptr<const android::mediametrics::Item> &item,
+ ItemType itemType) const;
+
+ private:
+ AudioAnalytics &mAudioAnalytics;
+ } mDeviceUse{*this};
+
+ // DeviceConnected is a nested class which handles audio device connection
+ // We define this class at the end to ensure prior variables all properly constructed.
+ // TODO: Track / Thread interaction
+ // TODO: Consider statistics aggregation.
+ class DeviceConnection {
+ public:
+ explicit DeviceConnection(AudioAnalytics &audioAnalytics)
+ : mAudioAnalytics{audioAnalytics} {}
+
+ // Called every time an endAudioIntervalGroup message is received.
+ void a2dpConnected(
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ // Called when we have an AudioFlinger createPatch
+ void createPatch(
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ // Called through AudioManager when the BT service wants to notify connection
+ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ const std::shared_ptr<const android::mediametrics::Item> &item);
+
+ // When the timer expires.
+ void expire();
+
+ private:
+ AudioAnalytics &mAudioAnalytics;
+
+ mutable std::mutex mLock;
+ int64_t mA2dpConnectionRequestNs GUARDED_BY(mLock) = 0; // Time for BT service request.
+ int64_t mA2dpConnectionServiceNs GUARDED_BY(mLock) = 0; // Time audio service agrees.
+
+ int32_t mA2dpConnectionRequests GUARDED_BY(mLock) = 0;
+ int32_t mA2dpConnectionServices GUARDED_BY(mLock) = 0;
+
+ // See the statsd atoms.proto
+ int32_t mA2dpConnectionSuccesses GUARDED_BY(mLock) = 0;
+ int32_t mA2dpConnectionJavaServiceCancels GUARDED_BY(mLock) = 0;
+ int32_t mA2dpConnectionUnknowns GUARDED_BY(mLock) = 0;
+ } mDeviceConnection{*this};
+
+ AudioPowerUsage mAudioPowerUsage{this};
};
} // namespace android::mediametrics
diff --git a/services/mediametrics/AudioPowerUsage.cpp b/services/mediametrics/AudioPowerUsage.cpp
new file mode 100644
index 0000000..e311bc8
--- /dev/null
+++ b/services/mediametrics/AudioPowerUsage.cpp
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2020 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 "AudioPowerUsage"
+#include <utils/Log.h>
+
+#include "AudioAnalytics.h"
+#include "MediaMetricsService.h"
+#include <map>
+#include <sstream>
+#include <string>
+#include <audio_utils/clock.h>
+#include <cutils/properties.h>
+#include <statslog.h>
+#include <sys/timerfd.h>
+#include <system/audio-base.h>
+
+// property to disable audio power use metrics feature, default is enabled
+#define PROP_AUDIO_METRICS_DISABLED "persist.media.audio_metrics.power_usage_disabled"
+#define AUDIO_METRICS_DISABLED_DEFAULT (false)
+
+// property to set how long to send audio power use metrics data to westworld, default is 24hrs
+#define PROP_AUDIO_METRICS_INTERVAL_HR "persist.media.audio_metrics.interval_hr"
+#define INTERVAL_HR_DEFAULT (24)
+
+// for Audio Power Usage Metrics
+#define AUDIO_POWER_USAGE_KEY_AUDIO_USAGE "audio.power.usage"
+
+#define AUDIO_POWER_USAGE_PROP_DEVICE "device" // int32
+#define AUDIO_POWER_USAGE_PROP_DURATION_NS "durationNs" // int64
+#define AUDIO_POWER_USAGE_PROP_TYPE "type" // int32
+#define AUDIO_POWER_USAGE_PROP_VOLUME "volume" // double
+
+namespace android::mediametrics {
+
+/* static */
+bool AudioPowerUsage::typeFromString(const std::string& type_string, int32_t& type) {
+ static std::map<std::string, int32_t> typeTable = {
+ { "AUDIO_STREAM_VOICE_CALL", VOIP_CALL_TYPE },
+ { "AUDIO_STREAM_SYSTEM", MEDIA_TYPE },
+ { "AUDIO_STREAM_RING", RINGTONE_NOTIFICATION_TYPE },
+ { "AUDIO_STREAM_MUSIC", MEDIA_TYPE },
+ { "AUDIO_STREAM_ALARM", ALARM_TYPE },
+ { "AUDIO_STREAM_NOTIFICATION", RINGTONE_NOTIFICATION_TYPE },
+
+ { "AUDIO_CONTENT_TYPE_SPEECH", VOIP_CALL_TYPE },
+ { "AUDIO_CONTENT_TYPE_MUSIC", MEDIA_TYPE },
+ { "AUDIO_CONTENT_TYPE_MOVIE", MEDIA_TYPE },
+ { "AUDIO_CONTENT_TYPE_SONIFICATION", RINGTONE_NOTIFICATION_TYPE },
+
+ { "AUDIO_USAGE_MEDIA", MEDIA_TYPE },
+ { "AUDIO_USAGE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
+ { "AUDIO_USAGE_ALARM", ALARM_TYPE },
+ { "AUDIO_USAGE_NOTIFICATION", RINGTONE_NOTIFICATION_TYPE },
+
+ { "AUDIO_SOURCE_CAMCORDER", CAMCORDER_TYPE },
+ { "AUDIO_SOURCE_VOICE_COMMUNICATION", VOIP_CALL_TYPE },
+ { "AUDIO_SOURCE_DEFAULT", RECORD_TYPE },
+ { "AUDIO_SOURCE_MIC", RECORD_TYPE },
+ { "AUDIO_SOURCE_UNPROCESSED", RECORD_TYPE },
+ { "AUDIO_SOURCE_VOICE_RECOGNITION", RECORD_TYPE },
+ };
+
+ auto it = typeTable.find(type_string);
+ if (it == typeTable.end()) {
+ type = UNKNOWN_TYPE;
+ return false;
+ }
+
+ type = it->second;
+ return true;
+}
+
+/* static */
+bool AudioPowerUsage::deviceFromString(const std::string& device_string, int32_t& device) {
+ static std::map<std::string, int32_t> deviceTable = {
+ { "AUDIO_DEVICE_OUT_EARPIECE", OUTPUT_EARPIECE },
+ { "AUDIO_DEVICE_OUT_SPEAKER_SAFE", OUTPUT_SPEAKER_SAFE },
+ { "AUDIO_DEVICE_OUT_SPEAKER", OUTPUT_SPEAKER },
+ { "AUDIO_DEVICE_OUT_WIRED_HEADSET", OUTPUT_WIRED_HEADSET },
+ { "AUDIO_DEVICE_OUT_WIRED_HEADPHONE", OUTPUT_WIRED_HEADSET },
+ { "AUDIO_DEVICE_OUT_BLUETOOTH_SCO", OUTPUT_BLUETOOTH_SCO },
+ { "AUDIO_DEVICE_OUT_BLUETOOTH_A2DP", OUTPUT_BLUETOOTH_A2DP },
+ { "AUDIO_DEVICE_OUT_USB_HEADSET", OUTPUT_USB_HEADSET },
+ { "AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET", OUTPUT_BLUETOOTH_SCO },
+
+ { "AUDIO_DEVICE_IN_BUILTIN_MIC", INPUT_BUILTIN_MIC },
+ { "AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET", INPUT_BLUETOOTH_SCO },
+ { "AUDIO_DEVICE_IN_WIRED_HEADSET", INPUT_WIRED_HEADSET_MIC },
+ { "AUDIO_DEVICE_IN_USB_DEVICE", INPUT_USB_HEADSET_MIC },
+ { "AUDIO_DEVICE_IN_BACK_MIC", INPUT_BUILTIN_BACK_MIC },
+ };
+
+ auto it = deviceTable.find(device_string);
+ if (it == deviceTable.end()) {
+ device = 0;
+ return false;
+ }
+
+ device = it->second;
+ return true;
+}
+
+int32_t AudioPowerUsage::deviceFromStringPairs(const std::string& device_strings) {
+ int32_t deviceMask = 0;
+ const auto devaddrvec = MediaMetricsService::getDeviceAddressPairs(device_strings);
+ for (const auto &[device, addr] : devaddrvec) {
+ int32_t combo_device = 0;
+ deviceFromString(device, combo_device);
+ deviceMask |= combo_device;
+ }
+ return deviceMask;
+}
+
+/* static */
+void AudioPowerUsage::sendItem(const std::shared_ptr<const mediametrics::Item>& item)
+{
+ int32_t type;
+ if (!item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &type)) return;
+
+ int32_t device;
+ if (!item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &device)) return;
+
+ int64_t duration_ns;
+ if (!item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &duration_ns)) return;
+
+ double volume;
+ if (!item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &volume)) return;
+
+#ifdef STATSD
+ (void)android::util::stats_write(android::util::AUDIO_POWER_USAGE_DATA_REPORTED,
+ device,
+ (int32_t)(duration_ns / NANOS_PER_SECOND),
+ (float)volume,
+ type);
+#endif
+}
+
+bool AudioPowerUsage::saveAsItem_l(
+ int32_t device, int64_t duration_ns, int32_t type, double average_vol)
+{
+ ALOGV("%s: (%#x, %d, %lld, %f)", __func__, device, type,
+ (long long)duration_ns, average_vol );
+ if (duration_ns == 0) {
+ return true; // skip duration 0 usage
+ }
+ if (device == 0) {
+ return true; //ignore unknown device
+ }
+
+ for (auto item : mItems) {
+ int32_t item_type = 0, item_device = 0;
+ double item_volume = 0.;
+ int64_t item_duration_ns = 0;
+ item->getInt32(AUDIO_POWER_USAGE_PROP_DEVICE, &item_device);
+ item->getInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, &item_duration_ns);
+ item->getInt32(AUDIO_POWER_USAGE_PROP_TYPE, &item_type);
+ item->getDouble(AUDIO_POWER_USAGE_PROP_VOLUME, &item_volume);
+
+ // aggregate by device and type
+ if (item_device == device && item_type == type) {
+ int64_t final_duration_ns = item_duration_ns + duration_ns;
+ double final_volume = (device & INPUT_DEVICE_BIT) ? 1.0:
+ ((item_volume * item_duration_ns +
+ average_vol * duration_ns) / final_duration_ns);
+
+ item->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, final_duration_ns);
+ item->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, final_volume);
+ item->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
+
+ ALOGV("%s: update (%#x, %d, %lld, %f) --> (%lld, %f)", __func__,
+ device, type,
+ (long long)item_duration_ns, item_volume,
+ (long long)final_duration_ns, final_volume);
+
+ return true;
+ }
+ }
+
+ auto sitem = std::make_shared<mediametrics::Item>(AUDIO_POWER_USAGE_KEY_AUDIO_USAGE);
+ sitem->setTimestamp(systemTime(SYSTEM_TIME_REALTIME));
+ sitem->setInt32(AUDIO_POWER_USAGE_PROP_DEVICE, device);
+ sitem->setInt64(AUDIO_POWER_USAGE_PROP_DURATION_NS, duration_ns);
+ sitem->setInt32(AUDIO_POWER_USAGE_PROP_TYPE, type);
+ sitem->setDouble(AUDIO_POWER_USAGE_PROP_VOLUME, average_vol);
+ mItems.emplace_back(sitem);
+ return true;
+}
+
+void AudioPowerUsage::checkTrackRecord(
+ const std::shared_ptr<const mediametrics::Item>& item, bool isTrack)
+{
+ const std::string key = item->getKey();
+
+ int64_t deviceTimeNs = 0;
+ if (!item->getInt64(AMEDIAMETRICS_PROP_DEVICETIMENS, &deviceTimeNs)) {
+ return;
+ }
+ double deviceVolume = 1.;
+ if (isTrack && !item->getDouble(AMEDIAMETRICS_PROP_DEVICEVOLUME, &deviceVolume)) {
+ return;
+ }
+ int32_t type = 0;
+ std::string type_string;
+ if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_STREAMTYPE, &type_string) == OK) ||
+ (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_SOURCE, &type_string) == OK)) {
+ typeFromString(type_string, type);
+
+ if (isTrack && type == UNKNOWN_TYPE &&
+ mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_USAGE, &type_string) == OK) {
+ typeFromString(type_string, type);
+ }
+ if (isTrack && type == UNKNOWN_TYPE &&
+ mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_CONTENTTYPE, &type_string) == OK) {
+ typeFromString(type_string, type);
+ }
+ ALOGV("type = %s => %d", type_string.c_str(), type);
+ }
+
+ int32_t device = 0;
+ std::string device_strings;
+ if ((isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_OUTPUTDEVICES, &device_strings) == OK) ||
+ (!isTrack && mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_INPUTDEVICES, &device_strings) == OK)) {
+
+ device = deviceFromStringPairs(device_strings);
+ ALOGV("device = %s => %d", device_strings.c_str(), device);
+ }
+ std::lock_guard l(mLock);
+ saveAsItem_l(device, deviceTimeNs, type, deviceVolume);
+}
+
+void AudioPowerUsage::checkMode(const std::shared_ptr<const mediametrics::Item>& item)
+{
+ std::string mode;
+ if (!item->getString(AMEDIAMETRICS_PROP_AUDIOMODE, &mode)) return;
+
+ std::lock_guard l(mLock);
+ if (mode == mMode) return; // no change in mode.
+
+ if (mMode == "AUDIO_MODE_IN_CALL") { // leaving call mode
+ const int64_t endCallNs = item->getTimestamp();
+ const int64_t durationNs = endCallNs - mDeviceTimeNs;
+ if (durationNs > 0) {
+ mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
+ mVoiceVolume * (endCallNs - mVolumeTimeNs)) / durationNs;
+ saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
+ }
+ } else if (mode == "AUDIO_MODE_IN_CALL") { // entering call mode
+ mStartCallNs = item->getTimestamp(); // advisory only
+
+ mDeviceVolume = 0;
+ mVolumeTimeNs = mStartCallNs;
+ mDeviceTimeNs = mStartCallNs;
+ }
+ ALOGV("%s: new mode:%s old mode:%s", __func__, mode.c_str(), mMode.c_str());
+ mMode = mode;
+}
+
+void AudioPowerUsage::checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item)
+{
+ double voiceVolume = 0.;
+ if (!item->getDouble(AMEDIAMETRICS_PROP_VOICEVOLUME, &voiceVolume)) return;
+
+ std::lock_guard l(mLock);
+ if (voiceVolume == mVoiceVolume) return; // no change in volume
+
+ // we only track average device volume when we are in-call
+ if (mMode == "AUDIO_MODE_IN_CALL") {
+ const int64_t timeNs = item->getTimestamp();
+ const int64_t durationNs = timeNs - mDeviceTimeNs;
+ if (durationNs > 0) {
+ mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
+ mVoiceVolume * (timeNs - mVolumeTimeNs)) / durationNs;
+ mVolumeTimeNs = timeNs;
+ }
+ }
+ ALOGV("%s: new voice volume:%lf old voice volume:%lf", __func__, voiceVolume, mVoiceVolume);
+ mVoiceVolume = voiceVolume;
+}
+
+void AudioPowerUsage::checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item)
+{
+ std::string outputDevices;
+ if (!item->get(AMEDIAMETRICS_PROP_OUTPUTDEVICES, &outputDevices)) return;
+
+ const std::string& key = item->getKey();
+ std::string flags;
+ if (mAudioAnalytics->mAnalyticsState->timeMachine().get(
+ key, AMEDIAMETRICS_PROP_FLAGS, &flags) != OK) return;
+
+ if (flags.find("AUDIO_OUTPUT_FLAG_PRIMARY") == std::string::npos) return;
+
+ const int32_t device = deviceFromStringPairs(outputDevices);
+
+ std::lock_guard l(mLock);
+ if (mPrimaryDevice == device) return;
+
+ if (mMode == "AUDIO_MODE_IN_CALL") {
+ // Save statistics
+ const int64_t endDeviceNs = item->getTimestamp();
+ const int64_t durationNs = endDeviceNs - mDeviceTimeNs;
+ if (durationNs > 0) {
+ mDeviceVolume = (mDeviceVolume * (mVolumeTimeNs - mDeviceTimeNs) +
+ mVoiceVolume * (endDeviceNs - mVolumeTimeNs)) / durationNs;
+ saveAsItem_l(mPrimaryDevice, durationNs, VOICE_CALL_TYPE, mDeviceVolume);
+ }
+ // reset statistics
+ mDeviceVolume = 0;
+ mDeviceTimeNs = endDeviceNs;
+ mVolumeTimeNs = endDeviceNs;
+ }
+ ALOGV("%s: new primary device:%#x old primary device:%#x", __func__, device, mPrimaryDevice);
+ mPrimaryDevice = device;
+}
+
+AudioPowerUsage::AudioPowerUsage(AudioAnalytics *audioAnalytics)
+ : mAudioAnalytics(audioAnalytics)
+ , mDisabled(property_get_bool(PROP_AUDIO_METRICS_DISABLED, AUDIO_METRICS_DISABLED_DEFAULT))
+ , mIntervalHours(property_get_int32(PROP_AUDIO_METRICS_INTERVAL_HR, INTERVAL_HR_DEFAULT))
+{
+ ALOGD("%s", __func__);
+ ALOGI_IF(mDisabled, "AudioPowerUsage is disabled.");
+ collect(); // send items
+}
+
+AudioPowerUsage::~AudioPowerUsage()
+{
+ ALOGD("%s", __func__);
+}
+
+void AudioPowerUsage::clear()
+{
+ std::lock_guard _l(mLock);
+ mItems.clear();
+}
+
+void AudioPowerUsage::collect()
+{
+ std::lock_guard _l(mLock);
+ for (const auto &item : mItems) {
+ sendItem(item);
+ }
+ mItems.clear();
+ mAudioAnalytics->mTimedAction.postIn(
+ mIntervalHours <= 0 ? std::chrono::seconds(5) : std::chrono::hours(mIntervalHours),
+ [this](){ collect(); });
+}
+
+std::pair<std::string, int32_t> AudioPowerUsage::dump(int limit) const {
+ if (limit <= 2) {
+ return {{}, 0};
+ }
+ std::lock_guard _l(mLock);
+ if (mDisabled) {
+ return {"AudioPowerUsage disabled\n", 1};
+ }
+ if (mItems.empty()) {
+ return {"AudioPowerUsage empty\n", 1};
+ }
+
+ int slot = 1;
+ std::stringstream ss;
+ ss << "AudioPowerUsage:\n";
+ for (const auto &item : mItems) {
+ if (slot >= limit - 1) {
+ ss << "-- AudioPowerUsage may be truncated!\n";
+ ++slot;
+ break;
+ }
+ ss << " " << slot << " " << item->toString() << "\n";
+ slot++;
+ }
+ return { ss.str(), slot };
+}
+
+} // namespace android
diff --git a/services/mediametrics/AudioPowerUsage.h b/services/mediametrics/AudioPowerUsage.h
new file mode 100644
index 0000000..446ff4f
--- /dev/null
+++ b/services/mediametrics/AudioPowerUsage.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 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 <android-base/thread_annotations.h>
+#include <deque>
+#include <media/MediaMetricsItem.h>
+#include <mutex>
+#include <thread>
+
+namespace android::mediametrics {
+
+class AudioAnalytics;
+
+class AudioPowerUsage {
+public:
+ explicit AudioPowerUsage(AudioAnalytics *audioAnalytics);
+ ~AudioPowerUsage();
+
+ void checkTrackRecord(const std::shared_ptr<const mediametrics::Item>& item, bool isTrack);
+ void checkMode(const std::shared_ptr<const mediametrics::Item>& item);
+ void checkVoiceVolume(const std::shared_ptr<const mediametrics::Item>& item);
+ void checkCreatePatch(const std::shared_ptr<const mediametrics::Item>& item);
+ void clear();
+
+ /**
+ * Returns a pair consisting of the dump string, and the number of lines in the string.
+ *
+ * The number of lines in the returned pair is used as an optimization
+ * for subsequent line limiting.
+ *
+ * \param lines the maximum number of lines in the string returned.
+ */
+ std::pair<std::string, int32_t> dump(int32_t lines = INT32_MAX) const;
+
+ // align with message AudioUsageDataReported in frameworks/base/cmds/statsd/src/atoms.proto
+ enum AudioType {
+ UNKNOWN_TYPE = 0,
+ VOICE_CALL_TYPE = 1, // voice call
+ VOIP_CALL_TYPE = 2, // voip call, including uplink and downlink
+ MEDIA_TYPE = 3, // music and system sound
+ RINGTONE_NOTIFICATION_TYPE = 4, // ringtone and notification
+ ALARM_TYPE = 5, // alarm type
+ // record type
+ CAMCORDER_TYPE = 6, // camcorder
+ RECORD_TYPE = 7, // other recording
+ };
+
+ enum AudioDevice {
+ OUTPUT_EARPIECE = 0x1,
+ OUTPUT_SPEAKER = 0x2,
+ OUTPUT_WIRED_HEADSET = 0x4,
+ OUTPUT_USB_HEADSET = 0x8,
+ OUTPUT_BLUETOOTH_SCO = 0x10,
+ OUTPUT_BLUETOOTH_A2DP = 0x20,
+ OUTPUT_SPEAKER_SAFE = 0x40,
+
+ INPUT_DEVICE_BIT = 0x40000000,
+ INPUT_BUILTIN_MIC = INPUT_DEVICE_BIT | 0x1, // non-negative positive int32.
+ INPUT_BUILTIN_BACK_MIC = INPUT_DEVICE_BIT | 0x2,
+ INPUT_WIRED_HEADSET_MIC = INPUT_DEVICE_BIT | 0x4,
+ INPUT_USB_HEADSET_MIC = INPUT_DEVICE_BIT | 0x8,
+ INPUT_BLUETOOTH_SCO = INPUT_DEVICE_BIT | 0x10,
+ };
+
+ static bool typeFromString(const std::string& type_string, int32_t& type);
+ static bool deviceFromString(const std::string& device_string, int32_t& device);
+ static int32_t deviceFromStringPairs(const std::string& device_strings);
+private:
+ bool saveAsItem_l(int32_t device, int64_t duration, int32_t type, double average_vol)
+ REQUIRES(mLock);
+ static void sendItem(const std::shared_ptr<const mediametrics::Item>& item);
+ void collect();
+
+ AudioAnalytics * const mAudioAnalytics;
+ const bool mDisabled;
+ const int32_t mIntervalHours;
+
+ mutable std::mutex mLock;
+ std::deque<std::shared_ptr<mediametrics::Item>> mItems GUARDED_BY(mLock);
+
+ double mVoiceVolume GUARDED_BY(mLock) = 0.;
+ double mDeviceVolume GUARDED_BY(mLock) = 0.;
+ int64_t mStartCallNs GUARDED_BY(mLock) = 0; // advisory only
+ int64_t mVolumeTimeNs GUARDED_BY(mLock) = 0;
+ int64_t mDeviceTimeNs GUARDED_BY(mLock) = 0;
+ int32_t mPrimaryDevice GUARDED_BY(mLock) = OUTPUT_SPEAKER;
+ std::string mMode GUARDED_BY(mLock) {"AUDIO_MODE_NORMAL"};
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 4b84bea..3b3dc3e 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -78,6 +78,74 @@
}
}
+/* static */
+std::pair<std::string, int64_t>
+MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid_t uid) {
+ // Meyer's singleton, initialized on first access.
+ // mUidInfo is locked internally.
+ static mediautils::UidInfo uidInfo;
+
+ // get info.
+ mediautils::UidInfo::Info info = uidInfo.getInfo(uid);
+ if (useUidForPackage(info.package, info.installer)) {
+ return { std::to_string(uid), /* versionCode */ 0 };
+ } else {
+ return { info.package, info.versionCode };
+ }
+}
+
+/* static */
+std::string MediaMetricsService::tokenizer(std::string::const_iterator& it,
+ const std::string::const_iterator& end, const char *reserved) {
+ // consume leading white space
+ for (; it != end && std::isspace(*it); ++it);
+ if (it == end) return {};
+
+ auto start = it;
+ // parse until we hit a reserved keyword or space
+ if (strchr(reserved, *it)) return {start, ++it};
+ for (;;) {
+ ++it;
+ if (it == end || std::isspace(*it) || strchr(reserved, *it)) return {start, it};
+ }
+}
+
+/* static */
+std::vector<std::pair<std::string, std::string>>
+MediaMetricsService::getDeviceAddressPairs(const std::string& devices) {
+ std::vector<std::pair<std::string, std::string>> result;
+
+ // Currently, the device format is EXACTLY
+ // (device1, addr1)|(device2, addr2)|...
+
+ static constexpr char delim[] = "()|,";
+ for (auto it = devices.begin(); ; ) {
+ auto token = tokenizer(it, devices.end(), delim);
+ if (token != "(") return result;
+
+ auto device = tokenizer(it, devices.end(), delim);
+ if (device.empty() || !std::isalnum(device[0])) return result;
+
+ token = tokenizer(it, devices.end(), delim);
+ if (token != ",") return result;
+
+ // special handling here for empty addresses
+ auto address = tokenizer(it, devices.end(), delim);
+ if (address.empty() || !std::isalnum(device[0])) return result;
+ if (address == ")") { // no address, just the ")"
+ address.clear();
+ } else {
+ token = tokenizer(it, devices.end(), delim);
+ if (token != ")") return result;
+ }
+
+ result.emplace_back(std::move(device), std::move(address));
+
+ token = tokenizer(it, devices.end(), delim);
+ if (token != "|") return result; // this includes end of string detection
+ }
+}
+
MediaMetricsService::MediaMetricsService()
: mMaxRecords(kMaxRecords),
mMaxRecordAgeNs(kMaxRecordAgeNs),
@@ -136,16 +204,10 @@
// Overwrite package name and version if the caller was untrusted or empty
if (!isTrusted || item->getPkgName().empty()) {
const uid_t uid = item->getUid();
- mediautils::UidInfo::Info info = mUidInfo.getInfo(uid);
- if (useUidForPackage(info.package, info.installer)) {
- // remove uid information of unknown installed packages.
- // TODO: perhaps this can be done just before uploading to Westworld.
- item->setPkgName(std::to_string(uid));
- item->setPkgVersionCode(0);
- } else {
- item->setPkgName(info.package);
- item->setPkgVersionCode(info.versionCode);
- }
+ const auto [ pkgName, version ] =
+ MediaMetricsService::getSanitizedPackageNameAndVersionCode(uid);
+ item->setPkgName(pkgName);
+ item->setPkgVersionCode(version);
}
ALOGV("%s: isTrusted:%d given uid %d; sanitized uid: %d sanitized pkg: %s "
diff --git a/services/mediametrics/MediaMetricsService.h b/services/mediametrics/MediaMetricsService.h
index faba197..b8eb267 100644
--- a/services/mediametrics/MediaMetricsService.h
+++ b/services/mediametrics/MediaMetricsService.h
@@ -69,6 +69,28 @@
*/
static bool useUidForPackage(const std::string& package, const std::string& installer);
+ /**
+ * Returns a std::pair of packageName and versionCode for a given uid.
+ *
+ * The value is sanitized - i.e. if the result is not approved to send,
+ * we use the uid as a string and a version code of 0.
+ */
+ static std::pair<std::string, int64_t> getSanitizedPackageNameAndVersionCode(uid_t uid);
+
+ /**
+ * Return string tokens from iterator, separated by spaces and reserved chars.
+ */
+ static std::string tokenizer(std::string::const_iterator& it,
+ const std::string::const_iterator& end, const char *reserved);
+
+ /**
+ * Parse the devices string and return a vector of device address pairs.
+ *
+ * A failure to parse returns early with the contents that were able to be parsed.
+ */
+ static std::vector<std::pair<std::string, std::string>>
+ getDeviceAddressPairs(const std::string &devices);
+
protected:
// Internal call where release is true if ownership of item is transferred
@@ -100,8 +122,6 @@
std::atomic<int64_t> mItemsSubmitted{}; // accessed outside of lock.
- mediautils::UidInfo mUidInfo; // mUidInfo can be accessed without lock (locked internally)
-
mediametrics::AudioAnalytics mAudioAnalytics; // mAudioAnalytics is locked internally.
std::mutex mLock;
diff --git a/services/mediametrics/TimedAction.h b/services/mediametrics/TimedAction.h
new file mode 100644
index 0000000..c7ef585
--- /dev/null
+++ b/services/mediametrics/TimedAction.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 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 <android-base/thread_annotations.h>
+#include <chrono>
+#include <map>
+#include <mutex>
+#include <thread>
+
+namespace android::mediametrics {
+
+class TimedAction {
+public:
+ TimedAction() : mThread{[this](){threadLoop();}} {}
+
+ ~TimedAction() {
+ quit();
+ }
+
+ // TODO: return a handle for cancelling the action?
+ template <typename T> // T is in units of std::chrono::duration.
+ void postIn(const T& time, std::function<void()> f) {
+ postAt(std::chrono::steady_clock::now() + time, f);
+ }
+
+ template <typename T> // T is in units of std::chrono::time_point
+ void postAt(const T& targetTime, std::function<void()> f) {
+ std::lock_guard l(mLock);
+ if (mQuit) return;
+ if (mMap.empty() || targetTime < mMap.begin()->first) {
+ mMap.emplace_hint(mMap.begin(), targetTime, std::move(f));
+ mCondition.notify_one();
+ } else {
+ mMap.emplace(targetTime, std::move(f));
+ }
+ }
+
+ void clear() {
+ std::lock_guard l(mLock);
+ mMap.clear();
+ }
+
+ void quit() {
+ {
+ std::lock_guard l(mLock);
+ if (mQuit) return;
+ mQuit = true;
+ mMap.clear();
+ mCondition.notify_all();
+ }
+ mThread.join();
+ }
+
+ size_t size() const {
+ std::lock_guard l(mLock);
+ return mMap.size();
+ }
+
+private:
+ void threadLoop() NO_THREAD_SAFETY_ANALYSIS { // thread safety doesn't cover unique_lock
+ std::unique_lock l(mLock);
+ while (!mQuit) {
+ auto sleepUntilTime = std::chrono::time_point<std::chrono::steady_clock>::max();
+ if (!mMap.empty()) {
+ sleepUntilTime = mMap.begin()->first;
+ if (sleepUntilTime <= std::chrono::steady_clock::now()) {
+ auto node = mMap.extract(mMap.begin()); // removes from mMap.
+ l.unlock();
+ node.mapped()();
+ l.lock();
+ continue;
+ }
+ }
+ mCondition.wait_until(l, sleepUntilTime);
+ }
+ }
+
+ mutable std::mutex mLock;
+ std::condition_variable mCondition GUARDED_BY(mLock);
+ bool mQuit GUARDED_BY(mLock) = false;
+ std::multimap<std::chrono::time_point<std::chrono::steady_clock>, std::function<void()>>
+ mMap GUARDED_BY(mLock); // multiple functions could execute at the same time.
+
+ // needs to be initialized after the variables above, done in constructor initializer list.
+ std::thread mThread;
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/mediametrics.rc b/services/mediametrics/mediametrics.rc
index 1efde5e..2a6c817 100644
--- a/services/mediametrics/mediametrics.rc
+++ b/services/mediametrics/mediametrics.rc
@@ -3,4 +3,4 @@
user media
group media
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index cf0dceb..f7988f1 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -803,7 +803,7 @@
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
- ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
+ ASSERT_EQ(10, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
@@ -839,7 +839,7 @@
// TODO: Verify contents of AudioAnalytics.
// Currently there is no getter API in AudioAnalytics besides dump.
- ASSERT_EQ(9, audioAnalytics.dump(1000).second /* lines */);
+ ASSERT_EQ(10, audioAnalytics.dump(1000).second /* lines */);
ASSERT_EQ(NO_ERROR, audioAnalytics.submit(item, true /* isTrusted */));
// untrusted entities can add to an existing key
@@ -883,6 +883,48 @@
}
}
+TEST(mediametrics_tests, device_parsing) {
+ auto devaddr = android::MediaMetricsService::getDeviceAddressPairs("(DEVICE, )");
+ ASSERT_EQ((size_t)1, devaddr.size());
+ ASSERT_EQ("DEVICE", devaddr[0].first);
+ ASSERT_EQ("", devaddr[0].second);
+
+ devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+ "(DEVICE1, A)|(D, ADDRB)");
+ ASSERT_EQ((size_t)2, devaddr.size());
+ ASSERT_EQ("DEVICE1", devaddr[0].first);
+ ASSERT_EQ("A", devaddr[0].second);
+ ASSERT_EQ("D", devaddr[1].first);
+ ASSERT_EQ("ADDRB", devaddr[1].second);
+
+ devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+ "(A,B)|(C,D)");
+ ASSERT_EQ((size_t)2, devaddr.size());
+ ASSERT_EQ("A", devaddr[0].first);
+ ASSERT_EQ("B", devaddr[0].second);
+ ASSERT_EQ("C", devaddr[1].first);
+ ASSERT_EQ("D", devaddr[1].second);
+
+ devaddr = android::MediaMetricsService::getDeviceAddressPairs(
+ " ( A1 , B ) | ( C , D2 ) ");
+ ASSERT_EQ((size_t)2, devaddr.size());
+ ASSERT_EQ("A1", devaddr[0].first);
+ ASSERT_EQ("B", devaddr[0].second);
+ ASSERT_EQ("C", devaddr[1].first);
+ ASSERT_EQ("D2", devaddr[1].second);
+}
+
+TEST(mediametrics_tests, timed_action) {
+ android::mediametrics::TimedAction timedAction;
+ std::atomic_int value1 = 0;
+
+ timedAction.postIn(std::chrono::seconds(0), [&value1] { ++value1; });
+ timedAction.postIn(std::chrono::seconds(1000), [&value1] { ++value1; });
+ usleep(100000);
+ ASSERT_EQ(1, value1);
+ ASSERT_EQ((size_t)1, timedAction.size());
+}
+
#if 0
// Stress test code for garbage collection, you need to enable AID_SHELL as trusted to run
// in MediaMetricsService.cpp.
diff --git a/services/mediatranscoding/mediatranscoding.rc b/services/mediatranscoding/mediatranscoding.rc
index 2dc547f..5bfef59 100644
--- a/services/mediatranscoding/mediatranscoding.rc
+++ b/services/mediatranscoding/mediatranscoding.rc
@@ -3,4 +3,4 @@
user media
group media
ioprio rt 4
- writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
+ task_profiles ProcessCapacityHigh HighPerformance
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 82cc90e..c9bf72f 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -76,6 +76,7 @@
result << " ExclusiveFoundCount: " << mExclusiveFoundCount << "\n";
result << " ExclusiveOpenCount: " << mExclusiveOpenCount << "\n";
result << " ExclusiveCloseCount: " << mExclusiveCloseCount << "\n";
+ result << " ExclusiveStolenCount: " << mExclusiveStolenCount << "\n";
result << "\n";
if (isExclusiveLocked) {
@@ -142,7 +143,13 @@
sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
const aaudio::AAudioStreamRequest &request) {
if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
- return openExclusiveEndpoint(audioService, request);
+ sp<AAudioServiceEndpoint> endpointToSteal;
+ sp<AAudioServiceEndpoint> foundEndpoint =
+ openExclusiveEndpoint(audioService, request, endpointToSteal);
+ if (endpointToSteal.get()) {
+ endpointToSteal->releaseRegisteredStreams(); // free the MMAP resource
+ }
+ return foundEndpoint;
} else {
return openSharedEndpoint(audioService, request);
}
@@ -150,7 +157,8 @@
sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
AAudioService &aaudioService,
- const aaudio::AAudioStreamRequest &request) {
+ const aaudio::AAudioStreamRequest &request,
+ sp<AAudioServiceEndpoint> &endpointToSteal) {
std::lock_guard<std::mutex> lock(mExclusiveLock);
@@ -161,18 +169,22 @@
// If we find an existing one then this one cannot be exclusive.
if (endpoint.get() != nullptr) {
- ALOGW("openExclusiveEndpoint() already in use");
- // Already open so do not allow a second stream.
+ if (kStealingEnabled
+ && !endpoint->isForSharing() // not currently SHARED
+ && !request.isSharingModeMatchRequired()) { // app did not request a shared stream
+ ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
+ mExclusiveStolenCount++;
+ endpointToSteal = endpoint;
+ }
return nullptr;
} else {
sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP(aaudioService);
- ALOGV("openExclusiveEndpoint(), no match so try to open MMAP %p for dev %d",
- endpointMMap.get(), configuration.getDeviceId());
+ ALOGV("%s(), no match so try to open MMAP %p for dev %d",
+ __func__, endpointMMap.get(), configuration.getDeviceId());
endpoint = endpointMMap;
aaudio_result_t result = endpoint->open(request);
if (result != AAUDIO_OK) {
- ALOGV("openExclusiveEndpoint(), open failed");
endpoint.clear();
} else {
mExclusiveStreams.push_back(endpointMMap);
@@ -183,7 +195,9 @@
if (endpoint.get() != nullptr) {
// Increment the reference count under this lock.
endpoint->setOpenCount(endpoint->getOpenCount() + 1);
+ endpoint->setForSharing(request.isSharingModeMatchRequired());
}
+
return endpoint;
}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index ba17853..ae776b1 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -19,6 +19,7 @@
#include <map>
#include <mutex>
+#include <sys/types.h>
#include <utils/Singleton.h>
#include "binding/AAudioServiceMessage.h"
@@ -62,7 +63,8 @@
private:
android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
- const aaudio::AAudioStreamRequest &request);
+ const aaudio::AAudioStreamRequest &request,
+ sp<AAudioServiceEndpoint> &endpointToSteal);
android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
const aaudio::AAudioStreamRequest &request);
@@ -91,11 +93,16 @@
int32_t mExclusiveFoundCount = 0; // number of times we FOUND an exclusive endpoint
int32_t mExclusiveOpenCount = 0; // number of times we OPENED an exclusive endpoint
int32_t mExclusiveCloseCount = 0; // number of times we CLOSED an exclusive endpoint
+ int32_t mExclusiveStolenCount = 0; // number of times we STOLE an exclusive endpoint
+
// Same as above but for SHARED endpoints.
int32_t mSharedSearchCount = 0;
int32_t mSharedFoundCount = 0;
int32_t mSharedOpenCount = 0;
int32_t mSharedCloseCount = 0;
+
+ // For easily disabling the stealing of exclusive streams.
+ static constexpr bool kStealingEnabled = true;
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index cac7453..ecbcb7e 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -85,6 +85,17 @@
aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
aaudio::AAudioStreamConfiguration &configurationOutput) {
+ // A lock in is used to order the opening of endpoints when an
+ // EXCLUSIVE endpoint is stolen. We want the order to be:
+ // 1) Thread A opens exclusive MMAP endpoint
+ // 2) Thread B wants to open an exclusive MMAP endpoint so it steals the one from A
+ // under this lock.
+ // 3) Thread B opens a shared MMAP endpoint.
+ // 4) Thread A can then get the lock and also open a shared stream.
+ // Without the lock. Thread A might sneak in and reallocate an exclusive stream
+ // before B can open the shared stream.
+ std::unique_lock<std::recursive_mutex> lock(mOpenLock);
+
aaudio_result_t result = AAUDIO_OK;
sp<AAudioServiceStreamBase> serviceStream;
const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
@@ -139,7 +150,6 @@
return result;
} else {
aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
- ALOGV("openStream(): handle = 0x%08X", handle);
serviceStream->setHandle(handle);
pid_t pid = request.getProcessId();
AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
@@ -147,6 +157,7 @@
// Log open in MediaMetrics after we have the handle because we need the handle to
// create the metrics ID.
serviceStream->logOpen(handle);
+ ALOGV("%s(): return handle = 0x%08X", __func__, handle);
return handle;
}
}
@@ -180,7 +191,10 @@
ALOGE("closeStream(0x%0x), illegal stream handle", streamHandle);
return AAUDIO_ERROR_INVALID_HANDLE;
}
+ return closeStream(serviceStream);
+}
+aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
pid_t pid = serviceStream->getOwnerProcessId();
AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 7167868..6a2ac1f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -55,6 +55,10 @@
aaudio::AAudioStreamConfiguration &configurationOutput)
override;
+ /*
+ * This is called from Binder. It checks for permissions
+ * and converts the handle passed through Binder to a stream pointer.
+ */
aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) override;
aaudio_result_t getStreamDescription(
@@ -84,8 +88,18 @@
aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
audio_port_handle_t clientHandle) override;
+ // ===============================================================================
+ // The following public methods are only called from the service and NOT by Binder.
+ // ===============================================================================
+
aaudio_result_t disconnectStreamByPortHandle(audio_port_handle_t portHandle);
+ /*
+ * This is only called from within the Service.
+ * It bypasses the permission checks in closeStream(handle).
+ */
+ aaudio_result_t closeStream(sp<aaudio::AAudioServiceStreamBase> serviceStream);
+
private:
/** @return true if the client is the audioserver
@@ -100,8 +114,6 @@
sp<aaudio::AAudioServiceStreamBase> convertHandleToServiceStream(
aaudio::aaudio_handle_t streamHandle);
-
-
bool releaseStream(const sp<aaudio::AAudioServiceStreamBase> &serviceStream);
aaudio_result_t checkForPendingClose(const sp<aaudio::AAudioServiceStreamBase> &serviceStream,
@@ -111,6 +123,10 @@
aaudio::AAudioStreamTracker mStreamTracker;
+ // We use a lock to prevent thread A from reopening an exclusive stream
+ // after thread B steals thread A's exclusive MMAP resource stream.
+ std::recursive_mutex mOpenLock;
+
// TODO Extract the priority constants from services/audioflinger/Threads.cpp
// and share them with this code. Look for "kPriorityFastMixer".
static constexpr int32_t kRealTimeAudioPriorityClient = 2;
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 647dcf7..b09cbf4 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -27,13 +27,13 @@
#include <utils/Singleton.h>
-#include "AAudioEndpointManager.h"
-#include "AAudioServiceEndpoint.h"
#include "core/AudioStreamBuilder.h"
+
+#include "AAudioEndpointManager.h"
+#include "AAudioClientTracker.h"
#include "AAudioServiceEndpoint.h"
#include "AAudioServiceStreamShared.h"
-#include "AAudioServiceEndpointShared.h"
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
@@ -87,16 +87,31 @@
return false;
}
-void AAudioServiceEndpoint::disconnectRegisteredStreams() {
+std::vector<android::sp<AAudioServiceStreamBase>>
+ AAudioServiceEndpoint::disconnectRegisteredStreams() {
+ std::vector<android::sp<AAudioServiceStreamBase>> streamsDisconnected;
std::lock_guard<std::mutex> lock(mLockStreams);
mConnected.store(false);
- for (const auto& stream : mRegisteredStreams) {
- ALOGD("disconnectRegisteredStreams() stop and disconnect port %d",
- stream->getPortHandle());
+ for (const auto &stream : mRegisteredStreams) {
+ ALOGD("%s() - stop and disconnect port %d", __func__, stream->getPortHandle());
stream->stop();
stream->disconnect();
}
- mRegisteredStreams.clear();
+ mRegisteredStreams.swap(streamsDisconnected);
+ return streamsDisconnected;
+}
+
+void AAudioServiceEndpoint::releaseRegisteredStreams() {
+ // List of streams to be closed after we disconnect everything.
+ std::vector<android::sp<AAudioServiceStreamBase>> streamsToClose
+ = disconnectRegisteredStreams();
+
+ // Close outside the lock to avoid recursive locks.
+ AAudioService *aaudioService = AAudioClientTracker::getInstance().getAAudioService();
+ for (const auto& serviceStream : streamsToClose) {
+ ALOGD("%s() - close stream 0x%08X", __func__, serviceStream->getHandle());
+ aaudioService->closeStream(serviceStream);
+ }
}
aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamBase>stream) {
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index ad30087..a171cb0 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -111,6 +111,21 @@
static audio_attributes_t getAudioAttributesFrom(const AAudioStreamParameters *params);
+ // Stop, disconnect and release any streams registered on this endpoint.
+ void releaseRegisteredStreams();
+
+ bool isForSharing() const {
+ return mForSharing;
+ }
+
+ /**
+ *
+ * @param flag true if this endpoint is to be shared between multiple streams
+ */
+ void setForSharing(bool flag) {
+ mForSharing = flag;
+ }
+
protected:
/**
@@ -119,7 +134,7 @@
*/
bool isStreamRegistered(audio_port_handle_t portHandle);
- void disconnectRegisteredStreams();
+ std::vector<android::sp<AAudioServiceStreamBase>> disconnectRegisteredStreams();
mutable std::mutex mLockStreams;
std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
@@ -132,7 +147,11 @@
int32_t mOpenCount = 0;
int32_t mRequestedDeviceId = 0;
+ // True if this will be shared by one or more other streams.
+ bool mForSharing = false;
+
std::atomic<bool> mConnected{true};
+
};
} /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index af2710d..0843e0b 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -23,10 +23,10 @@
#include <map>
#include <mutex>
#include <sstream>
+#include <thread>
#include <utils/Singleton.h>
#include <vector>
-
#include "AAudioEndpointManager.h"
#include "AAudioServiceEndpoint.h"
@@ -36,7 +36,6 @@
#include "AAudioServiceEndpointPlay.h"
#include "AAudioServiceEndpointMMAP.h"
-
#define AAUDIO_BUFFER_CAPACITY_MIN 4 * 512
#define AAUDIO_SAMPLE_RATE_DEFAULT 48000
@@ -48,7 +47,6 @@
using namespace android; // TODO just import names needed
using namespace aaudio; // TODO just import names needed
-
AAudioServiceEndpointMMAP::AAudioServiceEndpointMMAP(AAudioService &audioService)
: mMmapStream(nullptr)
, mAAudioService(audioService) {}
@@ -229,7 +227,7 @@
}
aaudio_result_t AAudioServiceEndpointMMAP::close() {
- if (mMmapStream != 0) {
+ if (mMmapStream != nullptr) {
// Needs to be explicitly cleared or CTS will fail but it is not clear why.
mMmapStream.clear();
// Apparently the above close is asynchronous. An attempt to open a new device
@@ -318,9 +316,8 @@
return 0; // TODO
}
-// This is called by AudioFlinger when it wants to destroy a stream.
-void AAudioServiceEndpointMMAP::onTearDown(audio_port_handle_t portHandle) {
- ALOGD("%s(portHandle = %d) called", __func__, portHandle);
+// This is called by onTearDown() in a separate thread to avoid deadlocks.
+void AAudioServiceEndpointMMAP::handleTearDownAsync(audio_port_handle_t portHandle) {
// Are we tearing down the EXCLUSIVE MMAP stream?
if (isStreamRegistered(portHandle)) {
ALOGD("%s(%d) tearing down this entire MMAP endpoint", __func__, portHandle);
@@ -333,6 +330,13 @@
}
};
+// This is called by AudioFlinger when it wants to destroy a stream.
+void AAudioServiceEndpointMMAP::onTearDown(audio_port_handle_t portHandle) {
+ ALOGD("%s(portHandle = %d) called", __func__, portHandle);
+ std::thread asyncTask(&AAudioServiceEndpointMMAP::handleTearDownAsync, this, portHandle);
+ asyncTask.detach();
+}
+
void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
android::Vector<float> values) {
// TODO Do we really need a different volume for each channel?
@@ -345,12 +349,20 @@
}
};
-void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
+void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t portHandle) {
+ const int32_t deviceId = static_cast<int32_t>(portHandle);
ALOGD("%s() called with dev %d, old = %d", __func__, deviceId, getDeviceId());
- if (getDeviceId() != AUDIO_PORT_HANDLE_NONE && getDeviceId() != deviceId) {
- disconnectRegisteredStreams();
+ if (getDeviceId() != deviceId) {
+ if (getDeviceId() != AUDIO_PORT_HANDLE_NONE) {
+ std::thread asyncTask([this, deviceId]() {
+ disconnectRegisteredStreams();
+ setDeviceId(deviceId);
+ });
+ asyncTask.detach();
+ } else {
+ setDeviceId(deviceId);
+ }
}
- setDeviceId(deviceId);
};
/**
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index f599066..3d10861 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -68,13 +68,15 @@
aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+ void handleTearDownAsync(audio_port_handle_t portHandle);
+
// -------------- Callback functions for MmapStreamCallback ---------------------
- void onTearDown(audio_port_handle_t handle) override;
+ void onTearDown(audio_port_handle_t portHandle) override;
void onVolumeChanged(audio_channel_mask_t channels,
android::Vector<float> values) override;
- void onRoutingChanged(audio_port_handle_t deviceId) override;
+ void onRoutingChanged(audio_port_handle_t portHandle) override;
// ------------------------------------------------------------------------------
aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable);
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index dba9fb9..531bfa1 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -225,7 +225,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.record(); });
@@ -268,7 +268,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_PAUSE)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.record(); });
@@ -310,7 +310,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.record(); });
@@ -364,7 +364,7 @@
mediametrics::Defer defer([&] {
mediametrics::LogItem(mMetricsId)
.set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_FLUSH)
- .set(AMEDIAMETRICS_PROP_DURATIONNS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
+ .set(AMEDIAMETRICS_PROP_EXECUTIONTIMENS, (int64_t)(AudioClock::getNanoseconds() - beginNs))
.set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
.set(AMEDIAMETRICS_PROP_STATUS, (int32_t)result)
.record(); });